Number Purchase API

Programmatically browse, buy, and remove Blooio lines over the v4 API — including the KYC, access-approval, and billing steps required before you can purchase.

The Number Purchase API lets you provision Blooio lines from your own code instead of clicking through the dashboard. You can browse available inventory, buy shared, dedicated, or inbound lines, poll the purchase, and remove (unsubscribe) a line — all over the v4 API.

This is the same billing and provisioning flow the dashboard uses, so the two never drift. It's also how resellers and platforms (for example, AgentPhone) provision a fresh number for each of their end customers without a human in the loop.

Note

The Number Purchase API is a v4 feature. All endpoints live under https://api.blooio.com/v4. Make sure the version toggle at the top of the docs is set to v4.

The full lifecycle — request access, browse, purchase, provision, track, and remove. Click a step for details; drag to pan, use the controls to zoom.

Before you can buy a line

Purchasing is gated. An organization has to clear three prerequisites before any purchase call will succeed — this keeps line provisioning tied to a verified, billable business.

1. Complete organization KYC

Open Organization Settings → Identity Verification in the dashboard and complete KYC with Stripe Identity. This verifies the business behind the organization and unlocks the features (including number purchasing) that aren't available to unverified organizations.

Until KYC shows as verified, requesting access is blocked and the API returns 403.

2. Request Number Purchase API access

In the same Organization Settings page, find the Number Purchase API section and submit the access request. You'll be asked for:

  • What you're building — your use case.
  • Website — your company/product URL.
  • Notes — anything else the review team should know (optional).

Submitting opens a review ticket. A Blooio staff member approves it, which flips settings.number_purchase_api.enabled to true for your organization. Only an organization admin can submit the request.

Warning

Until access is approved, every Number Purchase API endpoint returns 403 with code: feature_not_enabled and the message "The number purchase API is not enabled for this organization. Request access in your organization settings."

3. Add a payment method

Number purchases are billable and charge your organization's default card on file. Add one in the dashboard's billing settings first. Without a saved card, POST /channels/blooio/purchases returns 402 with code: no_payment_method.

Per-order line cap

A single purchase can't exceed your organization's per-order line cap (default 10). If you need to buy more lines in one call, apply for an increase under Messaging → Limits in the dashboard. Exceeding the cap returns 400 with code: line_limit_exceeded.

Authentication

Both credential types work, exactly like the rest of the v4 API:

  • API keysAuthorization: Bearer bl_live_.... Full organization access.
  • OAuth app tokens — when calling on behalf of another organization, the token must carry the right scope:
    • numbers:read — list available inventory and read purchase status.
    • numbers:manage — purchase and remove lines.
Authorization: Bearer YOUR_BLOOIO_API_KEY

See Calling the API with OAuth tokens for the app flow.

Step 1 — Browse available inventory

Use GET /channels/blooio/available to see what's in stock. The type query selects the plan family:

  • shared — available shared lines (returns masked inventory rows).
  • dedicated — unassigned dedicated inventory.
  • inbound — reply-only inbound lines (provisioned by area code — pass area_code to get a quote).
curl 'https://api.blooio.com/v4/channels/blooio/available?type=dedicated&country=US&limit=5' \
  -H 'Authorization: Bearer YOUR_BLOOIO_API_KEY'
