Skip to content

Order canceled

The carrier canceled the order, including an order that was already fulfilled (its numbers are withdrawn). status is error and error_message carries the reason.

Delivery contract (all events):

  • We send a POST with a JSON body to the webhook URL you gave on POST /numbers/orders. The body is the order exactly as GET /numbers/orders/{id} returns it, plus event, delivery_id and, on document events, kyc_document. The body status is the order's status at the time of the event, not necessarily its current one.
  • Answer with any 2xx status to confirm. Anything else (or no answer within 10 seconds) is retried, up to 6 attempts in total (the first, then retries after 3 minutes, 10 minutes, 1 hour, 6 hours and 24 hours), then the delivery is marked failed (it can be retried from Cockpit). Redirects are not followed.
  • Delivery is at-least-once: dedupe on X-Voxbi-Delivery.
  • Events can arrive out of order (a retry of an older event can land after a newer one). Use updated_at and the body status, or fetch GET /numbers/orders/{id}, when order matters.
  • Verify X-Voxbi-Signature before trusting the body.
No authentication required
X-Voxbi-Event header · string *
The event this delivery is about, for example `order.fulfilled`. Same value as `event` in the body. Use it to route the request before parsing the body.
enum: order.fulfilled order.failed order.canceled kyc.document_accepted kyc.document_rejected kyc.validated
X-Voxbi-Delivery header · string · uuid *
Unique id of this delivery (a UUID). Every retry of the same delivery carries the same value, so store it and ignore a delivery you have already processed: delivery is at-least-once. Same value as `delivery_id` in the body.
X-Voxbi-Signature header · string *
Proves the request comes from Voxbi and the body was not altered. Format: `t=<unix timestamp>,v1=<hex>[,v1=<hex>]`. `<hex>` is the lowercase hex HMAC-SHA256 of the string `<t>.<raw body>` (the timestamp, a dot, then the exact bytes of the request body) keyed with your webhook signing secret (it starts with `whsec_`). Right after you rotate the secret, the header carries two `v1` values for 24 hours (one per valid secret): accept the request if ANY of them matches. To verify: 1. Read the raw request body BEFORE parsing the JSON. 2. Split the header on `,`; take `t` and every `v1`. 3. Compute `HMAC_SHA256(secret, t + "." + raw_body)` as hex. 4. Compare it to each `v1` with a constant-time comparison. No match: reject with `401`. 5. Optionally reject when `t` is more than 5 minutes from your clock, to stop replays. Pseudo-code: `expected = hex(hmac_sha256(secret, t + "." + raw_body))`. The secret is managed in Cockpit under Settings, API keys and secrets, Webhook signing secret, where you can reveal and rotate it.

Request body · required · application/json

