Extract structured data

Attach a schema to an inbox and incoming email arrives as validated, typed fields instead of raw text.

Attach a schema to an inbox and every incoming email arrives with the fields you asked for — an order id, a total, a tracking number — already extracted and validated. No parsing, no prompts, no regex on your side.

Attach a schema

PUT /v1/inboxes/{inbox}/schema attaches or replaces the extraction contract, and needs the inboxes:write scope. Address the inbox by id or by its full address.

curl -X PUT https://api.dairo.app/v1/inboxes/orders@yourapp.com/schema \
  -H "Authorization: Bearer $DAIRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "schema": {
          "orderId": { "type": "string", "required": true },
          "total": { "type": "number" }
        },
        "onValidationError": "quarantine",
        "extractionHint": "Pull the order ID and total from the confirmation."
      }'
{
  "object": "inbox_schema",
  "inboxId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "schema": {
    "orderId": { "type": "string", "required": true },
    "total": { "type": "number" }
  },
  "onValidationError": "quarantine",
  "extractionHint": "Pull the order ID and total from the confirmation.",
  "createdAt": "2026-06-12T10:00:00+00:00",
  "updatedAt": "2026-06-12T10:00:00+00:00"
}

Schema shape

The schema is a flat map of field name to declaration. A standard JSON Schema also works — { "type": "object", "properties": { … }, "required": [ … ] } is folded into the same shape on write.

  • Types: string, number, integer, boolean, url, and email — the last two validate their format. object and array declare nested values.
  • Per-field keys: required, default, enum, and maxLength, applied when an incoming email is validated against the schema.
  • Size: the whole schema must stay within 8 KiB. An empty schema ({}, or omitted) means no contract — mail passes through unshaped.
  • extractionHint: an optional plain-English pointer for the extractor, up to 1,000 characters. It is prompt context only, never executed. A PUT replaces the whole contract, so include the hint on every update you want to keep.

Where the data lands

Once a schema is attached, each matching email carries its results on the message record — extractionStatus for the outcome and structured for the extracted object:

{
  "object": "message",
  "subject": "Order #4821 confirmed",
  "extractionStatus": "validated",
  "structured": { "orderId": "4821", "total": 129.5 }
}

The message.received webhook event carries the same result in a structured block, so an event-driven consumer never has to fetch the body to get the fields.

Extracted values are copied from the email, never invented: a required field whose value doesn't actually appear in the message fails validation instead of being filled in with a plausible guess.

When an email doesn't fit

onValidationError decides what happens when an email can't satisfy the schema — say, a confirmation with no order total.

ValueWhat happens
quarantine (default)The message is stored but held out of your normal flow: no message.received event fires. A message.quarantined event fires instead, carrying the raw candidate and the validation error.
passthroughThe message is delivered normally, carrying the raw candidate plus the validation error.

The outcome for any message is readable on its extractionStatus:

StatusMeaning
validatedThe extraction matched the schema; structured holds the typed object.
quarantinedValidation failed under quarantine; the message was held aside.
passthroughValidation failed under passthrough; the message was delivered with the raw candidate.
skippedExtraction didn't run — the inbox had no contract.
errorExtraction was unavailable; the message was delivered normally, without structured data.

Read or remove the schema

Check what contract an inbox carries, or detach it to go back to plain, unshaped mail. Reading uses the inboxes:read scope; detaching uses inboxes:write. Reading an inbox that has no schema returns 404; detaching returns 204 with an empty body.

curl https://api.dairo.app/v1/inboxes/orders@yourapp.com/schema \
  -H "Authorization: Bearer $DAIRO_API_KEY"

curl -X DELETE https://api.dairo.app/v1/inboxes/orders@yourapp.com/schema \
  -H "Authorization: Bearer $DAIRO_API_KEY"

A structured inbox turns "parse the email" into "read the fields." Pair it with the message.received event: when a matching email arrives, the extracted data is already in the payload.

Next steps

On this page