Quick Start
source and a feature_extractor; every retriever needs namespace, collections, and its stages pipeline. validate names any missing field, warns on unknown top-level keys (a typo like retreivers: won’t silently drop your search pipeline), and checks that references (namespace, bucket, collection names) resolve within the manifest.
Applying over resources that already exist
apply takes a mode query parameter. It defaults to create_only, which fails if any
resource in the manifest already exists, because the manifest is treated as a closed world:
a reference to a bucket that exists but is not declared is a validation error.
Use create_missing to add to a namespace that already has resources. It creates what is
missing, leaves what exists alone, and resolves references against the resources already in
the namespace.
apply plans before it creates anything. It runs the same checks as validate, and any
error refuses the whole manifest: nothing is created, and each error names the resource, the
field and the fix. The plan refuses:
- an extractor that no longer exists or is turned off on this deployment
- a retriever stage that is not registered, such as a retired stage type
- a feature URI that no collection in the manifest writes, such as one naming a renamed model
-
two collections in one namespace that write the same vector at different sizes, such as one
multimodal_extractorv2 collection withoutput_dimensionality: 1536and another at the default 3072. A namespace holds one index per vector, so give both the same size: - an alert application that does not map an input its alert’s retriever requires
-
a resource its create request refuses: the namespace, bucket, collection, retriever, cluster, taxonomy, trigger, webhook, alert and storage connection are each built into the same request a real create sends, so a cluster with a non-boolean
hierarchical_groupingor a taxonomy with an unknowntaxonomy_typeis refused here and readscluster:<name>: <field>: <reason> -
a bucket sync whose
sync_modeis notinitial_onlyorcontinuous -
a
seedthat names a sample key the Mixpeek sample bucket does not hold (a read-only listing; if the bucket cannot be listed,validateadds a warning and refuses nothing)
POST /v1/manifest/validate runs every check above, so a manifest it passes is one apply accepts. validate, diff and lint each take the parameters form field that apply takes.
A namespace budget above your organization’s budget is set to the organization’s budget, and
apply reports the change in warnings.
dry_run=true returns that plan and creates nothing. If a resource fails partway through,
apply rolls back the ones it already created, and if the API is stopped in the middle of an
apply, it rolls that apply back when it starts again.
Core Operations
Resource Types
Manifests support these resource types, applied in dependency order:- Namespaces - Isolation boundaries with feature extractors and a
budget - Extractors - The extractors collections run, declared under
extractors: - Storage connections and buckets - Object storage with schemas
- Bucket syncs - Scheduled pulls from a connection
- Collections - Document stores with indexing
- Retrievers - Search pipelines
- Alerts - Retrievers or system conditions that notify you
- Clusters and taxonomies - Grouping and classification
- Triggers and webhooks - Automation
Extractors
Anextractors: section declares the extractors the manifest’s collections run. Each entry has a name, a namespace, a version (default v1), a source and an optional compute_profile. Collections reference an entry by name in feature_extractor, and namespaces in feature_extractors. Once a manifest has an extractors: section, every extractor a collection or namespace names must be declared in it. Extractors apply before collections, so a collection never names an extractor that does not exist yet.
source is one of:
marketplace: a feature extractor name from the registry, for exampletext_extractor. This is the default whensourceis omitted ({marketplace: <name>}).model: a model your organization uploaded throughPOST /v1/models/uploads, asorg:<model_id>. Apply validates the model if it has not been validated, then serves it as an extractor at zero replicas until the first call.huggingface: a model repository on the Hugging Face Hub, as{repo, revision, framework, token_secret}. Apply imports the snapshot into your organization’s model registry through the same checks an upload and confirm run, then serves it exactly likemodel. No manual upload is needed.
huggingface source is checked against the Hub at validate (read-only: repo info, refs and the file list), and apply refuses the same things:
revisionis required and must be a full 40-character commit sha or a tag. A branch such asmainis refused because it moves, and the same manifest has to import the same weights on every apply.- Weights must be safetensors. The import takes
.safetensors,.json,.txtand.modelfiles and skips pickle weights (pytorch_model.bin,.pt,.ckpt), because loading a pickle file can run code. A repo with only pickle weights is refused. - The files the framework needs must be at the repo root:
config.jsonandmodules.jsonforsentence-transformers,config.jsonfortransformers. An extractor can servesentence-transformersmodels today, soframework: transformersis refused on an extractor. - The snapshot must fit the model upload limit (5 GB). The limit is checked from the file list and again while downloading.
- A gated or private repo needs
token_secret, the name of an organization secret holding a Hugging Face access token (HF_TOKENor${{ secrets.HF_TOKEN }}). A public repo needs none.
GET /v1/models/{model_id} shows license). Applying the same repo and revision again in the same organization reuses that entry with no Hub call and no download, and a tag that points at an already imported commit reuses it too. While the import runs, the apply’s task shows the extractor as importing_model; after that it moves through validating_model, deploying_model and warming or ready, the same states as a model source.
compute_profile carries the fields of a compute profile (resource_type, batch_size, max_concurrency, memory). Apply stores them as the namespace’s batch override for that extractor, merged over the extractor’s built-in profile when a batch is built. Two nested blocks are checked rather than stored:
batch.enabled: onlytrueis accepted. A collection that names the extractor runs it in batch.serve: the real-time serving block (enabled,min_replicas,max_replicas,min_reason,idle_timeout_s). Validate and apply check it; realtime still runs under the extractor’s built-in serve settings, andwarningssays so. Amin_replicasabove 0 is refused with the monthly cost of the warm replicas named, because a template adds no always-on floor. Leave it at 0 and the first call after idle waits for a replica to start.
Parameters
Aparameters: section declares the values a manifest needs from whoever applies it, so a template ships without a hand edit or a secret inside it. Reference a parameter anywhere in the document as ${{ params.<name> }}. A reference that is the whole value takes the parameter’s type; one inside a longer string is formatted into it as text. An array parameter is a list and can only be a whole value.
parameters form field, a JSON object of name to value:
-F 'parameters={"search_collections": ["faqs"], "tier": "pro"}'. A value outside enum is refused with the allowed values listed, and an array written inside a longer string ("types: ${{ params.search_collections }}") is refused.
A missing required parameter, a value of the wrong type, a reference to an undeclared parameter, or a name declared twice is refused before any resource is created, naming parameters.<name>.
Budget
A namespace entry takes abudget with daily_usd and monthly_usd, the same fields the namespaces API takes as spend_budget. Omit it to use your organization’s namespace default. A value above your organization’s budget is set to the organization’s budget, and apply reports the clamp in warnings.
Alerts
Analerts: section creates alerts beside the retrievers they watch. An alert mirrors the create request of the alerts API, plus namespace and retriever by name.
Track an apply
apply returns a task_id (absent on a dry run). GET /v1/tasks/{task_id} returns every resource of the plan with its states in order (planned, creating, created, exists, updated, or failed with the finding; a collection also passes warming and ready), each with the time it was reached. The same events arrive on the change feed and on a webhook.
readiness: for each collection whose extractor needs a real-time serving app, whether that app is running. An app scaled to zero is running; its first call answers 503 with Retry-After while it warms.
Secret References
Use${{ secrets.NAME }} to reference organization secrets:
Seed Data
Aseed: section gives a namespace sample objects to answer its first query. After the resources are created, apply ingests them into the named bucket with one batch, bounded by max_objects (default 50, at most 500). Applying the manifest again finds that batch and ingests nothing more.
Each object names a source for each blob property under blobs. The blob type comes from the bucket schema property. A source takes one of three forms.
A Mixpeek sample key, from the sample set samples/starter/<file>:
https:// URL (or http://, which object create also accepts). Mixpeek fetches it the way POST /v1/buckets/{id}/objects fetches a URL blob, with the same checks: the host must resolve to a public address, redirects are re-checked, and the response must be reachable and match the property’s type.
{connection, path}. connection names a storage connection declared under storage_connections in the same manifest, or one that already exists in the organization. path is the object’s key, written the way a bucket sync’s source_path is. Apply reads the object through the connection with the same provider client a bucket sync uses and copies it into the bucket’s storage. The connection’s provider must be s3, tigris, gcs, azure_blob, backblaze or supabase; Google Drive and SharePoint name files by id and Snowflake yields rows, so those connections are refused here and belong in bucket_syncs.
s3://, gs:// or file:// string, since object storage is read only through a connection.
A seed’s batch is billed to your organization when it is submitted, like any batch you create, whichever form its blobs take.
When the bucket declares other properties, set them on the object beside blobs, the way the object create API takes them:
blobs is refused, and the error names the bucket, the object and the property.
Seed or bucket sync
Useseed for a curated handful of objects, up to 500, that the first query should find. Use bucket_syncs with sync_mode: initial_only to import everything under a prefix of a storage connection once. The sync lists the prefix, applies file_filters and schema_mapping, and ingests the files in pages, with retries and a dead-letter queue for files that fail. sync_mode: continuous keeps polling for new files.
sync_mode takes exactly initial_only or continuous; any other value is refused at validate.
Taxonomy Labels
A flat taxonomy tags each document with its closest label. Write the labels inline on the taxonomy and name the feature to match on:
The manifest generates the resources the taxonomy runs on, and validate, the dry run and apply treat them like declared ones:
- a bucket
<taxonomy>-labelswithcontent(text) andlabel(string) properties - a collection
<taxonomy>-labelsthat embeds each label’s text,<label>: <description>, with the extractor and featurematch_onnames. It copiesoutput_dimensionalityfrom the collections applying the taxonomy, so labels and documents share one vector space - a retriever
<taxonomy>-lookupthat returns the single closest label for a document’s vector - a
seed:entry for the label bucket, one object per label - the taxonomy’s
config, wiring the retriever and the label collection, withlabelwritten totarget_field
Generated for taxonomy '<name>', and each row of the dry run and apply response for one carries generated_for: <taxonomy>. A resource you declare under a generated name is refused; rename yours.
Validate refuses:
- labels on a taxonomy that is not flat
- a label given twice, or an empty label list
- labels together with
config.source_collectionorconfig.retriever - a
match_onwhose extractor cannot compute that feature from text, such as a transcription embedding, which is computed from audio - a collection applying the taxonomy that does not write the
match_onfeature
type: flat with nodes reads the same way: each node’s name becomes a label and its description the label’s description. It needs match_on unless it can be inferred as above, and is refused until it has one.
The seed runs once per label bucket, like any seed. To change the labels of a namespace that is already applied, add or edit objects in the <taxonomy>-labels bucket directly. An export writes the generated resources and the taxonomy config out in full, since the label text lives in the bucket’s objects; the exported manifest applies as it is.
Acceptance
A manifest can carry anacceptance: block: the steps that show its use case works and what each must return. Apply creates nothing from it. Apply runs the steps when you ask with run_acceptance=true, and Mixpeek’s template acceptance harness runs them after every template apply.
Each kind reads its own
expect fields:
One step of each kind:
expect field the step’s kind does not read, and a step with no outcome, each naming acceptance.steps.<name>.
Wait for the seed and run the steps on apply
Two query flags onapply make it wait until the namespace is usable and then prove it:
Both wait within
deadline_seconds, or 900 seconds when you omit it. One clock covers the serving apps, the seed and the polling of documents and alert steps, which re-read until their count is reached or the time runs out.
successreports resource creation only, exactly as without the flags. Readacceptance.passedfor the use case.- A step passes only when it ran and every expected outcome held. A step that could not run is
passed: falseand itserrorstarts withnot run:and says why: a seed batch failed or was still processing at the deadline, a serving app was not running, the apply rolled back, or the cost cap was reached. - A failed run is also named in
errors, so a caller that only readserrorssees it. - On a dry run both flags do nothing, and
warningssays so.
apply are normal executions in your organization: each query and realtime call is billed like the same call made directly. max_cost_usd is read from your usage meter (the per-namespace credits in GET /v1/organizations/billing/usage/breakdown). Before each step after the first, a run that has reached the cap stops, and the remaining steps report not run. A closing max_cost_usd row compares the spend with the cap. When this apply created the namespace, the spend counts from its creation, seed ingest included. On a namespace that already existed it counts from the first step. Metering of very small usage can lag a few minutes, so the reading is a lower bound right after the run.
Re-running the steps. Re-apply the same manifest with mode=create_missing&run_acceptance=true. Every resource reads exists, nothing is created, the seed is not ingested again (its earlier batch is reused), and the steps run against the existing resources. Under the default create_only a re-apply is refused because the namespace exists, and the steps report not run.
Dependency Resolution
Resources are created in topological order. The manifest engine:- Resolves cross-resource references automatically
- Detects circular dependencies
- Rolls back all changes if any creation fails
Authoring manifests with an LLM
An LLM can write a complete manifest on the first or second try if it reads what the API publishes and letsvalidate check the result. The loop has six steps.
-
Read the schema.
GET /v1/discovery/schemareturns:schemas: a JSON schema for every section (parameters,namespace,extractor,storage_connection,bucket,bucket_sync,collection,retriever,alert,cluster,taxonomy,trigger,webhook,seed,acceptance) and for the wholemanifest.dependency_graph: for each type, the types whose names it references. A bucket sync needs a bucket and a storage connection, a seed needs a bucket, an acceptance step needs a retriever, collection or alert.examples: a short YAML block for each type.example_manifest: the complete manifest shown below.extractor_catalog: every extractor and version this plane serves, with its input types, the feature URIs it writes and its parameters with defaults.stage_catalog: every retrieverstage_idwith its category and parameter names.authoring_loop: these steps in short form.
GET /v1/discovery/extractorsandGET /v1/discovery/stagesfor the full parameter schemas and stage examples. -
Write the manifest. Reference resources by name. Copy feature URIs from
extractor_catalog. A collection writes a feature URI only when the extractor parameters that produce it are on, for examplerun_transcription_embedding: truefor the transcript embedding. -
Validate.
POST /v1/manifest/validatereturnsvalid,errors,warnings,missing_secrets,resources(a count per type) and the declaredparameters. Validate refuses what apply would refuse, so fix every entry inerrorsand rerun. Send parameter values in theparametersform field as a JSON object. -
Lint.
POST /v1/manifest/lintreturns suggestions that each carry a code, a location, a suggestion and afix_example. Three rules catch wiring that validates but cannot work:UNUSED_BUCKET(no collection reads the bucket),TAXONOMY_INPUT_NOT_WRITTEN(the taxonomy reads a feature that the collection it is applied to does not write) andRETRIEVER_FEATURE_NOT_IN_COLLECTIONS(the retriever searches a feature that none of its own collections write). -
Dry run.
POST /v1/manifest/applywithdry_run=truereturns the plan: what would be created, in dependency order, and any refusal. Nothing is created. -
Apply.
POST /v1/manifest/applycreates the resources and returns the result, or the task to poll for progress.
POST /v1/manifest/generate with a description. The model is given the live extractor and stage catalogs and the example below. The answer goes through the same checks as validate. If they find errors, the model is shown them and asked to fix its answer, up to two times. The response keeps manifest, format, manifest_name and description, and adds validation (valid, errors, warnings) and repair_rounds. When validation.valid is false the errors are still listed, so you can fix them yourself and continue from step 3.
Complete example
This manifest syncs ad videos from S3 once, seeds a few sample objects, indexes the ads in two collections, searches them with a hybrid retriever, tags each scene with a creative format, groups scenes by look and states what a working setup returns. It validates onceads_s3_bucket is supplied and the secrets AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY exist.
Workflows
Environment Replication
Describe what is in a namespace
Adddetail=stats to an export to see the data as well as the configuration. The YAML gets a read-only status section with each collection’s document count, field types and most common field values, and each cluster’s groups with their labels and sizes. Apply ignores status, so the file still applies as-is. detail=stats covers one namespace, so pass namespace_id.
unavailable with the reason, never an empty list.