Request schema
allOf
idstring · uuid
Internal order id assigned by us. Use this with `GET /numbers/orders/{id}` to fetch the latest state, and `POST /numbers/orders/{id}/documents` to send KYC documents. Also the `id` in every webhook payload (the webhook body is this same object). Your own reference is `custom_order_reference`.
example: 550e8400-e29b-41d4-a716-446655440000
order_idstring | null
DEPRECATED: same value as `custom_order_reference`, kept for existing integrations. Read `custom_order_reference` instead.
length: 0–64deprecated
example: po-2026-00042
custom_order_referencestring | null
Your own reference for the order, supplied as `custom_order_reference` (or the deprecated `order_id`) on `POST /numbers/orders`. Echoed back here, in `GET /numbers/orders` (filterable by it) and in every webhook payload. If you sent none, the carrier's generated one is backfilled here.
length: 0–64
example: po-2026-00042
order_referencestring | null · uuid
The carrier's own identifier for this order (a UUID they assign). Null until the order is placed. This is the reference the carrier says to use to identify the order, and it is distinct from `custom_order_reference` (your own reference, opaque to the carrier). Not settable by you.
example: 01a0ae76-0000-4000-8000-000000000000
customer_namestring
Legal name of the customer the numbers were ordered for, as supplied on create (and editable on retry).
example: ACME GmbH
statusstring
Lifecycle state of this order. * `requested`: sent upstream, awaiting fulfilment. * `submission_failed`: rejected during synchronous submission; editable + retryable. * `processing`: async success callback received; provisioning in progress. * `success`: provisioning complete; `numbers` is populated. * `success_kyc_required`: the numbers are provisioned (`numbers` is populated) but the carrier has not validated the KYC yet; see `kyc`. Moves to `success` once the KYC is validated. * `error`: async rejection; terminal. * `processing_failed`: internal processing exhausted retries; admin-only retry. * `canceled`: a reseller closed a `submission_failed` order that provably never reached the carrier; terminal. Nothing was canceled at the carrier and no webhook is sent.
enum: created requested submission_failed processing success success_kyc_required error processing_failed canceled
example: requested
addressobject
streetstring
example: Adenauerallee
street_numberstring
example: 1
postal_codestring
example: 53113
citystring
example: Bonn
area_codestring | null
Country-conditional (e.g. set for CH); null otherwise.
countrystring
example: DE
formatted_addressstring | null
Single-line, comma-separated address derived from the persisted components (street + number, postal code + city, country). Empty components are dropped.
example: Adenauerallee 1, 53113 Bonn, DE
quantityinteger
example: 1
prefixstring | null
Dial prefix in E.164 (country + area code if applicable).
example: +49228
numbersarray<string>
E.164-formatted numbers assigned to this order. Empty (`[]`) until the order reaches `success` - they are never returned synchronously by `POST /numbers/orders` (which only ever returns `requested`); they arrive later via the webhook and then appear here on `GET /numbers/orders/{id}`.
[]string
phone_numbersarray<object>
The assigned numbers, in the same shape as `GET /numbers` (id, number, name, is_active, call_flow), so you can read each number's id and current call flow and act on it directly (e.g. `PUT /numbers/{id}/call-flow`). Populated once the order reaches `success`.
idstring · uuid
example: 550e8400-e29b-41d4-a716-446655440000
numberstring
Phone number in E.164 format.
example: +12125550100
namestring | null
example: Customer Support
is_activeboolean
example: 1
call_flow_idstring | null · uuid
example: 550e8400-e29b-41d4-a716-446655440100
call_flowobject | null
idstring · uuid
namestring
enabledboolean
error_messagestring | null
Short error reason when status is `error` or `submission_failed`.
kycobject
The order's KYC (know-your-customer) progress with the carrier. Present on every order, including orders placed with KYC deferred (`kyc_required_at_order: false` on `POST /numbers/orders/check`), where `missing` lists what is still to be sent via `POST /numbers/orders/{id}/documents`.
statestring | null
Overall KYC state. * `pending`: no verdict yet. * `validated`: the carrier signed the KYC off (`validated_at` is set). * `rejected`: not validated and at least one provided document is rejected. Null until the carrier has reported on this order.
enum: pending validated rejected null
example: pending
validated_atstring | null · date-time
When the carrier validated the KYC. Null until then.
providedarray<object>
Documents the carrier has on file, with their individual verdict.
Each item — One KYC document the carrier has on file for the order, with the carrier's verdict on it.
document_typestring | null
The KYC document type code (the `documentType` you uploaded it under).
example: utility_bill
statusstring | null
The carrier's verdict on this document. * `pending`: received, not reviewed yet. * `accepted`: reviewed and accepted. * `rejected`: reviewed and refused; see `rejection_reason` and upload a replacement. * `unknown`: the carrier reported a status we do not recognize.
enum: pending accepted rejected unknown null
example: accepted
rejection_reasonstring | null
Why the carrier refused the document. Null unless `status` is `rejected`.
created_atstring | null · date-time
When the carrier received the document (a replacement carries a new value).
example: 2026-05-12T11:15:03+00:00
updated_atstring | null · date-time
When the carrier last changed the document (for example reviewed it).
example: 2026-05-13T08:02:41+00:00
missingarray<object>
Documents still needed. Each entry is either an object (that exact document is needed) or an array of objects (provide any ONE of them), like `kycRequirements` on the check endpoint.
[]object
webhook_urlstring | null
The webhook URL we will POST the final outcome to.
webhook_statusstring
enum: not_required pending retrying delivered failed cancelled
webhook_next_retry_atstring | null · date-time
created_atstring · date-time
updated_atstring · date-time
event*string
What happened. Same value as the `X-Voxbi-Event` header.
enum: order.fulfilled order.failed order.canceled kyc.document_accepted kyc.document_rejected kyc.validated
example: order.fulfilled
delivery_id*string · uuid
Identifies this delivery; every retry of it carries the same value. Same value as the `X-Voxbi-Delivery` header. Use it to ignore duplicates.
example: 7c9e6679-7425-40de-944b-e07fc1f90ae7
kyc_documentobject
Only on `kyc.document_accepted` and `kyc.document_rejected`: the document the carrier just decided on.
document_typestring
example: utility_bill
statusstring
enum: accepted rejected
example: accepted
rejection_reasonstring | null
created_atstring | null · date-time
example: 2026-05-12T11:15:03+00:00
updated_atstring | null · date-time
example: 2026-05-13T08:02:41+00:00

