Orders API
A commercial order is how a subscription comes into existence, changes, or ends. This page covers the order document endpoints (PDF generation and email delivery) and the order-document-related fields on the order and billing-template resources. For the order lifecycle itself (create, validate, preview, cancel), see the API surface tour and the catalog to first invoice quickstart.
Prerequisites
A bearer token, per Authentication:
export TOKEN=$(curl -s -X POST "$KEYCLOAK_TOKEN_URL" \
-d "grant_type=client_credentials&client_id=$KEYCLOAK_CLIENT_ID&client_secret=$KEYCLOAK_CLIENT_SECRET" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
Order documents
Every commercial order can be rendered to a PDF document (a quote/order confirmation, not an invoice) and, optionally, emailed to the customer. Both endpoints use a dedicated ORDER billing template, never an invoice template, so the two document families can never cross-resolve into each other.
| Method | Path | Description |
|---|---|---|
GET | /api/v1/orders/{code}/document?template= | Download the order document as a PDF. |
POST | /api/v1/orders/{code}/document/email?template= | Render the order document and email it as a PDF attachment. |
Download the order document
curl -H "Authorization: Bearer $TOKEN" \
-o order-QS1_ORDER.pdf \
"https://acme.billerang.com/api/v1/orders/QS1_ORDER/document"
Response: 200, Content-Type: application/pdf, with
Content-Disposition: attachment; filename="order-{code}.pdf". The response
is the raw PDF bytes, not a JSON envelope.
The template defaults to the built-in order-default template, which is
auto-provisioned on first use, so this call works out of the box with no
setup. To render with a different, uploaded ORDER template, pass
?template=:
curl -H "Authorization: Bearer $TOKEN" \
-o order-QS1_ORDER.pdf \
"https://acme.billerang.com/api/v1/orders/QS1_ORDER/document?template=commercial_order_fr"
Rendering is stateless: nothing is persisted by this call, and the PDF is
generated fresh on every request. Rated amounts come from the same virtual
rating engine as POST /orders/preview, so a DRAFT order shows real
prices, not placeholders. Quote-driven orders also include their persisted
order price lines, including ONE_SHOT_INVOICING_PLAN milestones.
The document is available for every order status except CANCELED.
Errors:
| Status | Body | Cause |
|---|---|---|
404 | ORDER_NOT_FOUND | No order with that code. |
404 | TEMPLATE_NOT_FOUND | The requested ?template= does not exist. |
400 | TEMPLATE_INVALID | The template exists but is not usable for orders: its documentKind is not ORDER, or its renderingMode is not HTML. |
409 | — | The order is CANCELED. |
All error bodies are JSON, even though a successful response is a PDF stream.
Email the order document
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{}' \
"https://acme.billerang.com/api/v1/orders/QS1_ORDER/document/email"
Every field in the body is optional; an empty object {} is a valid
request and relies entirely on the defaults below.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
to | string | No | Billing account contact email, then the customer contact email | Recipient address. 400 NO_RECIPIENT if neither default resolves. |
cc | string[] | No | The billing account's cced emails | Dropped (not merged) when to is explicitly overridden. |
subject | string | No | "Your order {orderNumber}" | Email subject. |
message | string | No | — | Email body text. |
The sender (from) is not a request field: it is always the seller's
contact email, falling back to the platform's mail.from property when the
seller has none.
?template= selects the ORDER template used to render the attached PDF,
with the same default (order-default) and validation rules as the
download endpoint above. The generated PDF is attached as
order-{code}.pdf.
Response (200): the effective values after defaults resolution, so a
caller can confirm who actually received the email.
{
"sent": true,
"to": ["billing@acme-customer.com"],
"cc": ["ap@acme-customer.com"],
"subject": "Your order ORD-2026-000042"
}
Errors:
| Status | Body | Cause |
|---|---|---|
400 | NO_RECIPIENT | No to was given and neither the billing account nor the customer has a contact email. |
400 | TEMPLATE_INVALID | Same template validation as the download endpoint. |
404 | ORDER_NOT_FOUND / TEMPLATE_NOT_FOUND | No order or template with that code/name. |
409 | — | The order is CANCELED. |
Auto-generate or auto-send on order create/update
POST /api/v1/orders and PUT /api/v1/orders/{code} accept two additional
booleans on processingOptions, alongside the existing runBilling:
| Field | Type | Description |
|---|---|---|
generatePdf | boolean | Render the order document and store it under the tenant's orders/pdf storage. |
sendByEmail | boolean | Render the order document and email it, using the same recipient/subject defaults as POST /orders/{code}/document/email. |
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"orderType": "NEW",
"orderOffers": [ ... ],
"processingOptions": {
"runBilling": false,
"generatePdf": true,
"sendByEmail": true
}
}' \
https://acme.billerang.com/api/v1/orders
Both run after the order is created/validated on POST (or after the
DRAFT edit on PUT), and both are failure-tolerant: same contract as
processingOptions.runBilling. A document rendering or email delivery
failure is logged but never fails the order create/update call itself — the
order operation always reflects only its own outcome.
Order document templates
Order documents are rendered through the same billing template resource
used for invoices (/api/v1/billingTemplates), distinguished by a
documentKind field.
POST /api/v1/billingTemplates (multipart upload) accepts:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
documentKind | string, enum INVOICE|ORDER | No | INVOICE | The business document this template renders. ORDER requires renderingMode=HTML; uploading an ORDER template with renderingMode=JASPER (or omitted, since JASPER is the rendering-mode default) returns 400. |
BillingTemplateV1 (the read/list response) includes documentKind on
every template, so a caller can tell invoice and order templates apart
without inspecting the file bundle.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-F "templateName=commercial_order_fr" \
-F "description=French commercial order confirmation" \
-F "renderingMode=HTML" \
-F "documentKind=ORDER" \
-F "htmlBody=@invoice.html" \
-F "cssBody=@styles.css" \
"https://acme.billerang.com/api/v1/billingTemplates"
ORDER templates use the same HTML bundle layout as HTML invoice templates:
an invoice.html entry file, styles.css, and optional i18n/*.properties
translation files. There is no separate entry-file naming for order
documents; documentKind alone decides how the bundle is interpreted at
render time.
ORDER template merge model
An HTML ORDER template is rendered with the following variables available:
| Variable | Description |
|---|---|
${order} | The CommercialOrder entity: code, orderNumber, orderDate, activationDate, status, description. |
${customerName} / ${customerCode} | The order's customer. |
${billingAccountName} / ${billingAccountCode} / ${billingAccountEmail} | The order's billing account. |
${sellerName} | The seller issuing the order. |
${offers} | List of {offerLabel, subscriptionCode, products: [{productLabel, quantity}]}, one entry per order offer line. |
${lines} | List of {label, chargeCode, type, quantity, amountWithoutTax}. type is one of ONE_TIME, RECURRING, USAGE, INVOICING_PLAN. |
${totals} | {oneTime, recurring, tax, withTax, currency}, the aggregated order amounts. |
${generatedOn} | Timestamp the document was rendered. |
${i18n} | Translation helper, resolved against the template's i18n/*.properties bundle for the requested locale. |
${baseUrl} | Base URL for resolving relative asset references (images, fonts) from the template bundle. |
This mirrors the invoice HTML template's merge model in shape (entity plus
computed totals plus an ${i18n} helper), but with order-specific fields:
${offers}/${lines} in place of invoice lines, and no payment or due-date
block, since an order document is a confirmation, not a bill.
See also
- API principles for the update-semantics,
idempotency, and error-model conventions shared by every v1 resource,
including
processingOptions.runBilling. - Catalog to first invoice quickstart for the full order lifecycle, run live, from offer to validated invoice.
- The full request/response schemas
(
CommercialOrderV1Dto,ProcessingOptionsV1Dto,OrderDocumentEmailV1Dto,BillingTemplateUploadFormV1) are in the Interactive API Explorer.