Developer Dashboard

Extraction API reference ​

The Extraction API reads supplied evidence and returns named, typed values. With Cloudflare's Clairic, a request can extract a verbatim identifier, compute a date or amount, collect a list, estimate an unknown quantity, or forecast a series. Each result retains the model's uncertainty signals where available. Your application validates the values before saving them or acting on them.

Use cf.clairic on https://api.avalai.ir/v1/extraction. This is a separate schema from Decisions and SystemOne: Extraction uses state and fields, not input, questions, choices, or criteria. The Clef decision models belong to those decision endpoints, rather than this route.

Verification boundary, 2026-10-24: this reference combines Cloudflare's current official schemas with AvalAI's current catalog and endpoint source. The requests and responses below are documentation examples; no live AvalAI Extraction response was captured for this page. Optional media behavior, all field types, large requests, and prediction quality need checks against the selected AvalAI route before application use. See the practical guide.

Endpoint, authentication, and model ​

text
POST https://api.avalai.ir/v1/extraction

Send UTF-8 JSON with Content-Type: application/json and Authorization: Bearer …. Read the AvalAI key from AVALAI_API_KEY on the server. A Cloudflare account or Cloudflare API key is not required for the caller. See authentication.

AvalAI model IDCategoryMinimum tierInput / cached input / output, USD per 1 million tokens
cf.clairicExtraction0$0.10 / $0.00 / $0.00

The current AvalAI catalog lists a 65,536-token input window and image support for this model. Cloudflare describes Clairic as a preview model. Its schema separately describes splitting field definitions across 8,192-token passes; this is an internal processing description, not a second interchangeable public input limit. Do not infer how much long text is retained from the catalog window alone. Inspect truncated, keep evidence bounded, and check the sizes required by your application.

The source route accepts only cf.clairic for Extraction. cf.clef, cf.clef-flash, and cf.clef-omni are decision models for /v1/decisions and /v1/systemone. These four models are not conversation models for /v1/chat/completions or /v1/responses. See Cloudflare, models, and pricing.

Request body ​

FieldTypeRequired or defaultMeaning
modelstringRequiredUse the AvalAI ID cf.clairic. AvalAI selects the upstream route; do not copy clairic or @cf/cloudflare/clairic into this field.
statestringNonblank text required unless images are suppliedEvidence to read. An object or array is not accepted as native state on this endpoint; serialize relevant records into text. Cloudflare's schema defaults to "" for image-only requests.
fieldsobject, or AvalAI shorthand/list formRequired, at least one fieldNamed typed definitions, described below. Prefer the full object form.
imagesarrayOptional, maximum 4Embedded images, described under media inputs. Remote URLs are not accepted.
reference_datestringOptional; no default specifiedISO date anchoring dates written without a year, such as 2026-09-02. Provide it when that ambiguity matters.
optionsobjectOptionalinclude_quantiles and return_state_text, described below.

Streaming is not supported. Do not send stream: true. The route forwards state, fields, images, reference_date, and options; other provider settings are not a supported way to control inference. Top-level fields omitted or set to null are not forwarded. A null field definition is invalid; it does not request an absent answer.

Field names and forms ​

A field name is 1–64 characters, starts with an ASCII letter or _, and then contains only ASCII letters, digits, _, ., or -. The exact pattern is ^[A-Za-z_][A-Za-z0-9_.\-]{0,63}$. Cloudflare's schema sets no field-count upper limit, but large schemas still consume input and may require multiple passes. Give fields distinct names; avoid duplicate name values in list form because conversion to an object cannot retain duplicate keys.

These three definitions express the same field through AvalAI:

json
{
  "fields": {
    "invoice_number": {
      "type": "text"
    }
  }
}
json
{
  "fields": {
    "invoice_number": "text"
  }
}
json
{
  "fields": [
    {
      "name": "invoice_number",
      "type": "text"
    }
  ]
}

AvalAI expands shorthand and list entries into Cloudflare's name-keyed object form. The full object form makes nested settings explicit and avoids relying on that convenience conversion.

All nine field types ​

Every full field definition requires type. instructions is optional text describing the intended value. State the source, role, and desired calculation when a name alone is ambiguous.

