SUMU
Receiving

Webhooks

Register an HTTPS endpoint and get called as documents arrive and transactions progress.

Register an HTTPS endpoint and SUMU calls it as documents arrive and transactions progress. Preferable to polling — inbound documents have no other push mechanism.

POST/v1/api/webhooks
FieldType Description
urlstringrequiredHTTPS endpoint. Must respond 2xx within 10 seconds
eventsstring[]requiredWhich events to receive. At least one
secretstringrequiredShared secret for signature verification. Shown once
taxpayer_iduuidoptionalOnly events for one taxpayer. Omit for all of them
activebooleanoptionalDefault true

GET /v1/api/webhooks lists them, PATCH /v1/api/webhooks/{id} updates url, events or active state, and DELETE removes one.

Events

EventFires whenAct on it by
document.receivedAn inbound document is stored for one of your taxpayersFetching it and posting into your ledger
transaction.deliveredThe buyer's Access Point signed for the documentMarking the invoice as sent
transaction.failedDelivery abandoned after all retriesAlerting someone — this needs a human
transaction.rejectedThe receiver refused the documentReading delivery.error and correcting
tax_report.acknowledgedThe Tax Authority accepted the reportRecording compliance
tax_report.rejectedThe Authority refused itEscalating — the invoice is issued but unreported

If you're on the Platform track, decide between one unscoped webhook (everything, one endpoint) and several taxpayer_id-scoped ones (routing events to different downstream systems per client) — both are supported.

Payload

POST https://your-app.com/hooks/peppol
Content-Type: application/json
X-Sumu-Signature: t=1755081045,v1=5257a869e7ecebeda32affa62cdca3fa...
X-Sumu-Delivery-Attempt: 1

{
  "id": "evt_9c4f2a1b",
  "type": "document.received",
  "created_at": "2026-08-13T10:30:45Z",
  "data": {
    "document_id": "d51c8b3a-...",
    "direction": "inbound",
    "recipient_taxpayer_id": "625919e2-...",
    "sender": { "scheme": "0248", "identifier": "OM1100446274", "name": "Seller LLC" },
    "received_at": "2026-08-13T10:30:45Z",
    "summary": {
      "invoice_number": "EINV-0001",
      "issue_date": "2026-08-12",
      "currency": "OMR",
      "total_including_tax": "443.89"
    }
  }
}

Verifying the signature

The header carries a timestamp and an HMAC-SHA256 of "{timestamp}.{raw body}", keyed with your secret. Compare using a constant-time function, and reject anything with a timestamp older than five minutes.

const [t, v1] = header.split(",").map(p => p.split("=")[1]);
const expected = crypto
  .createHmac("sha256", secret)
  .update(`${t}.${rawBody}`)
  .digest("hex");

if (!crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))) reject();
if (Date.now() / 1000 - Number(t) > 300) reject();

Retries

Any response other than 2xx, or a timeout, is retried five times: after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours. After that the webhook is parked and shown as failing in the dashboard. Events may arrive more than once — make your handler idempotent on id.

SUMU

Sumu — a business platform owned and operated by OceanGate for Smart Technologies LLC, a FinTech and SaaS solutions company. All rights reserved © 2026.

joinsumu.com ↗