LumiParser

Core API

Webhooks

Get notified the moment a document finishes parsing — no polling required.

Register a webhook

curl -X POST https://api.lumiparser.com/v1/webhooks \
  -H "Authorization: Bearer inv_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/invoices",
    "events": ["invoice.completed", "invoice.failed"]
  }'

Response:

{
  "id": "wh_abc123",
  "url": "https://your-app.com/webhooks/invoices",
  "events": ["invoice.completed", "invoice.failed"],
  "active": true,
  "created_at": "2024-01-20T10:00:00Z",
  "secret": "3f9a…c21e"
}

The secret is returned only once, at creation. Store it as WEBHOOK_SECRET to verify signatures. Webhook URLs must be public https:// addresses.

Events

EventWhen it fires
invoice.completedParse finished successfully — parsed and confidence are populated
invoice.failedParse failed — check the error field on the invoice

Webhook payload

{
  "event": "invoice.completed",
  "invoice_id": "inv_a1b2c3d4e5f6g7h8",
  "timestamp": "2024-01-20T10:30:05Z"
}

Fetch the full parsed result with GET /v1/invoices/:id using the invoice_id from the payload.

Verifying signatures

Every webhook request includes an X-Signature-256 header. Verify it to confirm the request came from us and hasn't been tampered with.

const crypto = require('crypto');

function verifyWebhook(rawBody, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  if (typeof signature !== 'string' || signature.length !== expected.length) return false;
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Express example
app.post('/webhooks/invoices', express.raw({ type: '*/*' }), async (req, res) => {
  const sig    = req.headers['x-signature-256'];
  const secret = process.env.WEBHOOK_SECRET;

  if (!verifyWebhook(req.body, sig, secret)) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body.toString());

  if (event.event === 'invoice.completed') {
    // Fetch full result and process
    const invoice = await fetchInvoice(event.invoice_id);
    await processInvoice(invoice);
  }

  res.send({ received: true });
});

Retries

If your endpoint returns a non-2xx status, we make up to 3 attempts, waiting 1s and then 2s between them (each attempt times out after 10s). After the third failure the delivery is abandoned. Make your webhook handler idempotent — the same event may be delivered more than once.

Manage webhooks

# List all webhooks
GET /v1/webhooks

# Remove a webhook
DELETE /v1/webhooks/:id