typePurposeSettings and interpretation
textExtract a string from the evidenceText-state answers can include span offsets and a verbatim guess. Check value before accepting that guess.
numberExtract or compute a numeric valueOptional unit, integer, and decimals. Validate finiteness, range, currency, and exact arithmetic in code.
dateExtract or compute a calendar datereference_date anchors dates without a year. Cloudflare's example returns an ISO date string; validate the calendar date.
timeRequest a time valueA supported field type in the schema. The published output schema does not specify a time-string format or timezone rule; validate your required representation.
datetimeRequest a combined date and timeA supported field type. The output schema does not establish timezone, offset, or ambiguity resolution; define and verify these for your application.
durationRequest an elapsed quantityOptional duration unit, integer, and decimals. Keep duration separate from a timestamp; calendar months and years need an explicit application policy.
listReturn several valuesRequires items: "text", "number", or "date". Number lists can use decimals. Text lists can include per-item spans. Validate every element.
estimatePredict an uncertain quantity within a gridRequires min and max; optional step and scale. Read the distribution summaries and tails rather than treating the estimate as an observed fact.
forecastPredict several future stepsUses estimate bounds/grid plus horizon and interval. Results can contain steps; inspect each step and preserve the time alignment chosen by your application.

Nested field settings ​

The official input schema defines these properties. AvalAI validates basic field names/types locally and leaves provider-specific settings to upstream validation; passing local validation does not establish that a particular combination is valid.

SettingSchema and defaultApplies to / meaning
instructionsstring; no default specifiedDescription of the value to extract, compute, or predict.
unitstringLabel for a quantity. For duration, use seconds, minutes, hours, days, weeks, months, or years; default seconds.
integerboolean; no default specifiednumber and duration.
decimalsinteger 0–6; no default specifiednumber, duration, and number lists. Requested precision is not proof of correct rounding or source precision.
itemstext, number, or date; required for listsType of each list element; arbitrary nested JSON objects are not a documented list type.
min, maxRequired for estimate and forecastNumbers, or ISO dates for date estimates. Bounds describe a prediction grid, not guaranteed outcomes.
stepPositive number, or month / year; no default specifiedNumeric grid spacing; a numeric step means days for date estimates.
scalelinear or log; default linearEstimate-grid scale. Let upstream validate the bounds required for a logarithmic grid.
null_thresholdnumber greater than 0 and at most 1; default 0.5Probability of null above which value becomes null. Lowering it changes abstention behavior; it does not by itself establish better accuracy.
horizoninteger 1–64; no default specifiedForecast step count. Supply it for a forecast rather than guessing an omitted default.
intervalstep, minute, hour, day, week, month, quarter, or year; no default specifiedForecast interval. Supply it and retain the reference timing in your application.

For example, these are field definitions, not a complete request or a captured output:

json
{
  "meeting_time": {
    "type": "time",
    "instructions": "Start time explicitly stated in the note"
  },
  "appointment": {
    "type": "datetime",
    "instructions": "Appointment date and time explicitly stated in the note"
  },
  "elapsed": {
    "type": "duration",
    "unit": "minutes",
    "integer": true
  },
  "quantities": {
    "type": "list",
    "items": "number",
    "decimals": 2
  },
  "arrival": {
    "type": "estimate",
    "min": "2026-10-01",
    "max": "2026-10-31",
    "step": 1
  },
  "daily_demand": {
    "type": "forecast",
    "min": 0,
    "max": 100,
    "step": 1,
    "horizon": 3,
    "interval": "day"
  }
}

Options ​

Property inside optionsType / defaultEffect
include_quantilesboolean / falseAdds 99 percentiles to estimates through quantiles. The output schema does not define their exact element shape; inspect the returned data.
return_state_textboolean / falseReturns state_text, the text that answer spans index into. Useful when verifying source spans.

Omit options you do not need. The public schema does not define generative controls such as temperature, a custom JSON Schema, or tool calls for this endpoint.

Media inputs ​

