Skip to main content

Test Identifiers

GoRoute ships a sandbox: a small set of fixed test recipients that behave the same way every time you send to them. Use them to prove your integration handles success and failure correctly, without putting anything on the real Peppol network.

There are nine of them. Every one uses the Peppol scheme 9999 and an identifier that starts with test-. Nothing else is a sandbox recipient.

The sandbox only works on a test environment

Sandbox behaviour is switched off in production. If you send to a 9999:test-โ€ฆ recipient on a production environment, the send is not simulated โ€” it is refused with the error code SANDBOX_NOT_ALLOWED. Check which environment you are on before you start, using the health check below.

Before you start: check which environment you are onโ€‹

Call the health check. It needs no API key.

curl -X GET https://app-test.goroute.ai/peppol-api/health

Response:

{
"status": "healthy",
"version": "1.0.0",
"environment": "test",
"timestamp": "2026-09-04T04:26:50.868013"
}

The sandbox works when environment is any one of sandbox, development, test or staging. If it reports production, nothing on this page applies to that host.

About the base URL in these examples

The examples use https://app-test.goroute.ai/peppol-api, which is a working test host โ€” its health check reports "environment": "test". It is not necessarily the base URL issued to your account, and more than one test hostname is in use at GoRoute. Use the base URL you were given, and confirm it with the health check above before relying on it.

The nine sandbox participantsโ€‹

Send to these as the receiver. Each one always produces the same outcome.

Sends that succeedโ€‹

Peppol IDResult of the sendTransaction status
9999:test-successSucceedsdelivered
9999:test-success-slowSucceedsdelivered
9999:test-duplicate-eventSucceedsdelivered
9999:test-out-of-orderSucceedsdelivered
9999:test-retry-then-successSucceedsdelivered

All five behave identically today: the send returns success straight away and the transaction is marked delivered. The last four names describe timing and event-ordering scenarios โ€” a slow delivery, a repeated notification, notifications out of order, a failure followed by a successful retry โ€” but the service does not currently produce any of those sequences. Treat all five as "a send that works". See What this page does not describe.

Sends that failโ€‹

Each of these makes the send fail with a specific error code, so you can check your error handling reacts to the right one rather than treating every failure alike.

Peppol IDError codeError messageTransaction status
9999:test-recipient-not-foundRECIPIENT_NOT_FOUNDSandbox: Recipient not registered in Peppol networkfailed
9999:test-as4-failureAS4_DELIVERY_FAILEDSandbox: AS4 delivery failed - receiver rejected messagefailed
9999:test-validation-failureVALIDATION_FAILEDSandbox: Receiver rejected document - Schematron validation failedfailed
9999:test-timeoutTIMEOUTSandbox: Request timed out waiting for receiver acknowledgmentretrying, then failed

test-timeout behaves differently from the other three. TIMEOUT is one of the error codes GoRoute treats as worth retrying, so the transaction moves to retrying and the send is attempted again โ€” up to three retries. Each attempt hits the same sandbox recipient and fails the same way, so the transaction ends as failed after the retries are exhausted. This makes it the right identifier to use when you want to see your integration cope with a transaction that changes status several times before settling.

Sending to a 9999:test-โ€ฆ identifier that is not in these two tables is refused with the error code UNKNOWN_SANDBOX_RECEIVER, and the message lists the ones that do exist.

What sends a document to the sandbox instead of the real networkโ€‹

Two conditions decide it. Both must hold, or your document goes to the real Peppol network.

  1. The receiver scheme is 9999 and the receiver identifier starts with test-.
  2. The environment is a test one โ€” sandbox, development, test or staging.

If the first condition holds and the second does not โ€” a sandbox recipient on production โ€” the send is refused with SANDBOX_NOT_ALLOWED rather than quietly delivered. That is deliberate, so nobody on production is ever handed a fake success.

GoRoute's in-country Oman service has no sandbox. It reports every document it exchanges to the Oman Tax Authority, so it never sends to a receiver under the scheme 9999, and nothing is reported for one. How you are told depends on the route:

  • POST /documents and the other routes that take a finished document refuse it on the call, before anything is recorded: HTTP 422 with the error code SANDBOX_RECEIVER_NOT_ALLOWED. Nothing is stored or queued.
  • POST /invoices and the POS counters check the buyer's Peppol ID first, and 9999 is not a Peppol scheme on this service: the answer is validation_failed with the rule EDEC_001, and nothing is sent. Sending a draft is refused by the same check.
  • A transaction that already exists (retried with POST /transactions/{id}/retry, or queued before this rule) ends failed with the error code SANDBOX_RECEIVER_NOT_ALLOWED and is never sent.

