Receiving inbound messages
Get notified when an inbound message is ready, fetch its documents, and confirm that your system has stored them.
When Unimaze receives a message for your participant, for example an invoice from a supplier, it validates the message and holds it for your system. Your system then does three things:
- Gets notified that the message is ready, through a webhook.
- Fetches the documents, as JSON or in their own format.
- Confirms the outcome, so the message is no longer pending.
Webhooks are the recommended way to learn about new messages. Polling is the legacy way, described in Polling (legacy) at the end of this page.
sequenceDiagram
participant U as Unimaze
participant W as Your webhook endpoint
participant E as Your back-end (ERP)
U->>W: POST deliveryNotified (inbound, signed)
W-->>U: 204 No Content (within 5 seconds)
W->>E: Queue for processing
E->>U: Fetch the document (XML or JSON)
U-->>E: Invoice
E->>E: Store the invoice
E->>U: POST /api/v1/messages/{messageId}/mark-as-delivered
All paths on this page are relative to your environment's base URL, for example https://mapi-sandbox.unimaze.com for the sandbox.
1. Get notified
Set up a webhook endpoint that receives the Inbound messages event category. When a message is validated and all its documents are stored, its status is pendingdelivery, and Unimaze sends your endpoint a deliveryNotified event with transfer set to inbound.
Verify the signature, store or queue the event, and respond with 2xx within 5 seconds. Fetch and import the documents in the background.
The event names the message in messageId. Its data is the message envelope, the same object that GET /api/v1/messages/{messageId} returns. A shortened example:
{
"eventId": "7d1f3a52-0c4e-4b8e-9a6d-2f1e5b7c9a10",
"action": "deliveryNotified",
"transfer": "inbound",
"occurredAt": "2026-10-02T09:15:04.120Z",
"test": false,
"party": "0196:5504030000",
"messageId": "6cfa5c24-25c5-4eb4-bed5-3ed6cc6c25c5",
"data": {
"uniqueId": "6cfa5c24-25c5-4eb4-bed5-3ed6cc6c25c5",
"timeReceived": "2026-10-02T09:15:02.870Z",
"documentNo": "INV-2026-0187",
"status": "pendingdelivery",
"transfer": "inbound",
"taxReportingStatus": "none",
"lifecycleStatus": "none",
"action": "SubmitInvoice",
"originParty": { "identifier": "0196:6511230889", "name": "Example Supplier ehf.", "countryCode": "IS" },
"destinationParty": { "identifier": "0196:5504030000", "name": "Example Buyer hf.", "countryCode": "IS" },
"documents": [
{
"index": 0, "name": "Invoice", "contentType": "application/xml", "attachmentType": "Core", "isTransformed": false,
"transaction": {
"transactionKeyName": "BISENUBL-3.0 SubmitInvoice",
"profileIdentifier": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0",
"customizationIdentifier": "urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0",
"documentIdentifier": "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0::2.1"
}
},
{ "index": 1, "name": "INV-2026-0187.pdf", "contentType": "application/pdf", "attachmentType": "Embedded", "isTransformed": false }
]
}
}data.action names the business transaction, for example SubmitInvoice or CorrectWithCredit. Use it to choose the fetch call in the next step.
2. Fetch the documents
To import structured data, fetch the document as JSON with the call for its transaction:
The JSON is the same whatever document specification the sender used, so your import doesn't depend on your business partners' formats.
To get a document in its own format, such as the UBL XML or a PDF attachment, use the documents calls:
GET /api/v1/messages/{messageId}/documents/corereturns the core business document.GET /api/v1/messages/{messageId}/documents/{index}returns a document by itsindexindata.documents. The core document is always index0.GET /api/v1/messages/{messageId}/documents/{name}returns a document by itsname.
If Unimaze transformed the core document into a specification your system can process, /documents/transformed returns the transformed document, and /documents/original returns it as the sender sent it.
If you import the JSON from the transaction calls above, data.action (for example SubmitInvoice) is all you need to know what you get. If you parse a document in its own format, read its transaction in data.documents to know which standard it follows: the profile, customization and document type identifiers. It is set on the Core document and, after a transformation, on the Original document. The Original document has the transaction the sender used, and the transformed Core document the one it was transformed to.
3. Confirm the outcome
Responding 2xx to the webhook never changes the message. When your system has safely stored the message, confirm it with one of these calls:
| Outcome | Call | New status |
|---|---|---|
| Stored | POST /api/v1/messages/{messageId}/mark-as-delivered | delivered |
| Can't be imported | POST /api/v1/messages/{messageId}/mark-as-failed?errorMessage={reason} | failed |
- Both calls work while the message is
pendingdelivery, andmark-as-failedalso while it ispendingprocessing. In any other status they return422. errorMessageis required. Say why the import failed, so the problem can be fixed.deliveredandfailedare final: the message changes again only if someone retries it manually.- Your change also sends a
messageStatusChangedevent, unless that is switched off for the routing rule.
Daily safety-net check
Each notification is sent once, and retried only if your endpoint doesn't respond. If your endpoint accepted a webhook but the message was never confirmed, for example because your system stopped, the message stays pendingdelivery.
Once a day, list these messages with GET /api/v1/messages?status=pendingdelivery&transfer=inbound, and fetch and confirm each one as in steps 2 and 3.
Special cases
- Replies to messages you sent. Message level responses (MLRs and eMLRs) arrive as inbound messages. They wait in
pendingdeliverylike any other message: fetch them with/documents/coreand mark them as delivered. See Working with MLRs, eMLRs and MLAs. - A processing step before import. If your participant has a processing step, for example an approval system, a new message first waits in
pendingprocessing, and the processing system gets aprocessingNotifiedevent in the Inbound processing notifications category. When the processing system callsPOST /api/v1/messages/{messageId}/mark-as-processed, the message becomespendingdeliveryanddeliveryNotifiedis sent. Until then, your ERP knows about the message only from itscreatedevent: note themessageId, and import the documents ondeliveryNotified. See Processing step. - Import queue. If your participant has an import queue, messages waiting to be imported are announced with
importNotifiedand confirmed withmark-as-imported. See Pending import messages.
Polling (legacy)
Polling is supported for existing integrations; new integrations should use webhooks.
To poll, call GET /api/v1/messages?status=pendingdelivery&transfer=inbound on a schedule. By default it returns the 50 most recent pending messages; use offset and limit to page through more. Then fetch and confirm each message as in steps 2 and 3.
To move from polling to webhooks, see Polling (legacy).
Updated about 1 hour ago
