> ## Documentation Index
> Fetch the complete documentation index at: https://docs.memvid.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API Integration Patterns

> Production patterns for integrating Memvid through api.memvid.com

Use this guide when you are integrating Memvid through HTTP in platforms like n8n, Replit, Lovable, or v0.

<Info>
  This page is the shared contract for API-first integrations. Platform pages should reuse these request shapes and reliability patterns.
</Info>

## Base Setup

* Base URL: `https://api.memvid.com`
* Auth header (recommended): `Authorization: Bearer mv2_YOUR_API_KEY`
* Alternative auth header: `X-API-Key: mv2_YOUR_API_KEY`

## Golden Path (Minimal Production Flow)

1. Create or select a memory ID
2. Ingest documents (JSON text, file, or URL)
3. Use `find` for retrieval UX
4. Use `ask` for grounded synthesis
5. Return answer with sources in your app UI

## Canonical Request Shapes

### Create Memory

```http theme={null}
POST /v1/memories
Content-Type: application/json

{
  "name": "Support KB",
  "description": "Runbooks and policies"
}
```

### Add Documents (JSON)

```http theme={null}
POST /v1/memories/:id/documents
Content-Type: application/json

{
  "documents": [
    {
      "title": "P1 Playbook",
      "text": "Acknowledge P1 in 10 minutes.",
      "tags": ["incident", "support"]
    }
  ]
}
```

### Find

```http theme={null}
POST /v1/memories/:id/find
Content-Type: application/json

{
  "query": "How quickly do we acknowledge P1 incidents?",
  "topK": 5
}
```

### Ask

```http theme={null}
POST /v1/memories/:id/ask
Content-Type: application/json

{
  "question": "What is our P1 SLA?",
  "options": {
    "model": "gpt-4o-mini",
    "includeSources": true,
    "maxContextChunks": 10
  }
}
```

## Typed Client Wrapper (TypeScript)

```typescript theme={null}
type MemvidRequestInit = Omit<RequestInit, "headers"> & {
  headers?: Record<string, string>;
  timeoutMs?: number;
  retries?: number;
};

class MemvidHttpError extends Error {
  constructor(
    public status: number,
    public body: string,
    public method: string,
    public path: string
  ) {
    super(`Memvid ${method} ${path} failed (${status})`);
  }
}

const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

export async function memvidRequest<T = unknown>(
  path: string,
  init: MemvidRequestInit = {}
): Promise<T> {
  const baseUrl = process.env.MEMVID_API_BASE || "https://api.memvid.com";
  const apiKey = process.env.MEMVID_API_KEY;
  if (!apiKey) throw new Error("Missing MEMVID_API_KEY");

  const retries = init.retries ?? 2;
  const timeoutMs = init.timeoutMs ?? 15000;
  const method = init.method || "GET";

  for (let attempt = 0; attempt <= retries; attempt++) {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), timeoutMs);

    try {
      const res = await fetch(`${baseUrl}${path}`, {
        ...init,
        signal: controller.signal,
        headers: {
          Authorization: `Bearer ${apiKey}`,
          "Content-Type": "application/json",
          ...(init.headers || {}),
        },
      });

      if (res.status === 204) return null as T;
      const text = await res.text();
      if (!res.ok) {
        const retryable = res.status === 429 || res.status >= 500;
        if (retryable && attempt < retries) {
          await sleep(250 * Math.pow(2, attempt));
          continue;
        }
        throw new MemvidHttpError(res.status, text, method, path);
      }
      return text ? (JSON.parse(text) as T) : (null as T);
    } catch (err) {
      const retryable = err instanceof Error && err.name === "AbortError";
      if (!retryable || attempt >= retries) throw err;
      await sleep(250 * Math.pow(2, attempt));
    } finally {
      clearTimeout(timeout);
    }
  }

  throw new Error("Unexpected retry loop exit");
}
```

## Async Ingestion and Job Polling

Some uploads are processed asynchronously via a background worker. This happens automatically when:

* The file is larger than **2 MB**
* A PDF has more than **5 scanned/image-only pages** (triggers OCR in the background)
* You set `options.async: true` explicitly

When async processing kicks in, the response returns **HTTP 202** instead of 200, with a `jobId` and `pollUrl`:

```json theme={null}
{
  "added": 0,
  "chunksCreated": 0,
  "jobId": "abc123...",
  "pollUrl": "/v1/jobs/abc123..."
}
```

### Polling for completion

```typescript theme={null}
type JobStatus = "pending" | "processing" | "completed" | "failed" | "partial";

interface JobResult {
  status: JobStatus;
  progress: number;        // 0-100
  message?: string;        // e.g. "Generating embeddings..."
  result?: {
    documentsAdded: number;
    chunksCreated: number;
    documentIds: string[];
    totalBytes: number;
    processingMs: number;
  };
  error?: string;
}

export async function waitForJob(jobId: string, timeoutMs = 600000) {
  const started = Date.now();
  while (Date.now() - started < timeoutMs) {
    const job = await memvidRequest<JobResult>(`/v1/jobs/${jobId}`);
    if (job.status === "completed" || job.status === "partial") return job;
    if (job.status === "failed") throw new Error(job.error || "Memvid job failed");
    await sleep(5000); // Poll every 5 seconds
  }
  throw new Error(`Timed out waiting for job ${jobId}`);
}
```

<Tip>
  Scanned PDFs with many pages can take 2–8 minutes to process via OCR. Use a generous timeout (5–10 minutes) and poll every 5–10 seconds.
</Tip>

### Handling both sync and async responses

```typescript theme={null}
const res = await fetch(`${baseUrl}/v1/memories/${memoryId}/documents`, {
  method: "POST",
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
  body: JSON.stringify({ documents: [...] }),
});

if (res.status === 200) {
  // Sync — documents are ready immediately
  const data = await res.json();
  console.log(`Added ${data.added} documents (${data.chunksCreated} chunks)`);
} else if (res.status === 202) {
  // Async — poll for completion
  const data = await res.json();
  const result = await waitForJob(data.jobId);
  console.log(`Job done: ${result.result?.chunksCreated} chunks created`);
}
```

## Reliability Checklist

* Keep API keys server-side only.
* Use memory-scoped keys for least privilege.
* Retry `429` and `5xx` with backoff.
* Enforce request timeouts to prevent hanging workers.
* Log method, path, status, and request IDs for debugging.
* Show source snippets in UI for grounded trust.

## 5-Minute Smoke Test

Run these calls in order and verify non-empty responses:

1. `POST /v1/memories` -> get `memory.id`
2. `POST /v1/memories/:id/documents` -> ingest sample text
3. `POST /v1/memories/:id/find` -> expect at least one hit
4. `POST /v1/memories/:id/ask` -> expect `answer/text` and optional `sources`

## Next Pages

* [n8n](/frameworks/n8n)
* [Replit](/frameworks/replit)
* [Lovable](/frameworks/lovable)
* [v0](/frameworks/v0)
* [REST API Reference](/api-reference/rest-api)