Responses

Any `2xx` status confirms the delivery. Any other answer is retried on the schedule above, then the delivery is marked failed. The response body is ignored.
Response schema
No response body.
post order.canceled
Request sample
curl -X POST 'https://your-server.example.com/webhooks' \
  -H 'Content-Type: application/json' \
  --data '{"id":"550e8400-e29b-41d4-a716-446655440000","order_id":"po-2026-00042","custom_order_reference":"po-2026-00042","order_reference":"01a0ae76-0000-4000-8000-000000000000","customer_name":"ACME GmbH","status":"error","address":{"street":"Adenauerallee","street_number":"1","postal_code":"53113","city":"Bonn","area_code":null,"country":"DE"},"formatted_address":"Adenauerallee 1, 53113 Bonn, DE","quantity":1,"prefix":"+49228","numbers":[],"phone_numbers":[],"error_message":"Order canceled","kyc":{"state":"pending","validated_at":null,"provided":[],"missing":[[{"document_type":"business_registration_certificate","notes":"Handelsregister"},{"document_type":"utility_bill","notes":"Dated within 3 months"}],{"document_type":"id_document","notes":null}]},"webhook_url":"https://your-app.example/hooks/number-order","webhook_status":"pending","webhook_next_retry_at":null,"created_at":"2026-05-12T10:36:47+00:00","updated_at":"2026-05-14T07:30:00+00:00","event":"order.canceled","delivery_id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"}'
const response = await fetch('https://your-server.example.com/webhooks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "order_id": "po-2026-00042",
    "custom_order_reference": "po-2026-00042",
    "order_reference": "01a0ae76-0000-4000-8000-000000000000",
    "customer_name": "ACME GmbH",
    "status": "error",
    "address": {
        "street": "Adenauerallee",
        "street_number": "1",
        "postal_code": "53113",
        "city": "Bonn",
        "area_code": null,
        "country": "DE"
    },
    "formatted_address": "Adenauerallee 1, 53113 Bonn, DE",
    "quantity": 1,
    "prefix": "+49228",
    "numbers": [],
    "phone_numbers": [],
    "error_message": "Order canceled",
    "kyc": {
        "state": "pending",
        "validated_at": null,
        "provided": [],
        "missing": [
            [
                {
                    "document_type": "business_registration_certificate",
                    "notes": "Handelsregister"
                },
                {
                    "document_type": "utility_bill",
                    "notes": "Dated within 3 months"
                }
            ],
            {
                "document_type": "id_document",
                "notes": null
            }
        ]
    },
    "webhook_url": "https://your-app.example/hooks/number-order",
    "webhook_status": "pending",
    "webhook_next_retry_at": null,
    "created_at": "2026-05-12T10:36:47+00:00",
    "updated_at": "2026-05-14T07:30:00+00:00",
    "event": "order.canceled",
    "delivery_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
}),
});

const data = await response.json();
console.log(data);
import requests

