Webhooks
Webhooks allow you to receive real-time HTTP notifications when invoice statuses change or connections are updated. Instead of polling the API, register a webhook URL and Mandato will POST events to it.
Each account has a single webhook configuration. Events are signed with HMAC-SHA256 so you can verify their authenticity.
Webhook configuration object
Section titled “Webhook configuration object”When retrieving the current webhook configuration, the secret is masked:
{ "url": "https://your-app.com/webhooks/mandato", "secret": "wh_***", "events": [ "invoice.accepted", "invoice.rejected", "invoice.error", "connection.expired" ]}Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
url | string|null | The URL where events are delivered |
secret | string|null | Webhook signing secret (masked after creation) |
events | array|null | Event types to subscribe to (null = all events) |
Webhook creation response
Section titled “Webhook creation response”When creating or updating a webhook, the full secret is returned once:
{ "url": "https://your-app.com/webhooks/mandato", "secret": "wh_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6", "events": [ "invoice.accepted", "invoice.rejected", "invoice.error", "connection.expired" ]}Register a webhook
Section titled “Register a webhook”POST /v1/webhooksRegisters or updates the webhook URL for your account. If a webhook already exists, it is replaced. A new signing secret is generated each time.
Request body
Section titled “Request body”| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | HTTPS URL to receive webhook events |
events | array | No | Event types to subscribe to. Omit to receive all events. |
Available event types
Section titled “Available event types”| Event | Description |
|---|---|
invoice.created | Invoice was created and queued |
invoice.validated | Invoice passed validation |
invoice.submitted | Invoice was submitted to the government system |
invoice.accepted | Government accepted the invoice |
invoice.rejected | Government rejected the invoice |
invoice.error | Processing error occurred |
connection.active | Government connection became active |
connection.expired | Government connection token expired |
Example request
Section titled “Example request”curl -X POST https://api.getmandato.dev/v1/webhooks \ -H "Authorization: Bearer sk_test_your_key" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-app.com/webhooks/mandato", "events": [ "invoice.accepted", "invoice.rejected", "invoice.error", "connection.expired" ] }'Example response (201 Created)
Section titled “Example response (201 Created)”{ "data": { "url": "https://your-app.com/webhooks/mandato", "secret": "wh_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6", "events": [ "invoice.accepted", "invoice.rejected", "invoice.error", "connection.expired" ] }}Error responses
Section titled “Error responses”| Status | Type | Description |
|---|---|---|
400 | validation_error | Invalid URL format or unknown event types |
Get webhook configuration
Section titled “Get webhook configuration”GET /v1/webhooksReturns the current webhook configuration. The signing secret is masked.
Example request
Section titled “Example request”curl https://api.getmandato.dev/v1/webhooks \ -H "Authorization: Bearer sk_test_your_key"Example response
Section titled “Example response”{ "data": { "url": "https://your-app.com/webhooks/mandato", "secret": "wh_***", "events": [ "invoice.accepted", "invoice.rejected", "invoice.error", "connection.expired" ] }}If no webhook is configured, all fields are null:
{ "data": { "url": null, "secret": null, "events": null }}Delete webhook
Section titled “Delete webhook”DELETE /v1/webhooksRemoves the webhook configuration. No more events will be delivered until a new webhook is registered.
Example request
Section titled “Example request”curl -X DELETE https://api.getmandato.dev/v1/webhooks \ -H "Authorization: Bearer sk_test_your_key"Example response
Section titled “Example response”{ "data": { "deleted": true }}Webhook delivery
Section titled “Webhook delivery”When an event occurs, Mandato sends an HTTP POST request to your webhook URL with the following format:
Headers
Section titled “Headers”| Header | Description |
|---|---|
Content-Type | application/json |
X-Mandato-Signature | HMAC-SHA256 signature of the request body |
X-Mandato-Event | Event type (e.g., invoice.accepted) |
X-Mandato-Delivery-Id | Unique delivery ID for deduplication |
Payload format
Section titled “Payload format”{ "id": "evt_a1b2c3d4e5f6", "type": "invoice.accepted", "createdAt": "2025-01-15T10:00:15.000Z", "data": { "id": "inv_a1b2c3d4e5f6", "country": "RO", "supplierVat": "RO12345678", "customerVat": "RO87654321", "status": "accepted", "netAmount": "5200.00", "vatAmount": "988.00", "grossAmount": "6188.00", "currency": "RON", "govId": "4523789012", "externalId": "order-12345" }}Retry behavior
Section titled “Retry behavior”If your endpoint returns a non-2xx status code or times out (30-second limit), Mandato retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 12 hours |
After 5 failed retries, the delivery is marked as failed. Failed deliveries are visible in the dashboard.
Signature verification
Section titled “Signature verification”Always verify the X-Mandato-Signature header before processing webhook events. See the Webhooks guide for implementation details and code examples.