Cloudflare documents embedded PNG, JPEG, or WebP, at most 4 images, 4 MiB per image, 8 MiB total decoded image bytes, and a 16 MiB whole request body. These are upstream limits, not live-tested AvalAI proxy limits. Encoding expands the request; decoded byte limits and whole-body limits are different checks. The AvalAI source validates the count and basic embedded shape; upstream validates the actual image content and other media constraints.

Use a lowercase data: URL or an object with both content_type and base64. AvalAI's current local check requires the lowercase prefix even though Cloudflare's raw schema accepts case variations. An image-only request may omit state or use ""; text-only state must contain nonwhitespace text. This route does not document audio, video, remote image URLs, PDF uploads, or file_id inputs.

json
{
  "images": [
    {
      "content_type": "image/png",
      "base64": "<BASE64_ENCODED_IMAGE_BYTES>"
    }
  ]
}

The placeholder is a shape illustration, not valid image bytes for a runnable request. Verify media extraction and span behavior separately; do not assume text offsets identify pixel locations.

Complete invoice request ​

The three clients send the same six-field payload. Python uses the standard library; JavaScript requires server-side Node.js 18 or later. Each makes one attempt, with a bounded timeout and no automatic retry. Keep the input synthetic while checking compatibility. Save the Python or JavaScript block as a file and run it with AVALAI_API_KEY already available in the environment.

bash
curl --fail-with-body \
  --connect-timeout 10 --max-time 120 \
  https://api.avalai.ir/v1/extraction \
  -H "Authorization: Bearer $AVALAI_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary '{
    "model": "cf.clairic",
    "state": "Invoice INV-2291 from Northwind Supply, issued 2026-09-02, net 30. 4 pallets at $250.00 each, shipping $80.00. Contact: Dana Ruiz, Priya Shah.",
    "fields": {
      "invoice_number": {"type": "text"},
      "due_date": {"type": "date", "instructions": "Issue date plus the net payment days"},
      "total": {"type": "number", "decimals": 2, "unit": "USD", "instructions": "Amount due including shipping"},
      "contacts": {"type": "list", "items": "text"},
      "po_number": {"type": "text", "instructions": "Purchase order number"},
      "days_to_pay": {"type": "estimate", "min": 0, "max": 120, "step": 1, "instructions": "Days after issue until this invoice is paid"}
    }
  }'
python
import json
import os
from urllib.error import HTTPError
from urllib.request import Request, urlopen

payload = {
    "model": "cf.clairic",
    "state": "Invoice INV-2291 from Northwind Supply, issued 2026-09-02, net 30. 4 pallets at $250.00 each, shipping $80.00. Contact: Dana Ruiz, Priya Shah.",
    "fields": {
        "invoice_number": {"type": "text"},
        "due_date": {
            "type": "date",
            "instructions": "Issue date plus the net payment days",
        },
        "total": {
            "type": "number",
            "decimals": 2,
            "unit": "USD",
            "instructions": "Amount due including shipping",
        },
        "contacts": {"type": "list", "items": "text"},
        "po_number": {"type": "text", "instructions": "Purchase order number"},
        "days_to_pay": {
            "type": "estimate",
            "min": 0,
            "max": 120,
            "step": 1,
            "instructions": "Days after issue until this invoice is paid",
        },
    },
}
request = Request(
    "https://api.avalai.ir/v1/extraction",
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {os.environ['AVALAI_API_KEY']}",
        "Content-Type": "application/json",
    },
    method="POST",
)
try:
    with urlopen(request, timeout=120) as response:
        result = json.load(response)
        print("avalai-request-id:", response.headers.get("avalai-request-id"))
except HTTPError as error:
    print("avalai-request-id:", error.headers.get("avalai-request-id"))
    print(f"HTTP {error.code}: {error.read().decode('utf-8', errors='replace')}")
    raise
print(json.dumps(result, indent=2, ensure_ascii=False))
javascript
const apiKey = process.env.AVALAI_API_KEY;
if (!apiKey) throw new Error("AVALAI_API_KEY is required");