response = requests.post('https://your-server.example.com/webhooks',
    json={
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "order_id": "po-2026-00042",
    "custom_order_reference": "po-2026-00042",
    "order_reference": "01a0ae76-0000-4000-8000-000000000000",
    "customer_name": "ACME GmbH",
    "status": "error",
    "address": {
        "street": "Adenauerallee",
        "street_number": "1",
        "postal_code": "53113",
        "city": "Bonn",
        "area_code": null,
        "country": "DE"
    },
    "formatted_address": "Adenauerallee 1, 53113 Bonn, DE",
    "quantity": 1,
    "prefix": "+49228",
    "numbers": [],
    "phone_numbers": [],
    "error_message": "Order canceled",
    "kyc": {
        "state": "pending",
        "validated_at": null,
        "provided": [],
        "missing": [
            [
                {
                    "document_type": "business_registration_certificate",
                    "notes": "Handelsregister"
                },
                {
                    "document_type": "utility_bill",
                    "notes": "Dated within 3 months"
                }
            ],
            {
                "document_type": "id_document",
                "notes": null
            }
        ]
    },
    "webhook_url": "https://your-app.example/hooks/number-order",
    "webhook_status": "pending",
    "webhook_next_retry_at": null,
    "created_at": "2026-05-12T10:36:47+00:00",
    "updated_at": "2026-05-14T07:30:00+00:00",
    "event": "order.canceled",
    "delivery_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
}
)
response.raise_for_status()
data = response.json()
print(data)
<?php
$context = stream_context_create([
    'http' => [
        'method'  => 'POST',
        'header'  => "Content-Type: application/json",
        'content' => '{
    \"id\": \"550e8400-e29b-41d4-a716-446655440000\",
    \"order_id\": \"po-2026-00042\",
    \"custom_order_reference\": \"po-2026-00042\",
    \"order_reference\": \"01a0ae76-0000-4000-8000-000000000000\",
    \"customer_name\": \"ACME GmbH\",
    \"status\": \"error\",
    \"address\": {
        \"street\": \"Adenauerallee\",
        \"street_number\": \"1\",
        \"postal_code\": \"53113\",
        \"city\": \"Bonn\",
        \"area_code\": null,
        \"country\": \"DE\"
    },
    \"formatted_address\": \"Adenauerallee 1, 53113 Bonn, DE\",
    \"quantity\": 1,
    \"prefix\": \"+49228\",
    \"numbers\": [],
    \"phone_numbers\": [],
    \"error_message\": \"Order canceled\",
    \"kyc\": {
        \"state\": \"pending\",
        \"validated_at\": null,
        \"provided\": [],
        \"missing\": [
            [
                {
                    \"document_type\": \"business_registration_certificate\",
                    \"notes\": \"Handelsregister\"
                },
                {
                    \"document_type\": \"utility_bill\",
                    \"notes\": \"Dated within 3 months\"
                }
            ],
            {
                \"document_type\": \"id_document\",
                \"notes\": null
            }
        ]
    },
    \"webhook_url\": \"https://your-app.example/hooks/number-order\",
    \"webhook_status\": \"pending\",
    \"webhook_next_retry_at\": null,
    \"created_at\": \"2026-05-12T10:36:47+00:00\",
    \"updated_at\": \"2026-05-14T07:30:00+00:00\",
    \"event\": \"order.canceled\",
    \"delivery_id\": \"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d\"
}',
    ],
]);

$response = file_get_contents('https://your-server.example.com/webhooks', false, $context);
$data = json_decode($response, true);
print_r($data);
Sample request
{ … }
"id": "550e8400-e29b-41d4-a716-446655440000",
"order_id": "po-2026-00042",
"custom_order_reference": "po-2026-00042",
"order_reference": "01a0ae76-0000-4000-8000-000000000000",
"customer_name": "ACME GmbH",
"status": "error",
"address": { … },
"street": "Adenauerallee",
"street_number": "1",
"postal_code": "53113",
"city": "Bonn",
"area_code": null,
"country": "DE"
},
"formatted_address": "Adenauerallee 1, 53113 Bonn, DE",
"quantity": 1,
"prefix": "+49228",
"numbers": [],
"phone_numbers": [],
"error_message": "Order canceled",
"kyc": { … },
"state": "pending",
"validated_at": null,
"provided": [],
"missing": [ … ]
[ … ],
{ … },
"document_type": "business_registration_certificate",
"notes": "Handelsregister"
},
{ … }
"document_type": "utility_bill",
"notes": "Dated within 3 months"
}
],
{ … }
"document_type": "id_document",
"notes": null
}
]
},
"webhook_url": "https://your-app.example/hooks/number-order",
"webhook_status": "pending",
"webhook_next_retry_at": null,
"created_at": "2026-05-12T10:36:47+00:00",
"updated_at": "2026-05-14T07:30:00+00:00",
"event": "order.canceled",
"delivery_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
}
No example for this status.