Specta API, as shipped in the download
Specta REST API & Webhooks (v3.0)
Specta ships a JSON REST API and signed webhooks so supplier catalogues can be queued, watched and exported without anyone sitting in front of the screen.
Specta uses your own LLM API key. Extracted data stays on your server; each document goes only to the provider you configure (with a local Ollama model, it never leaves your server).
Authentication
Create a key in API & Webhooks (in the sidebar). Send it on every call:
Authorization: Bearer apk_...
The key is shown once at creation and stored as a SHA-256 hash. Revoke any time.
Endpoints
| Method & path | What it does |
|---|---|
GET /api/ping | Verify a key |
GET /api/batches?status= | Batches with roll-up status and per-status counts |
GET /api/batches/{id} | One batch with every queued file and its state |
GET /api/catalogs?status= | The catalogues |
POST /api/catalogs | Queue a supplier document (multipart/form-data) |
GET /api/exports?catalog_id=&preset=&format= | Export a catalogue |
GET /api/openapi.json | OpenAPI 3 spec (no auth) |
Batches are the async part
A batch is the one genuinely long-running thing here: you queue twenty supplier PDFs, the run screen chews through them one file at a time, and you go and do something else. GET /api/batches gives you each batch's rolled-up status — processing, attention, extracted, reviewed or empty — plus the counts behind it (queued, extracting, extracted, failed, reviewed, total, done, remaining).
That status is the app's own roll-up, not a second opinion: attention means at least one file failed, reviewed means every file has been signed off, and a batch is never reported done while anything is still queued.
Queue a document
curl -X POST https://your-install/api/catalogs \
-H "Authorization: Bearer apk_..." \
-F "file=@supplier-2026.pdf" -F "supplier=Acme Ltd" -F "schema_id=3"
multipart/form-data, because JSON cannot carry a PDF. Same 20 MB cap, same MIME allow-list and same template fallback as the upload form — and, like the form, **the MIME type is read from the file's bytes, never from the header the client sends**.
Extraction stays a separate, explicit step. Queuing a document does not spend anything; extraction spends your LLM credit, and nothing here should spend that on your behalf without you asking. Trigger it from the catalogue screen or the batch run.
Export
GET /api/exports?catalog_id=12&preset=woocommerce
GET /api/exports?catalog_id=12&format=json
preset is plain, woocommerce or shopify. This serves the same bytes the export buttons serve — including a template's saved column mapping when one exists for that platform, at the same precedence — so a nightly job and a human clicking Export cannot produce two different files for the same catalogue.
format=json returns the products as structured data instead, each carrying needs_review computed from the same confidence threshold the review screen uses.
Webhooks
Add receiver URLs in API & Webhooks. Events:
batch.completed— a batch leavesprocessing, whether it finished cleanly or with failurescatalog.extracted— a catalogue has been read: a single upload's extraction, or each file a batch reads. The payload carries the catalogue and itsproductscount
batch.completed fires exactly once, on the transition out of processing. The run screen polls after every file; an alert that arrives twenty times is an alert nobody reads. A batch that ends with failures still completes — attention is a finished state, not a limbo — and the payload carries the full summary so a receiver can decide whether anyone needs waking up.
Each delivery is an HTTP POST with a JSON body and:
X-Specta-Event: batch.completed
X-Specta-Signature: sha256=<hex HMAC-SHA256 of the raw body, keyed with the endpoint's secret>
Verify the signature before trusting a payload. Delivery is one attempt with a short timeout; the Recent deliveries log shows every attempt and its response code. Design receivers to be idempotent.
Email alerts
Set an SMTP server and a notification address in Settings to get a mail when a batch finishes, including how many files failed and a link straight to the batch.
Backup
/backup.json exports every table with both the SMTP password and your LLM API key redacted.
Why there is no product import
Products in Specta exist only as extraction output, scored field by field against a template and carrying a confidence value that drives the review heat-map. There is no manual product-entry screen to mirror, and importing a CSV of finished products would bypass the template + confidence model that is the entire reason the product exists. The import path here is a supplier document; the schema is the thing you configure.
Limits, honestly
- The API acts at user level; there is no separate scope system.
- List endpoints cap at 200 rows.
- One delivery attempt per webhook event (log + idempotent receivers, not a retry queue).
- Extraction and review stay in the UI — both are judgement calls that spend money or attention.
- Extraction quality is your model's, not Specta's. The confidence scores tell you where to look.
Own It 3.0 — the agent endpoint and the rest
MCP — Specta for an AI assistant
Specta speaks MCP (Model Context Protocol) on one route, so an assistant can review a normalised catalogue, find what is incomplete and pull export-ready rows — without anyone writing an integration.
POST /mcp
Authorization: Bearer <your API key> ← the SAME revocable key the REST API uses
Content-Type: application/json
GET /mcp answers 405 with Allow: POST, so a misconfigured client is told what to do rather than left guessing at a 404.
| Tool | Writes? | What it does |
|---|---|---|
list_catalogs | no | Catalogues with status, supplier and schema |
catalog_detail | no | One catalogue, its schema columns and a completeness summary |
list_items | no | Products; incomplete_only returns just the ones missing required attributes |
item_detail | no | One product: every attribute, its confidence, and what is missing |
export_rows | no | The catalogue as export-ready rows, mapped through the schema's preset |
batch_status | no | Per-file progress of a batch run |
Connect it to an assistant
Mint the key in the app first: Settings → API keys. Choose Read only when the assistant should answer questions but never change anything — the endpoint then lists only the read tools and refuses the rest by name, so a careless prompt cannot write. Full access behaves as before.
Every client needs the same three facts, and nothing in the handshake is vendor-specific:
| The address | https://your-install/mcp |
| The key | header Authorization: Bearer apk_xxxx |
| The transport | MCP over streamable HTTP, stateless |
Claude — one command, or the same URL and header as a custom connector in the desktop and web apps:
claude mcp add --transport http specta https://your-install/mcp \
--header "Authorization: Bearer apk_xxxx"
ChatGPT and the OpenAI API — one entry in the Responses API's tools array (in ChatGPT itself, the same URL and key go in as a connector):
{
"type": "mcp",
"server_label": "specta",
"server_url": "https://your-install/mcp",
"authorization": "apk_xxxx",
"require_approval": "never"
}
Own Your AI reads a list of servers in this shape:
{
"mcpServers": [
{ "id": "specta", "name": "Specta", "url": "https://your-install/mcp",
"token": "apk_xxxx", "enabled": true }
]
}
**Every other client spells the same three facts differently — copy the shape from its own documentation, not from here.** VS Code is the clearest example of why: its configuration reference (read 6 September 2026) puts servers in .vscode/mcp.json under a "servers" object — *"an object that maps server names to their configurations"* — not an mcpServers array. Pasted as-is, the block above will not load there. The id, the URL and the token are what travel; the JSON around them belongs to whichever client you are configuring.
A local model, n8n, or your own code — n8n's MCP Client node takes the URL and the same Authorization: Bearer header; a model running on your own machine reaches it through any MCP client, so nothing leaves your network at all. Writing it yourself is one POST of JSON-RPC 2.0:
curl -X POST https://your-install/mcp \
-H "Authorization: Bearer apk_xxxx" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Any client that speaks MCP's streamable-HTTP transport works, including ones that do not exist yet: Specta implements the protocol, not an integration with a particular vendor.
No tool can start an extraction
This is the refusal the endpoint is designed around, and it is deliberate rather than missing.
An extraction run calls your AI provider on your key. An agent that could trigger one could spend real money without a person deciding to — a retry loop, an over-eager assistant re-running a catalogue "to be helpful", or simply a misunderstanding. Starting a run is a decision with a price attached, so it stays a button in the browser.
batch_status is read-only on purpose: an agent can watch a run it did not start, report on it and tell you when it finishes. Every tool in the table above is declared readOnly.
Completeness is first-class over the API
catalog_detail returns the same roll-up the Completeness screen shows:
"completeness": {
"total": 120, "complete": 96, "incomplete": 24,
"required_columns": ["Product name", "SKU", "Width"],
"missing_by_column": { "width": {"label": "Width", "missing": 18} }
}
list_items with incomplete_only: true returns only the products that are not ready, each with a missing_required list. That is the shape an assistant needs to be useful here: "these 24 items are missing a width, and here they are."
A value of 0 counts as supplied. Only empty and whitespace-only values are treated as missing — a required column with a legitimate zero is not a gap.
Roles
| Role | Can |
|---|---|
viewer | Read and export catalogues, items and completeness |
member | …plus upload, run extractions, correct products, edit schemas |
admin | …plus settings, branding, restore, the team |
An API key can do exactly what its owner can do. Because no tool starts a run, a member's key and a viewer's key differ here only in what they may read and export.
Spec sheet (PDF)
GET /products/{id}/spec.pdf
One page per item: the normalised attributes, your logo and accent. Required attributes that are not specified are listed as not specified rather than omitted, so the gap is visible to whoever reads the sheet.
Completeness export
GET /catalogs/{id}/incomplete.csv
Every product missing a required attribute, with the list of what it is missing — the working file for whoever is filling the gaps.
Scheduled backup
GET /backup/run?t=<token>
Mint the token in Backup & restore. Fourteen files are kept and older ones pruned. Both JSON backups — the scheduled one and the download — strip the same credentials: your AI provider key, SMTP and OIDC secrets, and every password and API-key hash. The .sqlite download is the whole install.