const payload = {
  model: "cf.clairic",
  state: "Invoice INV-2291 from Northwind Supply, issued 2026-09-02, net 30. 4 pallets at $250.00 each, shipping $80.00. Contact: Dana Ruiz, Priya Shah.",
  fields: {
    invoice_number: { type: "text" },
    due_date: { type: "date", instructions: "Issue date plus the net payment days" },
    total: { type: "number", decimals: 2, unit: "USD", instructions: "Amount due including shipping" },
    contacts: { type: "list", items: "text" },
    po_number: { type: "text", instructions: "Purchase order number" },
    days_to_pay: { type: "estimate", min: 0, max: 120, step: 1, instructions: "Days after issue until this invoice is paid" },
  },
};
const response = await fetch("https://api.avalai.ir/v1/extraction", {
  method: "POST",
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
  body: JSON.stringify(payload),
  signal: AbortSignal.timeout(120_000),
});
const text = await response.text();
console.log("avalai-request-id:", response.headers.get("avalai-request-id"));
if (!response.ok) throw new Error(`HTTP ${response.status}: ${text}`);
console.log(JSON.stringify(JSON.parse(text), null, 2));

The Python socket timeout does not impose a strict wall-clock deadline across every response read. Use a client with a total-operation timeout if your application needs one. These examples print synthetic response bodies for inspection; redact sensitive document contents and identifiers before sharing real error output.

Success response ​

The current source unwraps Cloudflare's result object, identifies the public model as cf.clairic, and adds AvalAI's estimated_cost when a cost estimate is available. It does not return Cloudflare's surrounding success / errors wrapper as the success envelope.

FieldTypeMeaning
modelstringAvalAI model ID; an echoed ID does not reveal a resolved model revision.
answersobjectOne named answer per requested field in the upstream contract. Each answer requires type and value.
truncatedbooleanWhether evidence was truncated. Treat truncation as material when omitted text can change the answer.
state_textstring, optionalIndexed text when return_state_text is enabled.
usageobjectRequired input_tokens and output_tokens; optional state_tokens, image_tokens, schema_tokens, and passes.
estimated_costobject, optionalAvalAI USD and local-currency estimate, described below.

Illustrative complete response adapted from Cloudflare's invoice example and AvalAI's source envelope. It is not a live AvalAI capture. Numeric values, usage, passes, and the presence of optional properties can differ. irt and exchange_rate are deliberately null because this example asserts no current exchange rate:

json
{
  "model": "cf.clairic",
  "answers": {
    "invoice_number": {
      "type": "text",
      "p_null": 3.8e-10,
      "span": [
        8,
        16
      ],
      "guess": "INV-2291",
      "confidence": 1,
      "value": "INV-2291"
    },
    "due_date": {
      "type": "date",
      "p_null": 1.9e-08,
      "guess": "2026-10-02",
      "confidence": 0.9999,
      "value": "2026-10-02"
    },
    "total": {
      "type": "number",
      "p_null": 2.3e-08,
      "guess": 1080,
      "confidence": 0.9999,
      "value": 1080
    },
    "contacts": {
      "type": "list",
      "value": [
        "Dana Ruiz",
        "Priya Shah"
      ],
      "spans": [
        [
          120,
          129
        ],
        [
          131,
          141
        ]
      ],
      "confidence": 0.9999
    },
    "po_number": {
      "type": "text",
      "p_null": 1,
      "span": [
        8,
        16
      ],
      "guess": "INV-2291",
      "confidence": 0.9995,
      "value": null
    },
    "days_to_pay": {
      "type": "estimate",
      "value": 30,
      "mean": 29.535,
      "median": 30,
      "p10": 21,
      "p90": 38,
      "p_below": 5.9e-06,
      "p_above": 1.2e-05
    }
  },
  "truncated": false,
  "usage": {
    "state_tokens": 61,
    "image_tokens": 0,
    "schema_tokens": 472,
    "input_tokens": 655,
    "output_tokens": 0,
    "passes": 3
  },
  "estimated_cost": {
    "unit": "0.0000655000",
    "irt": null,
    "exchange_rate": null
  }
}

Values, uncertainty, and spans ​

Read value first. value: null means the model did not establish an accepted value. guess may still contain a candidate, with a span and high confidence; those properties do not override null. The missing purchase-order example deliberately guesses the invoice number while returning value: null and p_null: 1. Saving guess as a purchase-order number would create a false record.

