Developer Guide

Quixyl API & Webhooks: Build a Document Processing Pipeline in an Afternoon

A developer-focused walkthrough of the Quixyl REST API and webhook system — upload documents, receive structured JSON, and trigger downstream actions automatically.

February 22, 2026 10 min read Quixyl Engineering Team api webhooks integration developer automation

If you need to process documents at volume, or plug Quixyl into an existing system, the REST API and webhook layer is the right entry point. This guide walks through authentication, the core extract endpoint, handling async jobs for large files, and wiring up webhooks to trigger downstream actions.

All examples use curl — the same patterns translate directly to any HTTP client.


Prerequisites

  • A Quixyl account on Pro or Enterprise (API access is not available on Free)
  • Your API key from Settings → API Keys
  • Basic familiarity with REST and JSON

Authentication

All API requests use Bearer token authentication:

curl -H "Authorization: Bearer $QUIXYL_API_KEY" \
     https://api.quixyl.com/v1/ping
{ "ok": true, "workspace": "acme-corp", "plan": "pro" }

API keys are workspace-scoped. Create separate keys for each integration so you can revoke them independently.

Rate limits by plan:

PlanRequests / minuteConcurrent extractions
Pro12010
Enterprise60050
CustomNegotiatedNegotiated

Synchronous extraction (small files)

For files under 10 MB and documents up to 10 pages, the synchronous endpoint returns results in the same HTTP response:

curl -X POST https://api.quixyl.com/v1/extract \
  -H "Authorization: Bearer $QUIXYL_API_KEY" \
  -F "file=@invoice.pdf"

Response (200 OK):

{
  "id": "ext_01HXYZ987K2P",
  "status": "complete",
  "document_type": "invoice",
  "confidence": 0.97,
  "data": {
    "vendor": "Meridian Logistics GmbH",
    "invoice_number": "ML-2026-3301",
    "invoice_date": "2026-02-18",
    "due_date": "2026-03-20",
    "currency": "EUR",
    "subtotal": 4200.00,
    "tax": 798.00,
    "total": 4998.00,
    "line_items": [
      { "description": "Freight - Frankfurt to Dublin", "qty": 1, "unit_price": 4200.00, "amount": 4200.00 }
    ]
  },
  "pages": 1,
  "processing_ms": 2140
}

The id field is your extraction record ID — use it to retrieve results later or to link to your internal records.


Asynchronous extraction (large or multi-page files)

For files over 10 MB or documents with more than 10 pages, submit the job and poll — or better, use webhooks (covered below).

Submit:

curl -X POST https://api.quixyl.com/v1/extract/async \
  -H "Authorization: Bearer $QUIXYL_API_KEY" \
  -F "file=@large-batch.pdf" \
  -F "notify_webhook=true"
{
  "id": "ext_01HXYZ112A7Q",
  "status": "queued",
  "estimated_seconds": 45
}

Poll for status:

curl https://api.quixyl.com/v1/extractions/ext_01HXYZ112A7Q \
  -H "Authorization: Bearer $QUIXYL_API_KEY"

Status transitions: queued → processing → complete | failed


Specifying document type

By default Quixyl auto-detects the document type. You can override this to skip detection and speed up processing:

curl -X POST https://api.quixyl.com/v1/extract \
  -H "Authorization: Bearer $QUIXYL_API_KEY" \
  -F "file=@receipt.jpg" \
  -F "document_type=receipt"

Supported values: invoice, receipt, purchase_order, delivery_note, bank_statement, contract, auto (default).


Webhooks

Polling is fine for low volume. For production pipelines, webhooks are better — Quixyl pushes a result to your endpoint the moment processing is complete.

Register a webhook endpoint

curl -X POST https://api.quixyl.com/v1/webhooks \
  -H "Authorization: Bearer $QUIXYL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/hooks/quixyl",
    "events": ["extraction.complete", "extraction.failed", "review.required"],
    "secret": "your-signing-secret"
  }'

Webhook payload

{
  "event": "extraction.complete",
  "timestamp": "2026-02-22T14:33:11Z",
  "extraction_id": "ext_01HXYZ987K2P",
  "document_type": "invoice",
  "confidence": 0.97,
  "data": { ... }
}

Verifying the signature

Every webhook request includes an X-Quixyl-Signature header — an HMAC-SHA256 of the raw request body using your signing secret. Always verify it:

import { createHmac, timingSafeEqual } from 'crypto';

function verifyWebhook(body: string, signature: string, secret: string): boolean {
  const expected = createHmac('sha256', secret).update(body).digest('hex');
  const expectedBuf = Buffer.from(`sha256=${expected}`);
  const signatureBuf = Buffer.from(signature);
  if (expectedBuf.length !== signatureBuf.length) return false;
  return timingSafeEqual(expectedBuf, signatureBuf);
}

Handling the review.required event

When a field confidence falls below your configured threshold, or validation rules fail, Quixyl fires review.required instead of extraction.complete. Your handler should:

  1. Store the partial extraction in your system
  2. Create a task for a human reviewer (Quixyl’s dashboard, your ticketing system, Slack, etc.)
  3. Call /v1/extractions/{id}/corrections with the corrected values once the human is done
curl -X PATCH https://api.quixyl.com/v1/extractions/ext_01HXYZ987K2P/corrections \
  -H "Authorization: Bearer $QUIXYL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "total": 4998.00, "due_date": "2026-03-20" }'

Quixyl logs the correction and retrains on it for your workspace.


Bulk upload

To submit a batch of files in one call:

curl -X POST https://api.quixyl.com/v1/extract/bulk \
  -H "Authorization: Bearer $QUIXYL_API_KEY" \
  -F "files[]=@inv001.pdf" \
  -F "files[]=@inv002.pdf" \
  -F "files[]=@inv003.pdf" \
  -F "notify_webhook=true"

Returns an array of job IDs. All results are delivered via webhook as each completes.


Listing and searching extractions

# Last 50 completed extractions
curl "https://api.quixyl.com/v1/extractions?status=complete&limit=50" \
  -H "Authorization: Bearer $QUIXYL_API_KEY"

# Extractions from a specific vendor
curl "https://api.quixyl.com/v1/extractions?vendor=Meridian+Logistics&limit=20" \
  -H "Authorization: Bearer $QUIXYL_API_KEY"

Example: Full Node.js integration

import Quixyl from '@quixyl/sdk';
import { readFileSync } from 'fs';

const client = new Quixyl({ apiKey: process.env.QUIXYL_API_KEY });

async function processInvoice(filePath: string) {
  const file = readFileSync(filePath);
  const result = await client.extract({ file, documentType: 'invoice' });

  if (result.confidence < 0.90) {
    await createReviewTask(result);
    return;
  }

  await postToAccountingSystem(result.data);
  console.log(`Posted invoice ${result.data.invoice_number} — total ${result.data.total}`);
}

The SDK handles retries, rate-limit backoff, and async polling transparently.


Error reference

CodeMeaning
400 invalid_fileFile type not supported or file is corrupt
400 page_limit_exceededDocument exceeds your plan’s per-document page limit
401 invalid_keyAPI key is missing, revoked, or malformed
402 quota_exceededMonthly page quota exhausted
429 rate_limitedToo many requests — back off and retry
500 extraction_errorUnexpected server error — retry once; contact support if it persists

Further reading

Teams

10,000+

Trust Quixyl daily

Accuracy

99.9%

AI-powered OCR

Speed

5 sec

Per document

Get started free

Ready to automate your document processing?

Extract invoice data in 5 seconds with 99.9% AI accuracy. Start with 5 pages free — no credit card required.

5 pages free · no credit card · cancel anytime