Go-Live Checklist
Validate business, domain, NRS, billing, security, retry, and operational readiness before sending live invoices.
Use test state to prove the complete customer and failure journey, then treat live as a separate controlled launch. A successful NRS sign alone is not enough: the integration also needs durable mappings, billing recovery, delivery handling, security controls, and an operator runbook.
Release path
Prove test
Exercise the approved mode, NRS validation/signing, billing failures, retries, transmission, and inbound processing.
Drain
Have Duplo place the domain in draining and clear all non-terminal billing, Stored, and transmission work.
Change state
Complete the controlled Duplo Dashboard state switch, sign in again, and create the target-state credential.
Reapprove
Configure the new credential and wait until the domain is approved in the target state and mode.
Re-onboard and ramp
Onboard represented businesses only after approval, then start with monitored traffic.
1. Business readiness
2. Credential and domain readiness
Run a non-billable host check:
curl https://acme.invoice.tryduplo.com/health{ "status": "ok" }Then run an authenticated, unbilled NRS self-health check:
curl \
--url https://acme.invoice.tryduplo.com/api/v1/invoice/transmit/self-health-check \
--header 'x-api-key: pk_live_replace_with_your_key'Do not use either health response as proof that a wallet is funded or a represented business is correctly mapped.
Also configure deployment probes deliberately: /health/live is process liveness, while /health/ready covers local initialization and the Redis safety dependency. A shared Spend or NRS outage does not deliberately make every Gateway pod unready. Duplo operators use the protected, cached /health/dependencies view for dependency, circuit, capacity, processor-heartbeat, and recovery state; customers must not treat that route as a live NRS probe.
3. Represented-business readiness
For every issuer, maintain a durable mapping such as:
{
"yourCustomerId": "erp-customer-1042",
"gatewaySubBusinessId": "nrs-business-demo",
"firsBusinessId": "nrs-business-demo",
"nrsIdentity": {
"entityId": "entity-demo",
"entityReference": "entity-reference-demo",
"nrsBusinessId": "nrs-business-demo",
"businessName": "Acme Retail Limited",
"tin": "12345678-0001",
"irnTemplate": "{{invoice_id}}-2A3A045D-{{YYYYMMDD}}"
},
"businessState": "live",
"cryptoVersion": 0
}cryptoVersion: 0 is a valid production state: it means no QR material is stored. Gateway stores supplied values opaquely and discovers an unusable public key only when QR generation is attempted. QR generation is best-effort and must not be used as proof that onboarding, signing, or transmission succeeded.
4. Invoice-data readiness
Validate representative invoice fixtures before any sign test:
- Smallest and largest expected invoices.
- Multiple line items and multiple tax subtotals.
- Commercial invoice plus every note/type your product supports.
- Receiver connected and receiver unavailable.
- Invalid TIN, expired resource code, bad total, duplicate IRN, and malformed body.
- Additive NRS fields, changed types, malformed success JSON, unexpected
202, empty204, provider401/403,429, redirect, timeout, and oversized response fixtures. - Payload near your configured application limit; Gateway's default buffered ceiling is 10 MiB.
5. Mode-specific readiness
Pass-through
Stored
6. Billing and failure drills
Prove these behaviors in an authorized test environment:
| Drill | Expected system behavior |
|---|---|
| Insufficient wallet balance | NRS is not called; invoice stays recoverable; operator sees 402 GW_BILLING_WALLET_DECLINED guidance |
| Invalid NRS document | Validation/sign rejection is recorded; no blind retry loop starts |
| Pass-through success | One charge and one durable sign result are correlated to IRN, idempotency identity, and request ID |
| Duplicate Pass-through attempt | 409 enters original-result recovery; a random key is not generated automatically |
Simulated GW_SIGN_OUTCOME_UNCONFIRMED | Automation freezes that IRN and creates a reconciliation case with requestId |
| Stored intake response lost | Exact request is resent with the same command key and resolves to one invoice |
| Stored worker remains pending | Poller backs off and alerts after its own service-level deadline without resubmitting |
| Transmission timeout | System reads current state and retries delivery only; it never signs again |
| Rate limit | Caller uses bounded jittered backoff and caps concurrency; honor Retry-After if a route supplies it |
| Slow or incomplete request body | Caller handles GW_REQUEST_BODY_TIMEOUT, sends the complete body promptly, and retains the same mutation identity |
| Gateway capacity full | Caller handles GW_GATEWAY_CAPACITY_EXCEEDED, honors Retry-After, and does not fan out to another host or key |
| Runtime routing loop | Gateway returns 508 GW_ROUTING_LOOP_DETECTED; automation stops until the upstream route is corrected |
Finance and engineering should be able to reconcile, for a chosen period:
Your sign commands
↔ Gateway request ID/idempotency/IRN
↔ NRS result or reconciliation state
↔ Duplo signing fee movement/reversal7. Webhook readiness
Inbound receipt is a separately approved launch scope
Do not use this checklist as proof that inbound NRS callbacks are enabled. Before you include inbound receipt in test or live scope, obtain Duplo confirmation that the environment has trusted-edge path proof, independent NRS sender authentication or an authoritative NRS source restriction, WAF/rate controls, and a proven callback timing and crash-recovery decision. The current handler completes NRS download/decrypt processing before acknowledging the callback and records caught processing failures without asking NRS to retry them. If those controls and a durable recovery path have not been certified, Duplo must leave the callback unregistered and your release must explicitly exclude inbound receipt.
See Verifying events for how an authenticated invoice read establishes invoice truth while the webhook request itself remains unsigned.
8. Security review
9. Production cutover
- Stop test invoice producers and reconcile every in-flight sign, non-terminal billing attempt, Stored invoice, and unresolved transmission.
- Coordinate with Duplo to place the approved domain in
draining, prepare it for target-state reapproval, and complete the Duplo Dashboard state transition. Expect the prior key/refresh context, represented-business access, and domain approval to stop being usable. - Sign in again and generate a new live API key.
- Configure the live webhook and intended caller IPs on that credential; if you rely on IP restriction, confirm that Duplo has enabled enforcement in live.
- Wait until the domain reports
approvedin live with the expected mode/version. - Complete live represented-business onboarding and save the new state-bound mappings.
- Run health, NRS resource, recipient lookup, and validation checks before the first billed sign.
- Send one controlled invoice, persist its complete safe audit trail, transmit it, and reconcile the sign fee.
- Ramp volume in stages while watching billing, NRS latency/error rate, Stored queue age where applicable, and webhook delivery.
Do not bypass reapproval
A state or mode mismatch returns 409 by design. Do not change the hostname, forge state/mode fields, reuse a revoked key, or call NRS directly to work around it. Finish the controlled domain workflow and preserve in-flight reconciliation.
Operator runbook
Document these decisions before launch:
| Signal | Owner | Immediate action | Escalation data |
|---|---|---|---|
401 surge | Security/integration | Verify secret deployment and key status; stop rotation loops | Host, state, timestamps, key prefix/last four only |
409 state or mode drift | Platform owner | Pause intake and read current domain configuration | Domain status, mode/version, request ID |
GW_BILLING_WALLET_DECLINED | Finance + operations | Pause billed intake; check wallet/account readiness | Request ID, IRN, child ID, UTC time, sanitized error |
GW_SIGN_OUTCOME_UNCONFIRMED | Reconciliation owner | Freeze the IRN and preserve evidence; do not re-sign | Request ID, operation ID when present, idempotency key, NRS lookup |
GW_TRANSMIT_ATTEMPT_ALREADY_PROCESSED | Reconciliation owner | Use the returned operation ID; do not bypass the existing claim | Request ID, operation ID, IRN, idempotency key |
GW_TRANSMIT_OUTCOME_UNCONFIRMED | Reconciliation owner | Freeze transmission and reconcile its durable claim; do not resend | Request ID, operation ID, IRN, idempotency key, NRS lookup |
GW_GATEWAY_CAPACITY_EXCEEDED | Gateway operator | Check noisy-domain, process, Redis, socket, and dependency saturation; retain caller keys | Request IDs, route, domain hash, capacity snapshot |
GW_ROUTING_LOOP_DETECTED | Platform/network | Stop retries and correct DNS, proxy, origin, or service discovery | Request ID, environment, configured target, route |
| Stored pending beyond SLO | Operations | Keep read-only polling bounded and open an investigation | Invoice ID, IRN, child ID, timestamps |
| Webhook outage | Application owner | Restore 2xx receiver, verify reachability, reconcile via invoice API | Delivery/invoice IDs and outage window |
| NRS resource rejection | Tax/integration | Refresh current catalog and review payload mapping | NRS status/body, resource version/time |
Use the Gateway operations runbook for dependency, health, circuit, Slack, and recovery procedures, and the billing support packet for Duplo escalation. Never include full credentials or private cryptographic material.
How is this guide?