document parsing webhookwebhook document processingautomate document pipeline

Webhook-Driven Document Processing: Build Automated Pipelines with Dokyumi

March 16, 2026 · Updated

Dokyumi webhooks deliver completed extraction data for documents submitted through a configured upload site. Direct POST /api/v1/extract requests are synchronous and do not accept a per-request webhook_url.

When Dokyumi sends a webhook

  1. Create or edit an upload site in Sites → Settings.
  2. Set the site webhook URL to a public HTTPS endpoint.
  3. A user submits a document through that site.
  4. After a successful extraction, Dokyumi sends one extraction.completed event.

Every plan includes at least one upload site, with plan-specific site limits. API-key uploads, dashboard uploads, and hard extraction failures do not produce this webhook event.

Payload contract

{
  "event": "extraction.completed",
  "extraction_id": "6f8c2d4a-7b31-4e95-9a20-c1d7f6b84210",
  "site_id": "3e5b7c19-8d42-4fa1-a760-c2e9d4b18306",
  "data": {
    "invoice_number": "INV-2026-0342",
    "vendor_name": "Acme Corp",
    "total_amount": 4250.00
  },
  "timestamp": "2026-08-23T19:12:00.000Z"
}

Use extraction_id as the idempotency key. The data object contains the extracted schema fields directly; it is not nested under result.

Verify signed deliveries

When a signing secret has been provisioned for the site, Dokyumi sends X-Dokyumi-Signature as the raw lowercase hexadecimal HMAC-SHA256 digest of the exact request body. The header does not include a sha256= prefix. If a configured site sends an empty signature header, contact Dokyumi support to provision or rotate its secret before treating the signature as an authentication control.

import express from 'express';
import crypto from 'node:crypto';

const app = express();
app.use('/webhooks/dokyumi', express.raw({ type: 'application/json' }));

app.post('/webhooks/dokyumi', async (req, res) => {
  const signature = req.header('x-dokyumi-signature') || '';
  const expected = crypto
    .createHmac('sha256', process.env.DOKYUMI_WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');

  if (!/^[0-9a-f]{64}$/i.test(signature) || !crypto.timingSafeEqual(
    Buffer.from(signature, 'utf8'),
    Buffer.from(expected, 'utf8')
  )) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const event = JSON.parse(req.body.toString('utf8'));
  if (event.event !== 'extraction.completed') {
    return res.status(400).json({ error: 'Unsupported event' });
  }

  await queue.add('process-dokyumi-extraction', {
    extractionId: event.extraction_id,
    siteId: event.site_id,
    data: event.data,
  });

  return res.status(200).json({ received: true });
});

For production code, compare equal-length buffers and reject a missing signature before calling timingSafeEqual. Keep the raw request body: parsing and re-serializing JSON changes the signed bytes.

Acknowledge quickly and process idempotently

Validate the event, await a durable queue write, and only then return a 2xx response. Do database writes and downstream API calls in the worker. Upsert on extraction_id so a retry cannot create duplicate bills, leads, or records.

async function processDokyumiEvent(event) {
  await db.extractions.upsert({
    where: { extraction_id: event.extractionId },
    create: {
      extraction_id: event.extractionId,
      site_id: event.siteId,
      data: event.data,
    },
    update: { data: event.data },
  });
}

Retries and operational limits

Dokyumi makes the initial delivery and retries an undelivered event during a background sweep roughly every ten minutes, up to six total attempts. There is currently no self-service replay button. Fix the receiver before the retry window closes; if it has passed, contact support with the extraction ID.

Your endpoint should accept duplicate deliveries, log extraction_id, return a non-2xx response only when a retry is useful, and alert on events that never reach your durable queue.

Direct API batches are different

For server-to-server batches sent to POST /api/v1/extract, use bounded concurrency and store each synchronous response. Send the multipart file and optional schema slug. A direct API request does not trigger a site webhook.

See the API reference for the response envelope, the bulk-processing guide for queue design, and the Zapier guide for no-code workflows. You can test a site workflow with the invoice parser or map it to an accounts-payable workflow.

These articles are selected from the same editorial cluster, not generated from keyword overlap.

Put build and ship api pipelines to work

Use the API and webhook reference

Confirm request fields, response data, validation, confidence, and webhook signing.

Trace an accounts-payable workflow

See how schema, endpoint, confidence review, and ledger delivery fit together.

Build the first endpoint

Create a schema and test the pipeline against a real source document.

Test the extraction on your own documents

25 free credits each month. One credit covers a document up to 5 pages; self-serve documents can be up to 50 pages. No credit card required.