Runtime recipe registration¶
Code-shipped recipes under src/mantis_agent/recipes/<name>/ require
a fork + redeploy to add or change. Runtime recipes live on the
shared volume at /data/tenants/<tenant>/recipes/<name>.json and can
be registered, fetched, and deleted over HTTP — no redeploy.
Use runtime recipes for:
- Domain-specific
spam_indicators,forbidden_controls,listing_card_exclusions,rejection_intentsthat don't make sense to ship with the core code. - Per-tenant overrides of a shipped recipe (e.g. extend
marketplace_listingswith your own spam tokens). - Quick experiments — add, run, observe, iterate without a deploy.
For one-off plans, the inline extract block (see
Sending plans / inline extraction schema)
is usually enough. Reach for runtime recipes when the same schema
shape is reused across many plans.
Endpoints¶
| Method | Path | Body | Returns |
|---|---|---|---|
POST |
/v1/recipes |
{"name": "<slug>", "schema": {...}} |
Persisted {name, schema} envelope |
GET |
/v1/recipes |
— | {"recipes": [{"name": "..."}]} |
GET |
/v1/recipes/{name} |
— | {name, schema} or 404 |
DELETE |
/v1/recipes/{name} |
— | {name, deleted: bool} |
All routes require the same X-Mantis-Token as /v1/predict and are
scoped to the caller's tenant — recipes registered under tenant A are
not visible to tenant B.
Name rules¶
- Letters, digits, hyphen, underscore. No dots, slashes, or spaces.
- 1–64 chars.
- Cannot start with
-.
These constraints exist so the name maps cleanly to a filesystem path and can't be used for traversal.
Schema payload¶
The schema field accepts the same shape as the inline extract
block — internally it's parsed by ExtractionSchema.from_dict.
curl -fsS -X POST "$ENDPOINT/v1/recipes" \
-H "X-Mantis-Token: $MANTIS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "my_marketplace",
"schema": {
"entity_name": "property",
"fields": [
{"name": "address", "type": "str", "required": true},
{"name": "price", "type": "str", "required": true},
{"name": "beds", "type": "str", "required": false},
{"name": "url", "type": "str", "required": true}
],
"required_fields": ["address", "price", "url"],
"spam_indicators": ["sponsored", "featured"],
"forbidden_controls": ["Contact Agent", "Request Tour"],
"rejection_intents": {"sponsored": "skip"}
}
}'
Validation runs at write time — malformed payloads return 400 at
registration, not at extract time. The recipe is then immediately
usable by any plan submitted under that tenant.
Resolution precedence¶
When a plan references a recipe by name (e.g. _recipe: "my_marketplace"
in the task suite envelope), the resolver:
- Looks for a runtime recipe under the caller's tenant directory.
- Falls back to the code-shipped recipe with that name.
So a tenant can override a shipped recipe (marketplace_listings,
job_listings, …) by registering a runtime recipe with the same name —
the override only affects that tenant.
Limits and caveats¶
- Tenant-scoped. No cross-tenant sharing; if you need a shared taxonomy across tenants, ship it as a code recipe.
- No versioning. A
POSToverwrites the existing recipe of the same name. Use distinct names (my_marketplace_v2) if you want side- by-side variants. - Best-effort persistence. The store writes a single JSON file per
recipe to the shared volume. Failed writes raise
500; clients should treat the operation as not-yet-applied and retry. - Not yet: plan / loop / rewards overlays. Only
ExtractionSchemais registrable at runtime today. The shipped-recipe surface still carriesplan.json,loop_overrides.py, etc.; runtime equivalents are deferred.
Python SDK helpers¶
Coming in a follow-up. Use requests/httpx directly meanwhile —
the endpoints are deliberately small and the JSON shape is stable.
See also¶
- Sending plans — when to use inline extract vs a recipe
- Recipes (code-shipped) — the static recipe inventory