Skip to content

Completions and data extraction

POST /v1/ai/complete runs a masked, routed, metered completion with optional context blocks, JSON mode and automatic fact extraction.

POST /v1/ai/complete is the native Subsidia completion endpoint. It goes through the same pipeline as the Gateway (PII shield, routing by task complexity, metering against your question allowance) but takes a simpler body, and adds two things the OpenAI dialect has no place for:

  • Supplementary context: structured data blocks (a customer record, a price list) that are rendered into the system prompt in a consistent, prioritised format.
  • Automatic extraction: after the answer, a second light call pulls typed facts out of the user message and stores them, so a chat turns into structured records as a side effect.

Use the Gateway when you want drop-in compatibility with an existing SDK. Use this endpoint when you are writing new code and want context injection, JSON output or extraction in one call.

POST/v1/ai/complete

Run a completion, optionally with context blocks, JSON output and fact extraction.

API key (Bearer or x-api-key)engine

Runs one chat completion. Personal data is masked before an external provider sees it and restored in the answer; detected-sensitive requests are kept on a local model. The call is metered against the workspace allowance of questions.

The answer comes back whole (no streaming). For streaming, use the Gateway.

Request body

  • systemPromptstringrequired
    The instructions. Must not be empty.
  • messagesobject[]required
    The conversation so far. Must be a non-empty array; the last user message drives routing and extraction.
  • supplementaryobject[]
    Structured context appended to the system prompt under a SUPPLEMENTARY CONTEXT heading. Items are sorted by priority, high first; each becomes a section titled by its label or category.
  • extractionobject
    Extract typed facts from the last user message, after the answer. Extraction cost is a system cost and is not added to the question count of the call.
  • jsonboolean
    Ask for a JSON object as the answer. For models without native JSON mode, an instruction is appended to the system prompt. Still validate what you receive.
  • max_tokensnumber
    Output ceiling.
  • temperaturenumber
    Sampling temperature. Lower it (0 to 0.2) for extraction and classification.

Request examples

curl https://api.subsidia.protypa.fr/v1/ai/complete \
-H "Authorization: Bearer $SUBSIDIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"systemPrompt": "You are a support assistant. Answer briefly.",
"messages": [
{"role": "user", "content": "Hi, I am Marie Lambert, please call me on 06 12 34 56 78 after 5pm."}
],
"supplementary": [
{"category": "customer", "label": "Account", "priority": "high", "data": {"plan": "Pro", "since": "2024-03"}}
],
"extraction": {"source": "ticket-4812", "categories": ["contact", "preferences"]},
"temperature": 0.2
}'

Responses

content is the answer with personal data restored. usage is the token usage of the main call. extracted lists the facts found in the user message (empty when extraction was not requested, or nothing matched). The body also carries diagnostic fields about routing and latency; do not depend on them.

Example response
{
"content": "Thanks Marie, we will call you after 5pm.",
"usage": { "promptTokens": 142, "completionTokens": 14, "totalTokens": 156 },
"extracted": [
{ "category": "contact", "key": "phone", "value": "06 12 34 56 78", "confidence": 0.97 },
{ "category": "preferences", "key": "call_window", "value": "after 5pm", "confidence": 0.9 }
]
}

Errors

  • 400Invalid body, see above.
  • 401Missing or invalid key.
  • 402Workspace allowance exhausted.
  • 402API_KEY_BUDGET_EXCEEDEDThe key reached its own monthly question ceiling.
  • 403scope_deniedThe key lacks the engine scope.
  • 429rate_limitedMore than 30 requests per minute (also the per-key limit when one is set).
  • 500Provider or internal failure.

Use cases

JSON output for a pipeline

Set json: true and describe the shape in systemPrompt. Keep the temperature low and parse defensively: a model can still return something that is not the shape you asked for, so validate before you trust it.

