Dexa APIv0.5.0

Task types

How decider and extractor tasks are shaped, named, and answered.

Every task is one entry in the tasks array and is one of two kinds, chosen by its type field. The field is the discriminator: the API routes on it, and the generated clients type it as a union of DecideTask and ExtractTask.

Decider#

A decider answers one question with one short value.

json{
  "type": "decide",
  "name": "should_renew",
  "question": "Should we offer a renewal discount?",
  "options": ["yes", "no"]
}
Field Required Notes
type yes Always decide
name yes Key this result appears under. [a-zA-Z0-9_][a-zA-Z0-9_.-]{0,63}
question yes The question to answer
options no Allowed answers. Constrains the output to one of them
choices no Alias of options
instruction no Extra guidance for this one decision
target_language no Overrides the request language for this task
id no Legacy alias of name

Answered as:

json{ "should_renew": { "value": "yes" } }

A reason may be present when the model explains itself:

json{ "should_renew": { "value": "yes", "reason": "Two-year term and a three-month lapse" } }

reason costs completion tokens. Leave it out of the request if you do not read it, and lower max_output_tokens accordingly.

Extractor#

An extractor pulls fields out of the context.

json{
  "type": "extract",
  "name": "renew_term",
  "instruction": "Extract the renewal term in months",
  "keys": ["months"]
}
Field Required Notes
type yes Always extract
name yes Key this result appears under. Same pattern as name above
instruction yes What to extract and how to phrase it
keys no Expected field names
fields no Alias of keys
schema no A JSON-Schema-like object describing the shape
target_language no Overrides the request language for this task
id no Legacy alias of name

Answered as:

json{ "renew_term": { "months": 24 } }

Flat versus shaped#

keys is the fast path, for flat objects:

json{ "type": "extract", "name": "customer", "instruction": "Extract the customer details", "keys": ["name", "plan"] }
json{ "customer": { "name": "Ada Lovelace", "plan": "enterprise" } }

Use schema when the result is nested or typed:

json{
  "type": "extract",
  "name": "invoice",
  "instruction": "Extract the invoice",
  "schema": {
    "type": "object",
    "properties": {
      "total": { "type": "number" },
      "currency": { "type": "string" },
      "lines": { "type": "array", "items": { "type": "object" } }
    },
    "required": ["total", "currency"]
  }
}

schema is passed through as-is, so anything expressible in JSON Schema works.

Naming#

name is the key in results, so choose names your code can read out loud:

json{ "name": "should_renew" }
json{ "results": { "should_renew": { "value": "yes" } } }

The pattern is [a-zA-Z0-9_][a-zA-Z0-9_.-]{0,63}: letters, digits, underscore, dot and hyphen, up to 64 characters, and it cannot start with a dot or a hyphen. Names must be unique within a request; duplicates overwrite each other in results.

Warning

Naming a field id used to be the only way. It still works, but name is canonical. Pick one and stay with it, or results will be keyed by whichever the model preferred.

Deciders and extractors together#

Tasks of both kinds go in one array and are answered together, in one call:

json{
  "model": "decider-extractor-v1",
  "context": "Ada upgraded to enterprise on 3 March. Invoice INV-8842 totals 4200 EUR.",
  "tasks": [
    { "type": "decide", "name": "is_churn_risk", "question": "Is there churn risk?", "options": ["yes", "no"] },
    { "type": "extract", "name": "invoice_total", "instruction": "Extract the invoice total", "keys": ["amount", "currency"] }
  ]
}
json{
  "results": {
    "is_churn_risk": { "value": "no" },
    "invoice_total": { "amount": 4200, "currency": "EUR" }
  }
}

This is the main reason the API exists: both tasks need the same context, so pay to read it once.