Use a GoRoute test environment for sandbox testing.

There is a third, softer rule about API keys. Sandbox use is intended for test keys, which start with pk_test_ or sk_test_. This is a recommendation, not a gate: a key without that prefix is not blocked from the sandbox on a test environment. Use a test key anyway, so that your test traffic is unmistakable.

The sandbox endpointsโ€‹

Four read-only endpoints let you ask the service itself what the sandbox contains, rather than taking this page's word for it. All four need an API key in the X-API-Key header. Outside a test environment, GET /api/v1/sandbox/status still answers HTTP 200, with "available": false; the other three return HTTP 403 with the error code SANDBOX_NOT_AVAILABLE.

EndpointWhat it returnsPermission required
GET /api/v1/sandbox/statusWhether the sandbox is available here, the rules it applies, and how many participants existtransactions:read
GET /api/v1/sandbox/participantsEvery participant, with its outcome and error codetransactions:read
GET /api/v1/sandbox/participants/{identifier}One participant in detailtransactions:read
GET /api/v1/sandbox/metricsSandbox usage counters, held in memory and reset whenever the service restartssettings:manage

For the participant detail endpoint, pass the identifier without the 9999: prefix โ€” for example test-success, not 9999:test-success. An identifier that does not exist returns HTTP 404 with the error code SANDBOX_PARTICIPANT_NOT_FOUND, and the response lists the names that do exist.

Check the sandbox is availableโ€‹

curl -X GET https://app-test.goroute.ai/peppol-api/api/v1/sandbox/status \
-H "X-API-Key: YOUR_TEST_API_KEY"

List the participantsโ€‹

curl -X GET https://app-test.goroute.ai/peppol-api/api/v1/sandbox/participants \
-H "X-API-Key: YOUR_TEST_API_KEY"

Look one upโ€‹

curl -X GET https://app-test.goroute.ai/peppol-api/api/v1/sandbox/participants/test-timeout \
-H "X-API-Key: YOUR_TEST_API_KEY"

A suggested order to test inโ€‹

  1. Check you are on a test environment with the health check above.
  2. Confirm the sandbox is switched on with GET /api/v1/sandbox/status.
  3. Send to 9999:test-success and confirm you record a delivered transaction.
  4. Send to 9999:test-recipient-not-found and confirm your code reads the error code RECIPIENT_NOT_FOUND, rather than assuming every failure is the same.
  5. Send to 9999:test-validation-failure and confirm a rejected document is distinguishable from an undeliverable one in your own records.
  6. Send to 9999:test-timeout and confirm nothing in your system hangs, and that a transaction which passes through retrying before failed does not produce duplicate records on your side.

Example: the receiver in a UBL invoiceโ€‹

The sandbox identifier goes in the customer party's endpoint, with schemeID set to 9999:

<cac:AccountingCustomerParty>
<cac:Party>
<cbc:EndpointID schemeID="9999">test-success</cbc:EndpointID>
<cac:PartyName>
<cbc:Name>Sandbox Test Receiver</cbc:Name>
</cac:PartyName>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Sandbox Test Receiver</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>

The rest of the invoice is an ordinary Peppol BIS Billing 3.0 document. See Send Invoice for a complete example.

What this page does not describeโ€‹

  • Webhook notifications. Each sandbox participant carries a timeline of webhook events in the service's source โ€” queued, sending, delivered, and so on. Nothing in the running service sends them. Rather than describe notifications you would wait for and never receive, they are left out. Test your webhook handling against real transaction events instead โ€” see Webhooks.
  • Country-specific test identifiers. The sandbox simulates delivery; it does not apply any country's invoice rules. To check a document against a country's requirements, validate it โ€” see Validation.

Moving to productionโ€‹

Before you go live:

  • Replace every 9999:test-โ€ฆ receiver with a real Peppol identifier
  • Switch to your production API key
  • Confirm the health check on your production base URL reports "environment": "production"
  • Register your own Peppol identifier โ€” see SMP Registration
  • Point your webhook URLs at your production system
  • Work through the Production Checklist

Next Stepsโ€‹