Create and send UBL and PDF documents
Follow this workflow to create a document, add a UBL or PDF representation, upload the file using a presigned URL, validate the document, and send it through an active delivery channel.
Prerequisites
- A JSON payload containing the document metadata and required document information.
- PDF or UBL file.
- Banqup permissions.
- The customer connection has been created in the space and at least one delivery channel (connector) has been activated. See this recipe.
- Resource IDs:
Step 1: Create document
Step 1.1: Create document with JSON
Endpoint: documents/core/transaction/v2/spaces/{spaceId}/documents
Path parameter:
spaceId
Requires a JSON request body.
- Add the customer (connection) information in the JSON to skip the step of assigning it to the document.
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": "476786b9-348f-4b02-a260-b03ca2468b91"
}
],
"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"
}
]
},
"lines": [
{
"$schema": "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:
{
"timestamps": {
"createdAt": "2026-09-01T08:49:17.084785Z",
"updatedAt": "2026-09-01T08:49:17.084785Z"
},
"lines": [
{
"id": "94975979-dd51-4924-bd27-c8d5d476c133",
"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": "476786b9-348f-4b02-a260-b03ca2468b91"
}
],
"id": "l3ab7b1e-c9c7-4af3-8cf3-e6dc0ebc0823",
"spaceId": "l2a801ff-41d8-40c5-9911-035c0cce8b47",
"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
}
Step 1.2: Create document representation
Depending on the format you want to send, create either a UBL or PDF representation for the document.
Endpoint: /documents/core/transaction/v1/spaces/{spaceId}/documents/{documentId}/representations
Path parameters:
spaceIddocumentId
Requires a JSON request body.
Set the type field depending on your required format:
eu.peppol.bis3: Specifies Peppol BIS Billing 3.0 (UBL). Standard across the Peppol network (mandated in Belgium).com.unifiedpost.btx.pdf: Specifies a standard PDF document.
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 '{
"fileSize": 32184,
"retentionTime": "2029-01-14T19:58:47+02:00",
"type": "eu.peppol.bis3",
"filename": "sales_invoice.xml",
"contentType": "application/xml",
"main": true,
"original": true
}'
Successful response:
{
"id": "9007f1ed-cd3a-41ca-a4a0-be56d7fde73b",
"documentId": "l3ab7b1e-c9c7-4af3-8cf3-e6dc0ebc0823",
"retentionTime": "2029-01-14T19:58:47+02:00",
"type": "eu.peppol.bis3",
"filename": "sales_invoice.xml",
"contentType": "application/xml",
"size": 32184,
"metadata": {
"trace-context": "8e5d18f67230c912e7fd2c3074de7e15-133ee5ea398adc49-0"
},
"status": "AWAITING_UPLOAD",
"main": true,
"uploadUrl": "https://storage.googleapis.com/gcs-hjctqakd-eu-datastore-dev/penc9c7-4af3-8cf3-e6dc0ebc0823/5007f1ed-cd3a-41ca-a4a0-be56d7fde73b?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=sw-btxdata-a8cd63-dev-eu%40pj-bu-btxdata-dev-eu.iam.gserviceaccount.com%2F20260901%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20260901T085159Z&X-Goog-Expires=3600&X-Goog-SignedHeaders=content-type%3Bhost&X-Goog-Signature=3c92138aa34387023e5dbc22e303c70e473b490e11823d52a7b9b09c5a17d2ce2cc0933fd94edcb2a442cd3cfd0b900443886010111bea5ed0c94011ffe3acb633da49ac0bea1137cf80214a8528c1f25780fef979116743390ae6e78d38904b31ae2a0affe6989b1a0f594a7d5be6d4d3eeb7bca8c715e1dacfc2610f030a1295bc54d3f5ef4c0a9eda30d57eae4cf665aa7b5ef8c00e6810b453a8633149109ba08f18482b0419caeda66b5f0b494e9b89f3df533f898246618b62a0af51415fd09b58e097ddc7ab281a30bf392fa7ef5dc36496018bbdadc0abdcb84dbc0729575bee1eeaee6f5a5493f7d91fb38499893108d2f898bb263b98510260b42b",
"createdAt": "2026-09-01T08:51:59.557902Z",
"updatedAt": "2026-09-01T08:51:59.557902Z",
"original": true
}
Step 1.3: Upload file
Endpoint: /uploadUrl (returned in the response of the previous step).
The uploadUrl is a presigned URL in GCP (Google Cloud Platform) which allows users to temporarily and securely access bucket resources for upload.
Request body:
- Binary file content. Use
application/xmlfor a UBL file orapplication/pdffor a PDF file. - The following example uploads a UBL XML file. For a PDF representation, use Content-Type:
application/pdfand upload the corresponding PDF file.
To prevent 403 Forbidden or 400 Bad Request errors from the uploadUrl, the Content-Type in the header must match the one used when creating the document representation.
curl -L -X PUT "$UPLOAD_URL" \
-H 'Content-Type: application/xml' \
--data-binary '@sales_invoice.xml'
Successful response: The file is uploaded successfully.
The generated uploadUrl is time-limited. Delayed background jobs or uploads outside its validity window will fail at the storage layer. Do not store URLs for deferred processing—always request a fresh URL immediately before initiating the file transfer.
Step 1.4: Assign customer to document (optional)
If you didn't add the customer connection when creating the document, assign it to the document in this step. Otherwise, skip to Step 2.
Endpoint: /documents/core/transaction/v2/spaces/{spaceId}/documents/{documentId}:assignConnection
Path parameter:
spaceIddocumentId
Requires a JSON request body.
Add the customer's connection ID in the request body.
curl -L 'https://{{serverURL}}/documents/core/transaction/v2/spaces/{spaceId}/documents/{documentId}:assignConnection' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"id": "476786b9-348f-4b02-a260-b03ca2468b91",
"relationType": "CUSTOMER"
}'
Successful response: The customer is assigned successfully to the document.
Step 2: Validate document
Endpoint: /connectors/core/v1/spaces/{spaceId}/documents/{documentId}:validate
Path parameter:
spaceIddocumentId
Requires a JSON request body.
dryRun=true: Validates the document and saves results without altering document status or triggering lifecycle/space events.dryRun=false: Runs full validation, transitions status to VALIDATED, and triggers all lifecycle and space events. (Returns 202 immediately if already VALIDATED.)
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 request was successfully accepted into the queue - it does not confirm that the document was validated.
- If you need to check the document status, use this endpoint.
Step 3: Send document
Endpoint: /documents/core/v2/spaces/{spaceId}/documents/{documentId}:send
Path parameter:
spaceIddocumentId
- The request defaults to stored identifiers and the first viable connector (unless
connectorOverrideis set). - Reprocessing triggers automatically if the confirmed participant ID differs from the stored ID.
curl -L -X POST 'https://{{serverURL}}/documents/core/v2/spaces/{spaceId}/documents/{documentId}:send' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d ''
Successful response:
- 202 Accepted indicates the delivery request was successfully accepted into the queue - it does not confirm that the document was delivered.
- Final delivery status, failures, and updates must be monitored asynchronously through document lifecycle events.