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

# Ingest Data

> Get your files into Mixpeek — namespaces, buckets, objects, uploads, and batching

> [card] [Ingest your own files](https://studio.mixpeek.com/namespaces/create?mode=managed\&utm_source=docs\&utm_medium=docs_ingest\&utm_campaign=managed_onramp)
>
> Create a namespace, connect a bucket, and run your first batch in minutes — no API key setup required.

## Set Up a Namespace

Every project starts with a namespace — the isolation boundary for all your resources. Use one per environment (dev, staging, prod) or per tenant.

```bash theme={null}
curl -X POST "https://api.mixpeek.com/v1/namespaces" \
  -H "Authorization: Bearer $MIXPEEK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "namespace_name": "production" }'
```

> [!NOTE]
>
> That's the whole managed create — `namespace_name` is the only required field. You don't register extractors up front: creating a collection with `features: [...]` auto-provisions the pipelines the namespace needs (see [Features](https://docs.mixpeek.com/docs/processing/features.md)). The old `feature_extractors` field is deprecated.

### Standalone (bring your own vectors)

To upsert your own vectors instead of Mixpeek-managed extraction, declare the index shapes with `vector_configs`:

```bash theme={null}
curl -X POST "https://api.mixpeek.com/v1/namespaces" \
  -H "Authorization: Bearer $MIXPEEK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "namespace_name": "byo-vectors",
    "vector_configs": [
      { "name": "clip", "dimension": 768, "metric": "cosine" }
    ]
  }'
```

> [!NOTE]
>
> `vector_configs` is a **list** of `{ name, dimension, metric }` objects (not a single object). `name` and `dimension` are required; `metric` defaults to `cosine` (also `euclidean`, `dot_product`). Passing `vector_configs` with no `features`/`feature_extractors` **infers standalone mode** — no `mode` field needed. Omit `vector_configs` entirely and indexes auto-create on first upsert.

Every subsequent request needs two headers: `Authorization: Bearer mxp_sk_...` and `X-Namespace: ns_...`.

[Namespace API →](https://docs.mixpeek.com/docs/api-reference/namespaces/create-namespace.md)

## Create a Bucket

Buckets are schema-validated containers for raw files. Define what blob types you accept (text, image, audio, video, json, binary).

```bash theme={null}
curl -X POST "https://api.mixpeek.com/v1/buckets" \
  -H "Authorization: Bearer $MIXPEEK_API_KEY" \
  -H "X-Namespace: $NAMESPACE_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "bucket_name": "product-catalog",
    "bucket_schema": {
      "properties": {
        "product_text": { "type": "text", "required": true },
        "hero_image": { "type": "image" }
      }
    }
  }'
```

[Bucket API →](https://docs.mixpeek.com/docs/api-reference/buckets/create-bucket.md)

### Storage class

Pass an optional `storage_class` on create/update to pick a cost tier for a bucket's objects. It's provider-agnostic — mapped to your object store:

| `storage_class` | GCS | S3 / MinIO | Best for |
| - | - | - | - |
| `standard` (default) | STANDARD | STANDARD | Hot, frequently-read buckets |
| `nearline` | NEARLINE | STANDARD\_IA | Warm / occasional access |
| `coldline` | COLDLINE | GLACIER\_IR | Cold / rare access |
| `archive` | ARCHIVE | GLACIER | Long-term retention |

> [!NOTE]
>
> **Applied on write for sync-based ingestion.** For buckets fed by a storage **sync** (S3, GCS, Drive, RSS, and other sources, which is the primary media path), the tier is set on each object at write time. Direct uploads (`POST /objects`) and presigned client uploads do not set it, and existing objects keep the tier they were written with. Keep hot, retriever-source buckets on `standard`, and reserve cheaper tiers for large write-once/read-occasionally media.

## Connect External Storage

Sync files directly from your existing cloud storage instead of uploading manually. Mixpeek reads from your provider — no migration needed. This is a **two-step** flow: create a reusable **connection** (holds the credentials, lives at the organization level), then attach a **sync** to a bucket that references it.

**Step 1 — Create the connection** (once per provider account):

```bash theme={null}
curl -X POST "https://api.mixpeek.com/v1/organizations/connections" \
  -H "Authorization: Bearer $MIXPEEK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production S3",
    "provider_type": "s3",
    "provider_config": {
      "provider_type": "s3",
      "bucket_name": "my-source-bucket",
      "region": "us-east-1",
      "credentials": {
        "access_key_id": "AKIA...",
        "secret_access_key": "..."
      }
    }
  }'
```

The response includes a `connection_id` (`conn_...`). Credentials are encrypted at rest and reusable across buckets.

**Step 2 — Attach a sync** to your bucket (flat body — no wrapper objects):

```bash theme={null}
curl -X POST "https://api.mixpeek.com/v1/buckets/$BUCKET_ID/syncs" \
  -H "Authorization: Bearer $MIXPEEK_API_KEY" \
  -H "X-Namespace: $NAMESPACE_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "connection_id": "conn_abc123",
    "source_path": "/videos/",
    "sync_mode": "continuous",
    "polling_interval_seconds": 3600
  }'
```

Then trigger the first sync:

```bash theme={null}
curl -X POST "https://api.mixpeek.com/v1/buckets/$BUCKET_ID/syncs/$SYNC_ID/trigger" \
  -H "Authorization: Bearer $MIXPEEK_API_KEY" \
  -H "X-Namespace: $NAMESPACE_ID"
```

After the initial sync, new files are picked up automatically at the configured polling interval. Use `continuous` mode (vs `initial_only`) to keep picking up new and changed files — only new or modified files since the last sync are processed, so existing files aren't reprocessed.

| Provider | Auth Method | S3-Compatible |
| - | - | - |
| AWS S3 | IAM User / Role | Native |
| Google Cloud Storage | Service Account Key | No |
| Azure Blob Storage | Access Key / Managed Identity | No |
| Cloudflare R2 | R2 API Token | Yes |
| Backblaze B2 | Application Key | Yes |
| Wasabi | Access Key | Yes |
| Tigris | Access Key | Yes |
| Box | OAuth | No |
| Mux | API Token | No |
| Supabase | Service Key | Yes |

See [Object Storage providers](https://docs.mixpeek.com/docs/integrations/object-storage/overview.md) for provider-specific setup guides.

[Sync API →](https://docs.mixpeek.com/docs/api-reference/bucket-syncs/create-sync-configuration.md)

## Register Objects

Objects are raw multimodal assets within a bucket. Two paths:

**URL references** — point to files in your existing storage:

```bash theme={null}
curl -X POST "https://api.mixpeek.com/v1/buckets/$BUCKET_ID/objects" \
  -H "Authorization: Bearer $MIXPEEK_API_KEY" \
  -H "X-Namespace: $NAMESPACE_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "key_prefix": "/products",
    "blobs": [
      { "property": "hero_image", "type": "image", "data": "https://example.com/photo.jpg" },
      { "property": "product_text", "type": "text", "data": "Wireless headphones" }
    ]
  }'
```

**Inline text.** A `text` blob takes the text itself in `data`:
`"data": "Wireless headphones"`, or `"data": {"text": "Wireless headphones"}`. A string
that starts with `http://`, `https://` or `s3://` is read as a URL. A bare
base64 string that decodes to readable text is rejected with a 422. To send encoded text, use
`"data": "data:text/plain;base64,<encoded_data>"` or
`"data": {"base64": "<encoded_data>", "mime_type": "text/plain"}`.

**Direct uploads** — upload to Mixpeek-managed storage via presigned URLs:

```bash theme={null}
curl -X POST "https://api.mixpeek.com/v1/buckets/$BUCKET_ID/uploads" \
  -H "Authorization: Bearer $MIXPEEK_API_KEY" \
  -H "X-Namespace: $NAMESPACE_ID" \
  -H "Content-Type: application/json" \
  -d '{ "filename": "photo.jpg", "content_type": "image/jpeg" }'
```

Then PUT the file to the returned `presigned_url` and confirm with `POST /uploads/{id}/confirm`.

For bulk imports, use [batch uploads](https://docs.mixpeek.com/docs/api-reference/bucket-uploads/batch-create-uploads.md) or connect your object storage via [sync configurations](https://docs.mixpeek.com/docs/integrations/object-storage/overview.md).

### Content addressing

Every blob carries a `details.hash` in `sha256:<hex>` form. You can supply it, or
let the platform compute it.

Send `checksum` on a blob to pin the value yourself. It propagates verbatim to
the stored blob's `details.hash`:

```json theme={null}
{
  "property": "hero_image",
  "type": "image",
  "data": "https://example.com/photo.jpg",
  "checksum": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
}
```

A bare 64-character hex string is accepted and normalized to `sha256:<hex>`.

> [!NOTE]
>
> **A malformed `checksum` is rejected with a 422 that tells you how to fix it**,
> so a typo cannot leave you with a blob you believe is pinned and is not.
>
> That guardrail arrived with the field itself. A deployment that predates it
> ignores `checksum` instead of rejecting it, and you get `details.hash: null`
> with no error — which is the failure the 422 exists to prevent. Confirm your
> deployment has it before relying on it:
>
> ```bash theme={null}
> for i in $(seq 1 12); do
>   curl -sS -H 'Connection: close' "$MIXPEEK_API_URL/docs/openapi.json" \
>     | python3 -c "import json,sys; print('checksum' in json.load(sys.stdin)['components']['schemas']['CreateBlobRequest']['properties'])"
> done | sort | uniq -c
> ```
>
> Twelve `True` means the guardrail is live everywhere. Any `False` means part of
> the fleet still drops `checksum` silently, so verify `details.hash` on a written
> blob rather than assuming it took.
>
> Sample rather than call once, and keep `Connection: close`. A single request
> answers *which server replied*, not *whether every server agrees*, and
> keepalive pins you to one of them — both give a clean unanimous answer from a
> mid-rollout fleet. Measured on Mixpeek Cloud during one rollout: 9 of 12
> fetches carried the field and 3 did not.
>
> Point it at **your own** host. A dedicated tenant deployment rolls on its own
> schedule, so it can be several releases behind the shared API or ahead of it.
> Measured the same minute: the shared API was mid-rollout on a newer build while
> a tenant host was converged on an older one, and the two gave opposite answers
> about the same field.

Omit `checksum` and the platform fills `details.hash` itself wherever it touches
the bytes: inline-text blobs on write, and every object arriving through
[storage sync](https://docs.mixpeek.com/docs/integrations/object-storage/overview.md).

Inline text works in both dialects, `"data": "some text"` and
`"data": {"text": "some text"}`, and both now persist. The dict form previously
skipped persistence entirely; blobs written that way also gain a
`properties.url` they did not have before.

> [!WARNING]
>
> **`details.hash` can be `null` on older objects.** Content addressing applies to blobs
> the platform has touched since the feature landed, so objects synced before then carry
> no hash.
>
> A null hash means the hash was never computed, and it says nothing about the content.
> Do not write code that treats `details.hash` as reliably present on older objects.

[Object API →](https://docs.mixpeek.com/docs/api-reference/bucket-objects/create-object.md) · [Upload API →](https://docs.mixpeek.com/docs/api-reference/bucket-uploads/create-upload.md)

## Process with Batches

Batches group objects for extraction. Create a batch, then submit it:

```bash theme={null}
# Create batch
curl -X POST "https://api.mixpeek.com/v1/buckets/$BUCKET_ID/batches" \
  -H "Authorization: Bearer $MIXPEEK_API_KEY" \
  -H "X-Namespace: $NAMESPACE_ID" \
  -H "Content-Type: application/json" \
  -d '{ "object_ids": ["obj_abc", "obj_def"] }'

# Submit for processing
curl -X POST "https://api.mixpeek.com/v1/buckets/$BUCKET_ID/batches/$BATCH_ID/submit" \
  -H "Authorization: Bearer $MIXPEEK_API_KEY" \
  -H "X-Namespace: $NAMESPACE_ID"
```

### Batch Lifecycle

```
DRAFT → QUEUED → PROCESSING → COMPLETED
                     ↘        ↘
                    FAILED    COMPLETED_WITH_ERRORS
```

Poll `GET /v1/buckets/{id}/batches/{id}` until the status is terminal — `COMPLETED`, `COMPLETED_WITH_ERRORS`, `FAILED`, or `CANCELED` (a poller that waits only for `COMPLETED` hangs on partial success) — or use [webhooks](https://docs.mixpeek.com/docs/platform/operations.md#webhooks) to get notified on `batch.completed`.

[Batch API →](https://docs.mixpeek.com/docs/api-reference/bucket-batches/create-batch.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.