Create and send sales invoices via JSON
This recipe demonstrates how to create, validate, and deliver sales invoices using JSON metadata, attach file representations (Factur-X hybrid PDF, Peppol BIS 3.0 UBL XML, or standard PDF), apply line-level discounts, and dispatch the document through an active connector.
Prerequisites
- A JSON payload containing invoice metadata, customer connections, and line items.
- The invoice file:
- Factur-X: A hybrid PDF containing embedded XML conforming to EN 16931 (France).
- Peppol UBL: An XML file complying with Peppol BIS Billing 3.0 (Belgium / Cross-border).
- PDF: A standard visual PDF.
- Banqup permissions.
- An active customer connection and activated delivery connector (see Create partner delivery channel).
- Resource IDs:
Step 1: Create document
Step 1.1: Create document with JSON
Endpoint: /documents/core/transaction/v2/spaces/{spaceId}/documents
Path parameter:
spaceId
- Assign customer inline: Include the customer connection directly in
connections[]to skip the separate customer assignment step. - Line-level discounts: Add an
allowancesarray inside a line'smetadataobject (as shown below) to specify item discounts.
curl -L -X POST 'https://{{serverURL}}/documents/core/transaction/v2/spaces/{spaceId}/documents' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"connections": [
{
"displayName": "Coffee Corner Shop",
"relationType": "CUSTOMER",
"id": "ae21ce68-9f98-4302-9a0c-cad78956e98h"
}
],
"folder": "SALES",
"status": {
"pay": "UNPAID",
"document": "AWAITING_VALIDATION"
},
"category": "INVOICE",
"metadata": {
"$schema": "[https://btx.unifiedpost.com/btx/datastore/document/document_metadata/v1/document-metadata-schema.json](https://btx.unifiedpost.com/btx/datastore/document/document_metadata/v1/document-metadata-schema.json)",
"documentNumber": "ABC250226",
"issueDate": "2026-08-01",
"documentTotals": {
"linesNetAmount": "200.00",
"netAmount": "200.00",
"vatAmount": "40.00",
"grossAmount": "240.00",
"payableAmount": "240.00"
},
"vatDetails": [
{
"netAmount": "200.00",
"vatAmount": "40.00",
"vatPercentage": "20.00",
"vatCategory": "S"
}
]
},
"lines": [
{
"$schema": "[https://btx.unifiedpost.com/btx/datastore/document/document_line/v1/document-line-schema.json](https://btx.unifiedpost.com/btx/datastore/document/document_line/v1/document-line-schema.json)",
"quantity": "1",
"unit": "EA",
"netAmount": "200.00",
"currency": "EUR",
"metadata": {
"allowances": [
{
"netAmount": "50.00"
}
]
},
"vatDetails": {
"vatCategory": "S",
"vatPercentage": "20"
},
"pricing": {
"itemNetPrice": "250.00",
"itemPriceQuantity": "1"
},
"productIdentifiers": {
"itemId": "SERV-001",
"itemDescription": "Coffee beans"
}
}
],
"retentionTime": "2029-01-14T19:58:47+02:00"
}'
Successful response (excerpt):
{
"timestamps": {
"createdAt": "2026-11-12T06:21:11.618409Z",
"updatedAt": "2026-11-12T06:21:11.618409Z"
},
"lines": [
{
"id": "01a1ce6a-da42-7391-a8f5-a2c141545de5",
"sequence": 1,
"data": {
"productIdentifiers": {
"itemId": "SERV-001",
"itemDescription": "Coffee beans"
},
"unit": "EA",
"metadata": {
"allowances": [
{
"netAmount": "50.00"
}
]
},
"quantity": "1",
"$schema": "https://btx.unifiedpost.com/btx/datastore/document/document_line/v1/document-line-schema.json",
"netAmount": "200.00",
"currency": "EUR",
"vatDetails": {
"vatCategory": "S",
"vatPercentage": "20"
},
"pricing": {
"itemNetPrice": "250.00",
"itemPriceQuantity": "1"
}
}
}
],
"customCategories": {},
"connections": [
{
"displayName": "Coffee Corner Shop",
"relationType": "CUSTOMER",
"id": "ae21ce68-9f98-4302-9a0c-cad78956e98h"
}
],
"id": "01a1ce6a-da42-7391-a8f5-a2c141545de2",
"spaceId": "8a494c33-7476-48f8-91f3-9839507bf042",
"folder": "SALES",
"status": {
"pay": "UNPAID",
"document": "AWAITING_VALIDATION"
},
"category": "INVOICE",
"metadata": {
"$schema": "https://btx.unifiedpost.com/btx/datastore/document/document_metadata/v1/document-metadata-schema.json",
"documentNumber": "ABC250226",
"issueDate": "2026-08-01",
"documentTotals": {
"linesNetAmount": "200.00",
"netAmount": "200.00",
"vatAmount": "40.00",
"grossAmount": "240.00",
"payableAmount": "240.00"
},
"vatDetails": [
{
"netAmount": "200.00",
"vatAmount": "40.00",
"vatPercentage": "20.00",
"vatCategory": "S"
}
]
},
"tags": [],
"retentionTime": "2029-01-14T17:58:47Z",
"readOnly": false,
"hasComments": false
}
Step 1.2: Create document representation
Endpoint: /documents/core/transaction/v1/spaces/{spaceId}/documents/{documentId}/representations
Path parameters:
spaceIddocumentId
The 'fileSize' must match the size of the file exactly.
- Factur-X (France)
- Peppol BIS 3.0 UBL
- Standard PDF
curl -L -X POST 'https://{{serverURL}}/documents/core/transaction/v1/spaces/{spaceId}/documents/{documentId}/representations' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"type": "fr.ctc:factur-x-en16931",
"filename": "invoice_facturx.pdf",
"contentType": "application/pdf",
"fileSize": 32184,
"main": true,
"original": true,
"retentionTime": "2029-01-14T19:58:47+02:00"
}'
Successful response:
{
"id": "01a1ce6e-325c-75f6-8ca6-1f67ddd3f03f",
"documentId": "01a1ce6a-da42-7391-a8f5-a2c141545de2",
"retentionTime": "2029-01-14T17:58:47Z",
"type": "fr.ctc:factur-x-en16931",
"filename": "invoice_facturx.pdf",
"contentType": "application/pdf",
"size": 32184,
"metadata": {
"trace-context": "419b2947a919e8e7ec6fb786d14031b6-9429d2bd353d3df0-0"
},
"status": "AWAITING_UPLOAD",
"main": true,
"uploadUrl": "<PRESIGNED_UPLOAD_URL>",
"createdAt": "2026-11-12T06:24:50.780964Z",
"updatedAt": "2026-11-12T06:35:57.334756Z",
"original": true
}
curl -L -X POST 'https://{{serverURL}}/documents/core/transaction/v1/spaces/{spaceId}/documents/{documentId}/representations' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"type": "eu.peppol.bis3",
"filename": "invoice_ubl.xml",
"contentType": "application/xml",
"fileSize": 32184,
"main": true,
"original": true,
"retentionTime": "2029-01-14T19:58:47+02:00"
}'
Successful response:
{
"id": "7eacc03f-c351-4e6d-9172-57f26ee0bbfe",
"documentId": "af239b0f-0e86-45de-ba78-38700b16c9b7",
"retentionTime": "2029-01-14T17:58:47Z",
"type": "eu.peppol.bis3",
"filename": "invoice_ubl.xml",
"contentType": "application/xml",
"size": 32184,
"metadata": {
"trace-context": "919b2947a919e8e7ec6fb786d14031b6-9429d2bd353d3df0-0"
},
"status": "AWAITING_UPLOAD",
"main": true,
"uploadUrl": "<PRESIGNED_UPLOAD_URL>",
"createdAt": "2026-11-12T06:24:50.780964Z",
"updatedAt": "2026-11-12T06:35:57.334756Z",
"original": true
}
curl -L -X POST 'https://{{serverURL}}/documents/core/transaction/v1/spaces/{spaceId}/documents/{documentId}/representations' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"type": "com.unifiedpost.btx.pdf",
"filename": "invoice.pdf",
"contentType": "application/pdf",
"fileSize": 32184,
"main": true,
"original": true,
"retentionTime": "2029-01-14T19:58:47+02:00"
}'
Successful response:
{
"id": "387e4970-6e39-407b-8e1d-db4d8d5f6939",
"documentId": "d2e25bb5-cb81-4b80-9ac6-95c0ed80a121",
"retentionTime": "2029-01-14T17:58:47Z",
"type": "com.unifiedpost.btx.pdf",
"filename": "invoice.pdf",
"contentType": "application/pdf",
"size": 32184,
"metadata": {
"trace-context": "519b2947a919e8e7ec6fb786d14031b6-9429d2bd353d3df0-0"
},
"status": "AWAITING_UPLOAD",
"main": true,
"uploadUrl": "<PRESIGNED_UPLOAD_URL>",
"createdAt": "2026-11-12T06:24:50.780964Z",
"updatedAt": "2026-11-12T06:35:57.334756Z",
"original": true
}
Step 1.3: Upload file binary
Endpoint: uploadUrl (presigned URL returned in Step 1.2)
- The
Content-Typeheader must match thecontentTypeconfigured in Step 1.2 (application/pdforapplication/xml). Mismatches will result in400or403errors from the storage provider. - Presigned URLs are time-limited. Initiate the binary upload immediately after requesting the representation.
- PDF / Factur-X
- UBL XML
curl -L -X PUT "$UPLOAD_URL" \
-H 'Content-Type: application/pdf' \
--data-binary '@invoice.pdf'
curl -L -X PUT "$UPLOAD_URL" \
-H 'Content-Type: application/xml' \
--data-binary '@invoice.xml'
Successful response: The file binary is uploaded successfully.
Step 1.4: Assign customer (optional)
If the customer connection was already declared in the document payload during Step 1.1, skip to Step 2.
Endpoint: /documents/core/transaction/v2/spaces/{spaceId}/documents/{documentId}:assignConnection
Path parameters:
spaceIddocumentId
curl -L -X POST 'https://{{serverURL}}/documents/core/transaction/v2/spaces/{spaceId}/documents/{documentId}:assignConnection' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"id": "ae21ce68-9f98-4302-9a0c-cad78956e98h",
"relationType": "CUSTOMER"
}'
Successful response: Customer connection assigned to the document.
Step 2: Validate document
Endpoint: /connectors/core/v1/spaces/{spaceId}/documents/{documentId}:validate
Path parameters:
spaceIddocumentId
dryRun: true: Validates syntax and rules without changing document status or emitting workflow events.dryRun: false: Performs full validation, moves document status toVALIDATED, and triggers space lifecycle events.
curl -L -X POST 'https://{{serverURL}}/connectors/core/v1/spaces/{spaceId}/documents/{documentId}:validate' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"dryRun": false
}'
Successful response: 202 Accepted indicates the validation task is queued. Wait until document validation is completed successfully before sending the document.
Validation check:
To inspect validation results and validator statuses, retrieve the document with include=validationData:
curl -L 'https://{{serverURL}}/documents/core/transaction/v2/spaces/{spaceId}/documents/{documentId}?include=validationData' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN'
Successful response:
{
"validationData": {
"validated": true,
"spaceExtensions": {
"com.unifiedpost.btx.payments.validators": {
"sddMandate": {
"status": "COMPLETED",
"validationId": "029494d4-1749-48cb-9377-04d857c2d54p"
}
},
"com.unifiedpost.btx.documents.validators": {
"general": {
"status": "COMPLETED",
"validationId": "029494d4-1749-48cb-9377-04d857c2d54p"
}
},
"com.unifiedpost.btx.connectors.validators": {
"peppol": {
"status": "COMPLETED",
"validationId": "029494d4-1749-48cb-9377-04d857c2d54p"
},
"dataExtraction": {
"status": "COMPLETED",
"validationId": "029494d4-1749-48cb-9377-04d857c2d54p"
}
}
}
}
}
Step 3: Send document
Endpoint: /documents/core/v2/spaces/{spaceId}/documents/{documentId}:send
Path parameters:
spaceIddocumentId
The send action automatically resolves the recipient's primary activated connector (for instance, Peppol takes precedence over Email if both are configured). Pass an optional JSON body with connectorOverride if routing must be explicitly designated.
curl -L -X POST 'https://{{serverURL}}/documents/core/v2/spaces/{spaceId}/documents/{documentId}:send' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d ''
Successful response: 202 Accepted confirms the transmission job is queued. Track status updates, dispatch confirmations, and connector receipts asynchronously through document lifecycle webhooks.