Try it
{
  "data": [
    {
      "masked_national": "(801) ***-****",
      "area_code": "801",
      "country_code": "1",
      "phone_number_country": "US",
      "location": "Salt Lake City, UT"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Note

The full phone number is never returned before purchase — only a masked form ((801) ***-****). The actual line is revealed once it's provisioned to your account.

Full schema: List available numbers.

Area-code quotes

For dedicated or inbound, pass one or more area_code values to check availability by area code. Each requested code comes back as either in stock (provisioned free) or needing a custom order (a custom area-code fee applies and a provisioning ticket is opened at purchase).

curl 'https://api.blooio.com/v4/channels/blooio/available?type=dedicated&area_code=801&area_code=212' \
  -H 'Authorization: Bearer YOUR_BLOOIO_API_KEY'
Try it
{
  "data": [
    { "area_code": "801", "matched": true },
    { "area_code": "212", "matched": false, "custom_order": true }
  ],
  "matched_count": 1,
  "custom_order_count": 1,
  "has_more": false,
  "next_cursor": null
}

Step 2 — Purchase a line

Buy lines with POST /channels/blooio/purchases. The call charges your default payment method and returns a purchase_id; the lines are provisioned asynchronously once Stripe confirms the invoice.

Warning

An Idempotency-Key header is required on every purchase. It makes the billable call safe to retry — replaying the same key returns the same purchase instead of charging twice.

curl -X POST 'https://api.blooio.com/v4/channels/blooio/purchases' \
  -H 'Authorization: Bearer YOUR_BLOOIO_API_KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 7f3c1e0a-9b2d-4c11-8f6a-2b1c9d4e5f60' \
  -d '{
    "plan": "dedicated_com",
    "quantity": 2,
    "area_codes": ["415", "628"]
  }'
Try it
Field Type Notes
plan string (required) Plan id — use the specific id (e.g. dedicated_com), not the family name (dedicated returns invalid_plan). See Plans.
quantity integer Number of new lines (dedicated/inbound). Shared is always 1. Defaults to 1.
area_codes string[] Preferred 3-digit US area codes. Out-of-stock codes incur a custom fee and open a ticket.
zip_codes string[] Preferred ZIP codes, used when an area code isn't specified.

A successful call returns 202 Accepted while provisioning proceeds in the background:

{ "data": { "purchase_id": "idem_abc123", "status": "provisioning" } }

Full schema: Purchase numbers.

Rules enforced

Checked server-side before the card is charged, 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 can't exceed the cap (default 10 new lines); larger orders return 400 line_limit_exceeded. Raise it from Messaging → Limits 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.

Cards that need authentication (3DS/SCA)

If the card requires 3DS/SCA, you get 202 with status: action_required and an action_url. Send the customer to that URL to authenticate; a number.purchase.action_required webhook also fires.

{
  "data": {
    "purchase_id": "idem_abc123",
    "status": "action_required",
    "action_url": "https://invoice.stripe.com/i/..."
  }
}

Step 3 — Track provisioning

Provisioning finishes after the invoice is paid. You can either poll or listen for a webhook.

Poll the purchase

curl 'https://api.blooio.com/v4/channels/blooio/purchases/idem_abc123' \
  -H 'Authorization: Bearer YOUR_BLOOIO_API_KEY'
Try it

The status moves from pending/provisioning to completed (with the provisioned allocations) or failed.

{
  "data": {
    "purchase_id": "idem_abc123",
    "status": "completed",
    "allocations": [
      { "binding_id": "bind_...", "phone_number": "+14155550142", "type": "dedicated" }
    ]
  }
}

Full schema: Get purchase status.

Or listen for webhooks

Prefer webhooks over polling for production. 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. Always verify the webhook signature before trusting a payload.

Removing a line

Unsubscribe an owned line with DELETE /channels/{channel}. The {channel} path segment accepts either the E.164 number or the ch_ channel id. A reasons array is required — it's the same churn-reason handling as the dashboard unsubscribe button, and it emits a number.removed webhook.

curl -X DELETE 'https://api.blooio.com/v4/channels/%2B14155550142' \
  -H 'Authorization: Bearer YOUR_BLOOIO_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ "reasons": ["too_expensive", "no_longer_needed"] }'
Try it
{
  "data": {
    "phone_number": "+14155550142",
    "channel_id": "ch_...",
    "reasons": ["Too expensive", "No longer needed"]
  }
}

Full schema: Remove a number.

Plans

Pass the specific plan id when purchasing. Pricing shown is indicative — see the pricing page for current rates.

Plan id Family Description
shared_nc shared Entry shared line (limited new conversations/day). One per organization.
shared_com shared Commercial shared line (higher new-conversation allowance). One per organization.
dedicated_com dedicated Dedicated commercial line. Buy several with quantity.
dedicated_ent dedicated Dedicated enterprise line with tiered volume pricing.
inbound_basic inbound Reply-only inbound line — outbound is gated by prior inbound history (see Number pools).

Note

Shared plans are limited to a single line per organization. Sending quantity > 1 on a shared plan returns 409 with code: shared_number_limit.

Errors

Status code Meaning
403 feature_not_enabled Number Purchase API access hasn't been approved for the organization.
402 no_payment_method No saved card. Add one in billing before purchasing.
402 payment_failed The card was declined; nothing was provisioned.
400 idempotency_key_required Purchases require an Idempotency-Key header.
400 invalid_plan Unknown plan id (e.g. dedicated instead of dedicated_com).
400 plan_sunset The plan is no longer available for new purchases.
400 line_limit_exceeded Order exceeds your per-order line cap. Apply for an increase under Messaging → Limits.
409 shared_number_limit A shared plan is limited to one line per organization.