POST /v1/send
Generate an e-invoice and queue it for network delivery in one call. In preview: the transmission endpoints are not generally available yet.
POST /v1/send takes the same EN 16931 invoice object as POST /v1/generate, plus the routing, and does both halves in one call: the engine builds and validates the XML, then the document is durably queued for delivery and you get a txn_ handle back. Submission to the network happens afterwards, asynchronously.
Request
{
"standard": "peppol-bis",
"profile": "peppol",
"invoice": { },
"network": "peppol",
"sender": { "scheme": "9930", "id": "DE123456789" },
"recipient": { "scheme": "0088", "id": "5790000435975" }
}
The generation half is POST /v1/generate’s own request, minus the knobs that only make sense for a file you read yourself: the network payload is always XML, so output, template, pdfTemplateId and verify are not accepted here. Validation is not optional on this route.
| Field | Type | Notes |
|---|---|---|
standard |
string | Target standard, as on /v1/generate. |
profile |
string? | Target profile, as on /v1/generate. facturxProfile is accepted too and wins over profile. |
invoice |
object | The EN 16931 invoice object, exactly as /v1/generate takes it. |
network |
string | Which delivery network to route over: peppol, sdi, ksef, ppf_pdp or choruspro. |
sender |
object | { scheme, id } you are sending as. Both are required and non-empty: this identity is what the audit record stands on. |
recipient |
object | { scheme, id } at the other end. A Peppol identifier is a four-digit scheme code and a value, for example 0088:5790000435975. |
documentTypeId |
string? | The network document type, where you want to name it rather than let it be derived from the document. |
deadlineAt |
string? | ISO 8601 delivery deadline. A transmission that is late against it is flagged atRisk, which is what transmission.at_risk reports. |
The request body is capped at 1 MiB; a larger one is a 413 and nothing is read.
The sender must be yours, in both places
You may only send as an identifier your organization has registered and that is active, so sender is checked against your registrations before the engine runs. It is not yours: 403 SENDER_NOT_REGISTERED.
The invoice carries the seller’s electronic address too, and Beliq refuses a send where the request and the document name different parties, rather than putting a document on a network under an identity its own content contradicts. That is 400 DOCUMENT_PARTY_MISMATCH, and details gives both identifiers and which role disagreed. On Peppol the document’s field is the seller’s cbc:EndpointID; on Italy SdI it is the CedentePrestatore VAT number, and on a document that may be a self-invoice the buyer’s or the issuing third party’s VAT as well (DOCUMENT_PARTY_MISMATCH says when).
In test mode this is the thing most likely to catch you out, because rehearsing means both the request and the invoice have to name the test identifier Beliq issued you.
Response
A 202 means the document was built, passed validation and is queued. It has not been submitted yet.
{
"success": true,
"data": {
"id": "txn_9f3c2a1b8e4d47a2b1c05d4e3f2a1b0c",
"status": "queued",
"direction": "outbound",
"network": "peppol",
"deduplicated": false,
"verdict": { }
}
}
| Field | Type | Notes |
|---|---|---|
id |
string | The transmission handle. Poll it at GET /v1/transmissions/{id}. |
status |
string | The status at the moment of the answer, normally queued. |
direction |
string | outbound. |
network |
string | The network it is queued for. |
deduplicated |
boolean | true when an Idempotency-Key replay handed back the transmission you already queued, with no new network activity. Such a reply is a 200, not a 202. |
verdict |
object? | The validation result for the document that was built, in the same shape POST /v1/validate returns. |
An invoice that fails validation is a 422 and nothing is queued.
Retrying safely
Send an Idempotency-Key header. A repeat with the same key and the same body returns the existing transmission as a 200 with deduplicated: true, costs no quota unit, and does not run the engine again. A repeat with the same key and a different body is refused with 409 IDEMPOTENCY_KEY_REUSED, so a key can never return the wrong document.
Without a key, Beliq still refuses to put the same document on the network twice: an identical document to the same recipient, while the first transmission can still arrive, is 409 CONTENT_ALREADY_SENT and details.transmissionId names the one you already have.
Following a transmission
| Read | What it gives |
|---|---|
GET /v1/transmissions/{id} |
The transmission: status, networkStatus, counterparty, atRisk, finishedAt, finishLimit, livemode, timestamps. |
GET /v1/transmissions |
Your transmissions, filterable by direction, status and network, cursor-paginated. |
GET /v1/transmissions/{id}/attempts |
Each submission attempt, with the provider reference and the error that ended it. |
GET /v1/transmissions/{id}/events |
The status history as a hash-chained log, with chainOk saying whether the chain verifies end to end. |
GET /v1/transmissions/{id}/receipts |
The network’s own proofs of what happened, and a single receipt read returns the verbatim artifact. |
GET /v1/transmissions/{id}/document |
The document as it went on the network. |
Every one of these reads is scoped to the calling key’s mode, so a live key cannot see a test-mode transmission and the other way round. The push equivalent is the transmission.* webhooks, which saves you polling.
When a transmission is finished
finishedAt is the time a transmission finished, and null until then. Once a transmission is finished its status does not change again and Beliq stops asking the delivery partner about it, so it is the field to wait on, rather than a particular status. A send finishes at once when it is rejected or failed. It finishes at its network’s own end, such as delivered, once the network’s proof of that end is stored with it; until the proof is read, Beliq keeps asking the provider for it.
finishLimit says why a send finished short of its network’s own end, and is null otherwise:
finishLimit |
Where the send ended |
|---|---|
provider_ceiling |
The send went as far as the delivery partner can report, with its proof, and the network’s end lies further. On Peppol the send was handed to the recipient’s access point with its transport receipt and stays cleared: that access point’s report of what became of the document cannot be read through the partner yet, so it never reads delivered. |
no_availability_report |
France: the send was handed to the recipient’s platform, with its proof, and stays cleared. No report from the platform that the invoice was made available (status 203) came within 7 days of the hand-over. That report does not come for every send, and the 7 days are a period Beliq chose. |
Sending a document you already built
Where you build the XML yourself, POST /v1/transmissions takes it directly: the same network, sender, recipient, documentTypeId and deadlineAt, plus format and the document as xml. It skips the engine’s generation step and queues what you gave it. The idempotency and duplicate rules above are the same.
Errors
| HTTP Status | Error Code | When |
|---|---|---|
| 400 | DOCUMENT_PARTY_MISMATCH |
The request and the document name different parties for the sender or the recipient |
| 400 | RECIPIENT_NOT_ROUTABLE |
The recipient is not in a form the network can address |
| 403 | SENDER_NOT_REGISTERED |
The sender is not a registered, active participant of your organization |
| 409 | CONTENT_ALREADY_SENT |
The same document is already on its way to that recipient |
| 409 | IDEMPOTENCY_KEY_REUSED |
An Idempotency-Key was reused with a different body |
| 413 | VALIDATION_ERROR |
The request body is larger than the 1 MiB size limit |
| 422 | VALIDATION_ERROR |
The invoice failed validation, so nothing was queued |
| 503 | TRANSMISSION_DISABLED |
Delivery is not open on this deployment. This is what the endpoint answers today |
| 503 | TRANSMISSION_NO_PROVIDER |
Delivery is open, but no provider is available for that network in your key’s mode |
| 503 | DATABASE_UNAVAILABLE |
Beliq’s database could not serve the request, and the send may still have been queued. Retry after Retry-After with the same Idempotency-Key. Quota refunds says whether a spent unit comes back |
France and Poland have refusals of their own, because a document that may not legally travel this way is refused rather than queued. The full set, with what to do about each, is on the errors page.