Developer API
API reference

Conversions

Two endpoints do the whole job: one starts a conversion, one reads its result. Upload a PDF, get back a job_id, then poll that job until an ESX download link appears.

Start a conversion

POST /v1/conversions

Send the PDF as multipart/form-data. The response is immediate — a 202 with a job_id; the actual conversion runs in the background.

Body fields

FieldRequiredTypeNotes
fileYesfileThe estimate PDF. application/pdf content-type or a .pdf filename.
price_listYesstringXactimate price-list code, e.g. FLTA8X_02MAY26. Must be one of the currently-loaded lists.
output_formatsNostring[]Repeatable form field. Default ["esx"]. Add "json" for a structured JSON artifact alongside the ESX — see JSON output.
callback_urlNostringPer-request webhook override.

Headers

HeaderRequiredNotes
X-API-KeyYesSee Authentication.
Idempotency-KeyNoSame key + same partner within the retention window returns the existing job instead of creating a duplicate. Use it to make retries after a timeout safe.

Limits

  • Max upload size: 50 MB.
  • Only PDF is accepted — anything else returns invalid_file_type.

Example

curl
curl -s -X POST https://est.anterotrail.com/v1/conversions \
  -H "X-API-Key: at_test_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: my-job-2026-07-09-001" \
  -F "file=@estimate.pdf;type=application/pdf" \
  -F "price_list=FLTA8X_02MAY26"
202 Accepted
{ "job_id": "b3f1...", "status": "queued" }

Read a conversion

GET /v1/conversions/{job_id}

Returns the job that belongs to your partner account only. A job ID that isn't yours — or doesn't exist — returns 404 not_found. The two cases are never distinguished, so no one can probe for other partners' jobs.

While running

200 · processing
{
  "job_id": "b3f1...",
  "status": "processing",
  "created_at": "2026-07-09T12:00:00Z"
}

On success

200 · succeeded
{
  "job_id": "b3f1...",
  "status": "succeeded",
  "created_at": "2026-07-09T12:00:00Z",
  "summary": { "room_count": 14, "line_item_count": 132, "total_rcv": 48213.44, "warnings": [] },
  "esx_url": "https://.../job.esx?sig=...",
  "esx_expired": false,
  "artifacts": [
    { "type": "esx", "url": "https://.../job.esx?sig=...", "expired": false }
  ]
}
  • esx_url is a time-limited signed URL, generated fresh on every GET — valid 7 days from generation (not from job completion). Re-poll to refresh it.
  • artifacts lists every output the job produced, each with its own signed URL — one entry per requested output_format. Jobs requesting only ESX carry the single esx entry shown above; esx_url remains alongside it either way.
  • Once the artifact passes the 30-day retention window, esx_url is null and esx_expired is true. summary stays available indefinitely.

On failure

200 · failed
{
  "job_id": "b3f1...",
  "status": "failed",
  "created_at": "2026-07-09T12:00:00Z",
  "error": { "code": "unsupported_estimate", "message": "No line items could be read..." }
}
Polling

Poll on a reasonable interval — a few seconds. Most conversions finish in under a minute. A job stuck in processing past 20 minutes is force-failed with error.code: "timeout". To skip polling entirely, use a webhook.