Classify an incoming email into a typed object
type Triage = { category: 'invoice' | 'contract' | 'other'; urgent: boolean; summary: string }
async function triage(emailText: string): Promise<Triage | null> {
const res = await fetch('https://api.subsidia.protypa.fr/v1/ai/complete', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.SUBSIDIA_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
systemPrompt:
'Classify the email. Reply with a JSON object: ' +
'{"category": "invoice" | "contract" | "other", "urgent": boolean, "summary": string (max 20 words)}.',
messages: [{ role: 'user', content: emailText }],
json: true,
temperature: 0,
max_tokens: 300,
}),
})
const { content } = await res.json()
try {
const parsed = JSON.parse(content)
const ok = ['invoice', 'contract', 'other'].includes(parsed.category) && typeof parsed.urgent === 'boolean'
return ok ? (parsed as Triage) : null
} catch {
return null // send to a human instead of guessing
}
}

Fact extraction as a side effect of a chat

With extraction, each call that includes a customer message also leaves behind structured facts, grouped by your source label and one of your categories. Facts are upserted on the combination of source, category and key, so a customer who corrects their phone number updates the record rather than adding a second one. Read them back with the routes below.

Extracted data

GET/v1/ai/extracted-data

List the facts extracted by your completions.

API keyengine

Newest first. Scoped to the user the key belongs to, not to the whole workspace. Filter by source to retrieve everything gathered for one customer or one ticket.

Query parameters

  • sourcestring
    Only facts stored under this extraction.source.
  • categorystring
    Only this category.
  • limitnumberdefault 100
    Maximum number of facts, capped at 500.

Request examples

curl "https://api.subsidia.protypa.fr/v1/ai/extracted-data?source=ticket-4812&category=contact" \
-H "Authorization: Bearer $SUBSIDIA_API_KEY"

Responses

The facts and their number.

Example response
{
"data": [
{
"id": "clx9m1zq20003xyz",
"source": "ticket-4812",
"category": "contact",
"key": "phone",
"value": "06 12 34 56 78",
"confidence": 0.97,
"createdAt": "2026-10-09T09:31:10.000Z",
"updatedAt": "2026-10-09T09:31:10.000Z"
}
],
"count": 1
}

GET/v1/ai/extracted-data/:id

Get one extracted fact.

API keyengine

Path parameters

  • idstringrequired
    Fact id.

Request examples

curl https://api.subsidia.protypa.fr/v1/ai/extracted-data/$FACT_ID -H "Authorization: Bearer $SUBSIDIA_API_KEY"

Responses

The fact.

Example response
{ "data": { "id": "clx9m1zq20003xyz", "source": "ticket-4812", "category": "contact", "key": "phone", "value": "06 12 34 56 78", "confidence": 0.97 } }

Errors

  • 404No such fact for this user.

DELETE/v1/ai/extracted-data/:id

Delete one extracted fact.

API keyengine

Use it to honour an erasure request about a person whose details you extracted: delete by source listing, fact by fact.

Path parameters

  • idstringrequired
    Fact id.

Request examples

curl -X DELETE https://api.subsidia.protypa.fr/v1/ai/extracted-data/$FACT_ID -H "Authorization: Bearer $SUBSIDIA_API_KEY"

Responses

Deleted. No body.

Errors

  • 403read_only_keyThe key is read-only.

Tips

GoalAdvice
Reliable extractionKeep categories short and meaningful; anything outside them is discarded. Use a stable source so repeated mentions update the same record.
Reliable JSONDescribe the exact shape, set temperature to 0, validate, and have a fallback path to a person.
Context from your systemsPut records in supplementary rather than pasting them into the user message: they are labelled, ordered by priority and kept apart from the question.
Personal dataMasking applies to external providers and is restored in content. Persisted facts hold the values people gave you (here, a phone number): treat the extracted-data store as personal data and delete on request.
Grounding in documentsThis endpoint does not retrieve from your knowledge base. Retrieve with /v1/knowledge/query and pass the passages in supplementary, or use Light.

Related