p_null estimates whether no answer is supported; null_threshold controls the provider's null decision. confidence is a separate returned signal. The public output schema does not define a formula connecting them or establish application calibration. Do not multiply them, replace null with a guess, or treat a missing confidence property as zero. Lists and estimates need not have the same metadata as text fields.

span is an array of integer offsets; text lists may use spans. Cloudflare's example uses start/end positions to locate substrings. Retain the exact indexed text and verify the bounds and substring before using spans as provenance. Character indexing can differ between Python and JavaScript for Unicode; establish the provider's convention on non-ASCII inputs instead of treating offsets as byte positions. Computed dates/numbers and predictions may have no verbatim source span.

Estimates and forecasts ​

An estimate can return value, mean, median, p10, p90, p_below, p_above, and optional quantiles. The official example describes p10 to p90 as an 80% interval. p_below and p_above retain tail probability outside the requested grid. Bounds do not certify that the actual result will fall inside them, and a narrow interval does not demonstrate accuracy.

A forecast can return steps, described as one estimate per step. The generic output schema does not prescribe the full nested step layout or a top-level forecast value format. Preserve the returned structure, validate it against your chosen application representation, and attach the reference time and interval from your request. A forecast needs evaluation against later observations; it is not a list of extracted facts.

Usage and billing ​

AvalAI's current source charges reported input_tokens × USD 0.10 / 1,000,000 for cf.clairic. Output pricing is zero. Input may include schema, media, and repeated processing passes; do not estimate cost from text-state length alone or assume state_tokens + image_tokens + schema_tokens always equals billed input_tokens.

In the illustration, 655 × 0.10 / 1,000,000 = USD 0.0000655, matching estimated_cost.unit. unit is the USD amount as a decimal string, irt is the toman estimate, and exchange_rate is the conversion rate used for that response. The last two may be null; do not substitute a fixed rate. A missing estimated_cost is not proof of a free request. See the cost-tracking guide for the distinction between response estimates and transaction accounting.

The catalog's zero cached-input price does not establish cache controls, hits, or lifetime for Extraction. passes describes processing passes, not a request retry count. Keep usage for each response; a retried inference can incur additional input charges.

Errors and retries ​

Extraction validation and upstream failures use an error object. Local validation can identify a param; upstream errors may omit it. This is an illustrative local validation response for stream: true, not a live receipt:

json
{
  "error": {
    "message": "Streaming is not supported on /v1/extraction.",
    "type": "invalid_request_error",
    "code": "invalid_request",
    "param": "stream",
    "request_id": "<AVALAI_REQUEST_ID>"
  }
}
Status / symptomCurrent contract and useful response
400Local unsupported model, streaming, empty/invalid fields, invalid state, or invalid embedded image shape; some upstream schema errors also use 400. Correct the request before repeating it.
401 / 403 from AvalAI admissionInspect your AvalAI authentication, permissions, and tier. A provider credential failure is separately mapped to 503; it is not a request for a Cloudflare key from the caller.
413An upstream oversized request with validation detail can retain this status. Reduce the payload; do not retry unchanged bytes.
422Provider-specific settings such as estimate bounds can fail upstream validation. Inspect the sanitized message and correct the field definition.
429Provider rate limiting maps to rate_limit_exceeded; AvalAI admission may also enforce rate or quota limits. Inspect the body before retrying.
503Provider transport/timeout, provider authentication failure, unavailable provider, non-JSON result, or a result without answers. A bounded retry may be appropriate for temporary failure.

The source sanitizes upstream validation messages and replaces provider credential/server failures with a generic unavailable response. Preserve the HTTP status, model ID, redacted error body, and avalai-request-id response header when present. See response headers and error handling.

For application retries, set a finite total attempt count and overall deadline, use bounded backoff, and honor returned retry delays when present. Stop for quota_exceeded or insufficient_quota, invalid requests, invalid successful-response JSON, or incompatible answer schemas. A timeout can happen after processing and billing; no Extraction idempotency-key contract is established here. Keep database writes, notifications, and payments outside the inference retry loop.

Sources and next steps ​