Parsli documentation

Send data out

Webhooks

Send every extracted result to your own endpoint. Configuration, payload, authentication, and the two failure modes that matter.


A Webhook posts the full result to an endpoint you control, the moment a document finishes. It is the general-purpose route: whatever Parsli does not connect to directly, a webhook reaches.

Setting one up

Under the parser's Outbound, add a Webhook integration.

SettingNotes
URLYour HTTPS endpoint
MethodPOST or PUT
Auth typeNone, bearer token, or basic
Auth tokenSent in the Authorization header

Use the test action before relying on it. A webhook pointing at a typo fails silently — extraction keeps working and the data simply never arrives.

The payload

json
{
  "event": "document.processed",
  "parser_id": "6f3a1c88-...",
  "parser_name": "Supplier invoices",
  "document_id": "b71c9d02-...",
  "timestamp": "2026-08-07T09:14:22.481Z",
  "data": {
    "invoice_number": "INV-2026-0417",
    "vendor_name": "Northgate Supplies",
    "total_amount": 1284.5,
    "line_items": [
      { "description": "Steel brackets", "quantity": 40, "unit_price": 12.5 }
    ]
  },
  "metadata": {
    "file_name": "northgate-0417.pdf",
    "mime_type": "application/pdf",
    "source_type": "email",
    "page_count": 2
  }
}

Field-by-field reference: receiving results.

data is keyed by your field names and typed to your field types. It is the same shape the API returns, so one handler covers both.

It fires for everything

A webhook catches every completed document on the parser, however it arrived — uploaded, emailed, sent through the inbound webhook, or submitted over the API.

That makes it the single most useful integration point. One endpoint sees everything.

metadata.source_type tells you which route a document took, so one handler can treat an emailed invoice differently from an uploaded one without a second parser.

Authentication

Set an auth type. Without one the endpoint accepts anything that finds the URL.

Bearer token — Parsli sends Authorization: Bearer <token>. Compare it against your secret and reject mismatches.

Basic — standard HTTP basic auth.

javascript
if (req.headers.authorization !== `Bearer ${process.env.PARSLI_WEBHOOK_SECRET}`) {
  return res.sendStatus(401)
}

The two failure modes that matter

Duplicates. Delivery is at-least-once: a network problem between our send and your acknowledgement causes a retry, and your handler sees the same document twice. Deduplicate on document_id. This bug is silent — you get duplicate rows, never an error.

Empty values treated as zero. A field the engine could not ground in the document is null. If a total arrives null and your code does amount || 0, you have just recorded a zero-value invoice. Check explicitly and route it to a human.

javascript
app.post("/parsli", async (req, res) => {
  if (req.headers.authorization !== `Bearer ${process.env.PARSLI_WEBHOOK_SECRET}`) {
    return res.sendStatus(401)
  }

  const { document_id, data, metadata } = req.body
  res.sendStatus(200)                                   // acknowledge first

  if (await alreadyProcessed(document_id)) return       // at-least-once delivery
  if (data.total_amount == null) return flagForReview(document_id, metadata)

  await recordInvoice({ documentId: document_id, ...data })
})

Acknowledge before you work. Return 200 immediately and queue the processing. A handler that does its work inline will eventually be slow enough to look like a failure and trigger a retry.

Developing against it

A webhook needs a publicly reachable URL, which localhost is not. Either use a tunnel, or poll the API while developing and switch to the webhook in production.

When it fails

A failing webhook does not fail the extraction — the document still processes, the result is still stored, and other integrations still fire. The failure is recorded against the integration.

To recover missed results, fetch them with GET /api/v1/documents/{id}. Nothing is lost.

Something here wrong or missing? Tell us — we treat it as a bug.