Skip to main content

Create and Send Invoice

POST 

/api/v1/invoices

Submit an invoice using the Canonical Invoice Model (human-friendly JSON format). The system will:

  1. Validate against business, country, and BIS rules
  2. Transform to Peppol BIS 3.0 UBL XML
  3. Queue for AS4 transmission to recipient

No Peppol XML knowledge required - just provide invoice data in JSON.

Returns transaction ID for tracking delivery status.

There is no dry run here: validate_only: true is refused with 422 VALIDATE_ONLY_NOT_SUPPORTED before anything is created or sent. To check an invoice without sending it, use POST /api/v1/invoices/validate. Production (app.goroute.ai) takes this refusal in a later release than the test environment; a deployment without it ignores validate_only and sends the invoice, so never set it expecting a dry run.

There is no "send later" here either, and the body carries no idempotency key. Each is refused with 422 before anything is created or sent, whether it is at the top level or under an options object:

  • send_immediately other than true: SEND_IMMEDIATELY_NOT_SUPPORTED. To keep an invoice and send it later, create a draft (POST /api/v1/drafts) and send it with POST /api/v1/drafts/{draft_id}/send.
  • idempotency_key: IDEMPOTENCY_KEY_IN_BODY_NOT_SUPPORTED. Send the key in the Idempotency-Key header, the only place it is read. A body idempotency_key equal to that header is accepted: the header deduplicates the request.

Production takes these two refusals in the same later release: until then it ignores both fields, so send_immediately: false sends the invoice at once and a body idempotency_key does not stop a duplicate.

An invoice that fails validation is not sent. It is answered with status validation_failed and the errors under validation.errors: with HTTP 202 by default, or, where the platform answers it with 422 (INVOICE_VALIDATION_FAILED_STATUS), with HTTP 422 and the same body plus a detail block (error validation_failed, error_code INVOICE_INVALID, errors). Where the platform enforces the official rules (the UBL XSD and the Schematron of the rendered document's customisation), an invoice they refuse is answered the same way, one error per rule.

A retry with the same Idempotency-Key header (within 24 hours, for the same request) is a replay: it answers the first send's transaction_id, that transaction's status as it is now, the message Invoice already accepted (idempotent replay) and the document_number and document_uuid the first send recorded. Nothing is sent twice. A replay is not validated again, so its validation is null (production, until it takes the same later release, validates it again and returns that result). The same key with a different request is refused with 422 IDEMPOTENCY_KEY_REUSED.

The same invoice (same seller, type, number, issue date and tax totals) is sent once, whatever Idempotency-Key carries it: sent again under another key it is refused with 409 SAME_DOCUMENT_EXISTS naming the first transaction. When that send failed, an invoice a tax authority is told about (an Omani one) is refused with SAME_DOCUMENT_FAILED and its /retry; any other goes again as a new transaction (a corrected buyer, say). Sent again without a key, it is answered with the first transaction. Two sends of one invoice at the same moment are taken one after the other; one still waiting after a minute is refused with 409 SAME_DOCUMENT_IN_FLIGHT (retryable).

Requestโ€‹

Responsesโ€‹

Successful Response