> ## 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.

# Custom Extractor API (Dedicated Infrastructure)

> HTTP reference for the custom-extractor upload, deploy, real-time, and version-management endpoints — available on dedicated Enterprise deployments

<Warning>
  **Dedicated infrastructure only.** These endpoints are served by dedicated Enterprise deployments — they are **not** exposed on the shared public API at `api.mixpeek.com`, where they return `404`/`405`. See [Custom Extractors → Availability](/processing/custom-extractors#availability). [Contact your account team](https://mixpeek.com/contact) to provision a dedicated deployment.

  Because this surface isn't in the shared OpenAPI, it isn't part of the auto-generated API Reference. This page is the hand-maintained contract; the reference client is `server/scripts/api/plugins.py`.
</Warning>

<Info>
  **Naming.** On this surface, custom extractors are addressed as **plugins**: the path segment is `/plugins`, and a deployed extractor's `plugin_id` is `{name}_{version}` with dots replaced by underscores — e.g. `text_embed@1.0.0` → `text_embed_1_0_0`. All endpoints are namespace-scoped and take `Authorization: Bearer $MIXPEEK_API_KEY`.
</Info>

## Lifecycle: upload → deploy → search

The CLI (`python server/scripts/api/plugins.py push` / `deploy`) wraps these calls. The raw HTTP flow:

```bash theme={null}
NS=ns_...                 # your namespace id
API=https://<your-dedicated-host>/v1
AUTH=(-H "Authorization: Bearer $MIXPEEK_API_KEY" -H "Content-Type: application/json")

# 1. Request a presigned upload URL (auto-bumps the patch version if you omit it)
UP=$(curl -s -X POST "$API/namespaces/$NS/plugins/uploads" "${AUTH[@]}" \
  -d '{"name":"text_embed","version":"1.0.0","file_size_bytes":5000}')
UPLOAD_ID=$(echo "$UP" | jq -r '.upload_id')
PRESIGNED=$(echo "$UP" | jq -r '.presigned_url')

# 2. PUT the zipped extractor to S3
curl -s -X PUT "$PRESIGNED" -H "Content-Type: application/zip" --data-binary @text_embed.zip

# 3. Confirm — runs the security scan + manifest validation
curl -s -X POST "$API/namespaces/$NS/plugins/uploads/$UPLOAD_ID/confirm" "${AUTH[@]}" \
  -d '{"message":"initial release"}'
# → { "success": true, "plugin_id": "text_embed_1_0_0", "validation_errors": [] }

# 4. Deploy (omit deployment_type for full deploy incl. realtime; batch_only skips the HTTP endpoint)
curl -s -X POST "$API/namespaces/$NS/plugins/text_embed_1_0_0/deploy" "${AUTH[@]}"

# 5. Poll deployment status until DEPLOYED
curl -s "$API/namespaces/$NS/plugins/text_embed_1_0_0/status" "${AUTH[@]}"
```

Once `DEPLOYED`, reference the extractor's `feature_uri` in a collection's `feature_extractor` and in retriever `feature_search` stages exactly as you would a built-in — see the [end-to-end walkthrough](/processing/custom-extractors#end-to-end-extractor-collection-retriever).

| Deployment status    | Meaning                                     |
| -------------------- | ------------------------------------------- |
| `QUEUED` / `PENDING` | Waiting in / triggered the deployment queue |
| `IN_PROGRESS`        | Blue-green rollout in progress              |
| `DEPLOYED`           | Ready for real-time inference               |
| `NOT_DEPLOYED`       | Batch-only mode                             |
| `FAILED`             | Check the `error` field                     |

## Endpoint reference

All paths are prefixed with `/v1/namespaces/{namespace_id}`.

### Upload & deploy

| Method | Path                                   | Purpose                                                                                                                         |
| ------ | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `POST` | `/plugins/uploads`                     | Request a presigned upload URL. Body: `{name, version?, file_size_bytes}` → `{upload_id, presigned_url, version}`               |
| `POST` | `/plugins/uploads/{upload_id}/confirm` | Confirm the S3 upload; runs security scan + manifest validation. Body: `{message?}` → `{success, plugin_id, validation_errors}` |
| `POST` | `/plugins`                             | Direct multipart upload (alternative to the presigned flow)                                                                     |
| `POST` | `/plugins/{plugin_id}/deploy`          | Deploy or redeploy. Query: `deployment_type=batch_only` to skip the realtime endpoint                                           |
| `POST` | `/plugins/{plugin_id}/undeploy`        | Tear down the deployment (keeps the version)                                                                                    |
| `GET`  | `/plugins/{plugin_id}/status`          | Deployment status                                                                                                               |
| `POST` | `/plugins/{plugin_id}/realtime/test`   | Invoke the deployed `realtime.py` endpoint with a test payload                                                                  |

### Discover & inspect

| Method | Path                            | Purpose                                                  |
| ------ | ------------------------------- | -------------------------------------------------------- |
| `GET`  | `/plugins`                      | List custom plugins in the namespace                     |
| `GET`  | `/plugins/{plugin_id}`          | Plugin details (schemas, deployment + validation status) |
| `GET`  | `/plugins/{plugin_id}/source`   | Download source files of the active version              |
| `GET`  | `/plugins/{plugin_id}/download` | Presigned archive download URL                           |
| `GET`  | `/plugins/available`            | Org-level plugins available to enable in this namespace  |

### Version management

| Method | Path                                             | Purpose                                           |
| ------ | ------------------------------------------------ | ------------------------------------------------- |
| `GET`  | `/plugins/by-name/{slug}/versions`               | Version history with deploy timestamps + messages |
| `POST` | `/plugins/by-name/{slug}/rollback`               | Restore a previous version as active              |
| `GET`  | `/plugins/by-name/{slug}/diff?v1=1.0.0&v2=1.0.1` | Diff source files between two versions            |

### Org plugins & namespace config

| Method   | Path                               | Purpose                                                               |
| -------- | ---------------------------------- | --------------------------------------------------------------------- |
| `POST`   | `/plugins/org/{plugin_id}/enable`  | Enable an org-level plugin in this namespace                          |
| `POST`   | `/plugins/org/{plugin_id}/disable` | Disable an org-level plugin in this namespace                         |
| `POST`   | `/plugins/reconfigure`             | Reconfigure the namespace vector-store schema for a plugin's features |
| `DELETE` | `/plugins/{plugin_id}`             | Delete a plugin version                                               |

## CLI equivalents

`server/scripts/api/plugins.py` is the maintained client for this surface (reads `MIXPEEK_API_KEY`, `MIXPEEK_NAMESPACE`, `MIXPEEK_API_URL`):

| CLI             | Endpoint(s)                                                            |
| --------------- | ---------------------------------------------------------------------- |
| `push`          | `POST /plugins/uploads` → `PUT` → `POST /plugins/uploads/{id}/confirm` |
| `pull`          | `GET /plugins/by-name/{slug}/versions` → `GET /plugins/{id}/source`    |
| `status`        | `GET /plugins/by-name/{slug}/versions`                                 |
| `log`           | `GET /plugins/by-name/{slug}/versions`                                 |
| `rollback`      | `POST /plugins/by-name/{slug}/rollback`                                |
| `diff`          | `GET /plugins/by-name/{slug}/diff`                                     |
| `lint` / `test` | offline — no API call (work on any plan)                               |

See [Custom Extractors](/processing/custom-extractors) for the full authoring guide and [Local Development](/processing/custom-extractors#local-development) for `lint`/`test`.
