Number Purchase API
Programmatically browse, purchase, and remove Blooio lines with API keys or OAuth tokens
The Number Purchase API lets you browse available Blooio inventory, buy new lines, and remove (unsubscribe) lines you own — everything you can do from the dashboard, now over the API. It is scoped under the Blooio channel type: /v4/channels/blooio.
Note
This feature is gated. An organization admin must request access from Settings → Number Purchase API (KYC must be verified first). A Blooio admin then approves the request before the endpoints become available. Until then, these endpoints return
403 feature_not_enabled.
Authentication
Both authentication methods work:
- API keys —
Authorization: Bearer bl_live_.... Full organization access. - OAuth app tokens — must carry the right scope:
numbers:read— list available inventory and read purchase status.numbers:manage— purchase and remove lines.
Browse available numbers
# Available shared lines
curl 'https://api.blooio.com/v4/channels/blooio/available?type=shared' \
-H 'Authorization: Bearer bl_live_...'
# Quote specific dedicated area codes
curl 'https://api.blooio.com/v4/channels/blooio/available?type=dedicated&area_code=415&area_code=628' \
-H 'Authorization: Bearer bl_live_...'Try itWhen you pass one or more area_code values for dedicated or inbound, the response is a quote: each requested code is reported as either in stock (provisioned for free) or requiring a custom order. Custom orders incur a custom area-code fee and open a provisioning ticket when purchased.
{
"data": [
{ "matched": true, "area_code": "415", "allocation_id": "alloc_..." },
{ "matched": false, "area_code": "628" }
],
"matched_count": 1,
"custom_order_count": 1,
"has_more": false,
"next_cursor": null
}Purchase numbers
Purchases are asynchronous and billable. The call charges your organization's default payment method and returns a purchase_id; the lines are provisioned once Stripe confirms the invoice.
Warning
An
Idempotency-Keyheader is required. Reusing the same key returns the original purchase instead of charging again — always send a fresh, unique key per distinct order and reuse it on retries.
curl -X POST 'https://api.blooio.com/v4/channels/blooio/purchases' \
-H 'Authorization: Bearer bl_live_...' \
-H 'Idempotency-Key: order-2026-08-07-abc123' \
-H 'Content-Type: application/json' \
-d '{
"plan": "dedicated",
"quantity": 2,
"area_codes": ["415", "628"]
}'Try itA successful call returns 202 Accepted:
{ "data": { "purchase_id": "idem_order-2026-08-07-abc123", "status": "provisioning" } }Rules enforced
These are enforced server-side, identically to the dashboard:
- Payment method required — no saved card returns
402 no_payment_method. - One shared number per organization — a second shared purchase returns
409 shared_number_limit. - Per-order line cap — a single order may not exceed the cap (default 10 new lines). Larger orders return
400 line_limit_exceeded. Apply to raise the cap from Limits → Number purchase limit in the dashboard. - Custom area codes — out-of-stock codes are billed the custom area-code fee and open a provisioning ticket; in-stock codes are assigned immediately at no custom fee.
Card authentication (3DS/SCA)
If the saved card needs additional authentication, the response is 202 with status: action_required and an action_url. Open the URL to complete authentication; provisioning continues afterward.
{
"data": {
"purchase_id": "idem_...",
"status": "action_required",
"action_url": "https://invoice.stripe.com/i/..."
}
}Track a purchase
Poll the purchase, or (recommended) subscribe to the webhook events below.
curl 'https://api.blooio.com/v4/channels/blooio/purchases/idem_order-2026-08-07-abc123' \
-H 'Authorization: Bearer bl_live_...'Try it{
"data": {
"purchase_id": "idem_...",
"status": "completed",
"allocations": [
{ "binding_id": "bind_...", "phone_number": "+14155550123", "type": "dedicated" }
]
}
}Remove (unsubscribe) a number
Removing a line cancels its subscription — the same flow as the dashboard unsubscribe button (it updates your CRM and posts to Slack). Reference the line by its E.164 number or its ch_ channel id, and include one or more churn reasons.
curl -X DELETE 'https://api.blooio.com/v4/channels/+14155550123' \
-H 'Authorization: Bearer bl_live_...' \
-H 'Content-Type: application/json' \
-d '{ "reasons": ["too_expensive", "no_longer_needed"] }'Try itA successful call returns 200 and emits a number.removed webhook.
Webhook events
Because purchases complete asynchronously, subscribe to these events to learn the outcome:
| Event | When |
|---|---|
number.purchase.completed |
Lines provisioned. Payload carries purchase_id, lines (each { phone_number, channel_id }), plus flat phone_numbers and channel_ids arrays covering every provisioned line. |
number.purchase.action_required |
Card needs 3DS/SCA. Payload carries purchase_id, action_url. |
number.purchase.failed |
Terminal failure; nothing provisioned. Payload carries purchase_id, reason. |
number.removed |
A line was removed/unsubscribed. Payload carries phone_number, channel_id, binding_id, reasons. |
For API-originated purchases, Blooio also emails your organization's admins the result (completed / action required / failed).
See Webhook signatures for verifying delivery authenticity.