Best practices
The pieces that turn a working integration into a production-grade one.
Everything below is documented in full elsewhere in this reference — this page is the checklist that ties it together. If you only read one section, read Check the receiver first and Always send an idempotency key; they're the two mistakes that are expensive to make in production and free to avoid.
Check the receiver first
Before calling Create an invoice, call
Participant lookup on the buyer's identifier. A business that isn't
registered on Peppol yet — or hasn't published the capability you need — will fail delivery after
you've already committed an invoice number, which is harder to unwind than a lookup that returns
found: false. Lookups are cheap and cached briefly on SUMU's side; there's no penalty for checking
routinely rather than only when a send fails.
Always send an idempotency key
Every POST that creates something accepts an Idempotency-Key header — see
Idempotency. An invoice number, once issued, is permanently claimed. Without
a key, retrying a request whose response you never saw either double-sends or fails with
DUPLICATE_INVOICE_NUMBER, and neither is cleanly recoverable after the fact. Generate the key once per
logical send (e.g. from your own order/invoice reference), not once per HTTP attempt.
Validate before you build the real thing
Validate runs the full Oman rule set against a UBL document without sending
anything, doesn't count against your send quota, and is safe to call as often as you like. Wire it into
CI or a pre-flight check in your own pipeline rather than discovering SCHEMATRON_INVALID failures in
production.
Handle errors by code, and back off correctly
Work from the full error table rather than branching on HTTP status alone —
400 covers several distinct causes, and 409 TAXPAYER_NOT_ASSOCIATED needs a completely different
response (go finish the Fawtara association) than 409 DUPLICATE_INVOICE_NUMBER does (that send already
happened, go look it up). On 429, respect the Retry-After header and back off exponentially rather
than retrying immediately — see Rate limits. On 503 NETWORK_UNAVAILABLE,
retry later; it means Peppol or the Tax Authority is unreachable, not that your request was wrong.
Track delivery deliberately
Every send gives you a transaction_id immediately, before delivery or tax reporting have happened —
see why there are two separate outcomes. Don't
poll Get a transaction in a tight loop; either register a webhook (see
below) or poll on a sane interval. If you do both, treat the webhook as authoritative and use polling
only as a reconciliation fallback, not as your primary signal.
Verify webhook signatures
Every webhook delivery carries an X-Sumu-Signature header — an HMAC-SHA256 of the timestamp and raw
body, keyed with the secret shown once at registration. Webhooks has the full
verification code. Treat an unverified webhook body as untrusted input; don't act on it without checking
the signature first.
Respect rate limits, and plan bulk sends around the reporting window
Limits apply per API key, shared across every taxpayer on the Platform track — see Rate limits for the actual numbers. There's no batch-send endpoint today, so a month-end run is many individual calls: spread it over the hour rather than bursting it, since Oman requires tax reporting within 15 minutes of issuing a B2B invoice. Talk to SUMU before a run above 10,000 documents.
Keep credentials out of source control
Load your API key from an environment variable or a secrets manager, never a committed file. Rotate by creating a new key and deploying it before revoking the old one — see Authentication for the multi-key rotation model. If you're on the Platform track, also read that page's callout on the current access-control gap: one key today has full access to every taxpayer under it.
Test against sandbox before going live
sumu_test_ keys build and validate documents without transmitting them — see
Sandbox for what's simulated versus real. Point your integration tests at
sandbox rather than mocking SUMU's API yourself where possible; the sandbox is more likely to catch a
real Oman validation rule than a hand-written stub is.