Integrate in Pass-Through Mode
Validate, bill and sign synchronously, then look up and transmit the signed IRN.
Pass-through mode is for integrations that keep their own durable invoice record and send the NRS wire document through Duplo. Validation and transmission are unbilled. The exact signing route is billable.
Request contract
Every request uses your approved hostname and three customer-controlled headers: x-api-key, the API key from Duplo Dashboard; x-sub-business-id, the gatewaySubBusinessId of the represented business; and content-type: application/json. A sign attempt also carries x-idempotency-key, x-attempt-number, and x-request-id. Each example below shows the full header set for its operation.
x-attempt-number must be a positive integer and defaults to 1 when omitted. Keep both the attempt number and idempotency key unchanged for network retries of the same logical attempt. Only after an outcome is conclusively failed or reversed—and a new attempt is authorized—increment the attempt number and create a new stable idempotency key. Never increment or retry an unconfirmed outcome.
An optional x-invoice-reference must exactly equal the body irn. Send only the documented Gateway headers; state, mode, payer wallet, and fee are server-controlled.
Processing flow
Validate
Gateway binds the child and relays the document to NRS without charging.
Debit
On sign, Duplo resolves the current fee and confirms the payer transfer before NRS.
Sign
Gateway calls NRS and accepts only the documented sign-success contract.
Transmit
Use the same child and signed IRN for unbilled lookup and delivery.
1. Validate the document
Build the Pass-through NRS document, then call:
curl --request POST \
--url https://acme.invoice.tryduplo.com/api/v1/invoice/validate \
--header 'content-type: application/json' \
--header 'x-api-key: pk_test_replace_with_your_key' \
--header 'x-sub-business-id: 44444444-4444-4444-8444-444444444444' \
--data @invoice.jsonExpected NRS success:
{
"code": 200,
"data": {
"ok": true
}
}Before forwarding, the Gateway requires the document's business_id and normalized accounting_supplier_party.tin to match the authorized represented business. Validation creates no signing charge and no Stored invoice row.
2. Sign the same document
Do not mutate a validated invoice before signing. Reuse its exact IRN and document bytes where practical.
curl --request POST \
--url https://acme.invoice.tryduplo.com/api/v1/invoice/sign \
--header 'content-type: application/json' \
--header 'x-api-key: pk_test_replace_with_your_key' \
--header 'x-sub-business-id: 44444444-4444-4444-8444-444444444444' \
--header 'x-idempotency-key: sign-INV000001-2A3A045D-20260818-1' \
--header 'x-attempt-number: 1' \
--header 'x-request-id: req-018f0f56-f5f5-7c98-a21a-4a03c11a85b1' \
--data @invoice.jsonThe Gateway performs these checks in order:
- Authorize the API key against the approved host and represented business.
- Verify the body IRN, NRS business ID, and supplier TIN.
- Resolve and debit the current-state signing fee from the business you manage in Duplo Dashboard.
- Persist recovery context before contacting NRS.
- Call NRS, classify the outcome, and return the applicable response.
An exact success has an HTTP 2xx status and this NRS body contract:
{
"code": 201,
"data": {
"ok": true
}
}NRS may add response fields under data. Treat the returned document as sensitive, store it durably, and do not log full invoice contents in general application logs.
Pass-through does not store the invoice
A successful Pass-through request creates billing and audit state but no customer-visible invoice row in Duplo Dashboard. Your system must keep the original request, NRS response, IRN, represented-business mapping, idempotency key, and request trace.
3. Check receiver availability
After exact sign success, the Gateway normally binds that IRN to the host, state, and represented business. This post-sign delivery binding is best-effort: if it is temporarily unavailable, sign can still succeed while later lookup or transmit returns 503. Preserve the successful sign response and contact support instead of signing again. Use the same child:
curl \
--url https://acme.invoice.tryduplo.com/api/v1/invoice/transmit/lookup/INV000001-2A3A045D-20260818 \
--header 'x-api-key: pk_test_replace_with_your_key' \
--header 'x-sub-business-id: 44444444-4444-4444-8444-444444444444'A common NRS response reports connection state for the invoice parties:
{
"code": 200,
"data": {
"accounting_supplier_party": { "up": true },
"accounting_customer_party": { "up": true },
"bill_party": { "up": true },
"ship_party": { "up": true }
}
}NRS owns this response schema and may evolve it. Preserve the original response and use the current NRS reference when interpreting party availability.
4. Transmit the signed IRN
curl --request POST \
--url https://acme.invoice.tryduplo.com/api/v1/invoice/transmit/INV000001-2A3A045D-20260818 \
--header 'content-type: application/json' \
--header 'x-api-key: pk_test_replace_with_your_key' \
--header 'x-sub-business-id: 44444444-4444-4444-8444-444444444444' \
--data '{}'Expected NRS success:
{
"code": 200,
"data": {
"ok": true
}
}NRS can return code: 201 for this operation; success requires data.ok: true. Lookup and transmission are unbilled. A child/IRN mismatch or missing signed-IRN binding returns 403 before NRS.
Safe retry behavior
| Situation | What to do |
|---|---|
| Validation failed with a definite 4xx | Correct the document; validation was not billed |
| Sign was rejected definitively | Keep the trace; Duplo starts the safe fee reversal |
Sign returned an unconfirmed 502 | Do not sign again; retain traceId, check the IRN, and contact Duplo support |
| Your client lost a successful sign response | Query the signed IRN first; replay with the same attempt identity is blocked with 409 |
Lookup/transmit returned 503 after sign success | Preserve the sign response and contact support; do not re-sign to recreate delivery state |
Read Billing and safe retries before implementing automatic retries.
How is this guide?
Last updated on