Skip to main content

Number Porting

Number porting lets you transfer existing phone numbers from another carrier to DialStack. This guide walks through the full port-in workflow: checking eligibility, creating a port order, approving the order, and tracking it through completion.

Overview​

Porting follows a draft-based workflow:

  1. Check eligibility — verify numbers can be ported
  2. Create a draft — provide subscriber details and your requested date and time
  3. Approve — customer approves the port order with their electronic signature
  4. Submit — send the order to the carrier for processing
  5. Track status — monitor until the port completes
Text
draft → approved → submitted → foc (scheduled) → complete
→ exception → (fix + resubmit) → submitted

Prerequisites​

  • DialStack API key (sk_live_*)
  • Phone numbers currently active with another US carrier
  • Subscriber details matching the losing carrier's records (name, service address, account number, and PIN as listed on the current carrier's records)

Step 1: Check Eligibility​

Before creating a port order, verify that the numbers can be ported. This endpoint is platform-scoped — no DialStack-Account header is needed.

Bash
curl -X POST https://api.dialstack.ai/v1/port-in-eligibility \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_numbers": ["+12025551234", "+12025555678"]
}'

Response:

JSON
{
"portable_numbers": [
{
"phone_number": "+12025551234",
"losing_carrier_name": "Verizon Wireless",
"losing_carrier_spid": "6006",
"is_wireless": true,
"account_number_required": true
}
],
"non_portable_numbers": [
{
"phone_number": "+12025555678",
"rate_center": "WASHINGT DC",
"city": "Washington",
"state": "DC"
}
]
}

Only numbers listed in portable_numbers can be included in a port order. Check account_number_required — when true, you'll need to include the subscriber's account_number and pin in the port order.

Step 2: Create a Draft Port Order​

Create a draft with the subscriber details, phone numbers, and your requested porting date and time. You can control exactly when your numbers transfer by setting requested_foc_date and optionally requested_foc_time. The date must be at least 5 business days and no more than 30 calendar days from today.

The date and time are read in requested_foc_timezone, an IANA timezone such as America/Los_Angeles. It is optional and defaults to Eastern (America/New_York). The carrier only accepts port times between 08:00 and 20:00 Eastern, so the time must fall inside that window once converted: 05:00 to 17:00 in America/Los_Angeles, for example. Responses and webhooks include requested_foc_timezone only when it was set on the order. When it is absent, the date and time are Eastern.

Once the carrier confirms the order, the port will complete at the date and time you requested — so you can plan your cutover with confidence.

Bash
curl -X POST https://api.dialstack.ai/v1/port-orders \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41" \
-H "Content-Type: application/json" \
-d '{
"phone_numbers": ["+12025551234"],
"subscriber": {
"btn": "+12025551234",
"business_name": "Smith Consulting",
"approver_name": "Jane Smith",
"account_number": "123456789",
"pin": "1234",
"address": {
"house_number": "123",
"street_name": "Main St",
"city": "Washington",
"state": "DC",
"zip": "20001"
}
},
"requested_foc_date": "2026-03-15",
"requested_foc_time": "10:00"
}'

Response:

JSON
{
"id": "port_01jkx...",
"status": "draft",
"details": {
"phone_numbers": ["+12025551234"],
"subscriber": {
"btn": "+12025551234",
"business_name": "Smith Consulting",
"approver_name": "Jane Smith",
"account_number": "123456789",
"pin": "1234",
"address": {
"house_number": "123",
"street_name": "Main St",
"city": "Washington",
"state": "DC",
"zip": "20001"
}
},
"requested_foc_date": "2026-03-15",
"requested_foc_time": "10:00"
},
"submitted_at": null,
"created_at": "2026-02-11T14:00:00Z",
"updated_at": "2026-02-11T14:00:00Z"
}
Subscriber Details

All subscriber fields must match the records on file with the losing carrier. Mismatches are the most common reason for port rejections.

  • BTN (Billing Telephone Number) — the main number on the account
  • Approver name — the name of the person authorized to approve the port
  • Service address — split into house_number and street_name to match the carrier's format
  • Account number and PIN — required for wireless ports (check account_number_required in the eligibility response)

Step 3: Approve the Port Order​

Have the customer approve the port order by providing their electronic signature and IP address. This authorizes the transfer of their phone numbers.

Bash
curl -X POST https://api.dialstack.ai/v1/port-orders/port_01jkx.../approve \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41" \
-H "Content-Type: application/json" \
-d '{
"signature": "Jane Smith",
"ip": "203.0.113.42"
}'

Response:

JSON
{
"id": "port_01jkx...",
"status": "approved",
"details": {
"phone_numbers": ["+12025551234"],
"subscriber": { "...": "..." },
"approval": {
"signature": "Jane Smith",
"ip": "203.0.113.42",
"timestamp": "2026-02-11T14:02:00Z"
},
"requested_foc_date": "2026-03-15",
"requested_foc_time": "10:00"
},
"submitted_at": null,
"created_at": "2026-02-11T14:00:00Z",
"updated_at": "2026-02-11T14:02:00Z"
}

The status transitions from draft to approved. If the numbers or subscriber details are updated after approval, the approval is cleared and the status reverts to draft — the customer must approve again. Changing only the requested port date or time keeps the approval.

Step 4: Submit the Order​

Once the order is approved, submit it to begin the porting process:

Bash
curl -X POST https://api.dialstack.ai/v1/port-orders/port_01jkx.../submit \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41"

Response:

JSON
{
"id": "port_01jkx...",
"status": "submitted",
"details": { "...": "..." },
"submitted_at": "2026-02-11T14:05:00Z",
"created_at": "2026-02-11T14:00:00Z",
"updated_at": "2026-02-11T14:05:00Z"
}

The status transitions from approved to submitted. The necessary authorization documents are generated and forwarded to the carrier automatically.

Step 5: Track the Order​

To be notified as the order progresses, subscribe a webhook endpoint to port_order.updated. It fires on every status change and whenever the port date moves, usually within seconds of the carrier acting. See port_order.updated for the payload.

You can also fetch the order at any time. Non-terminal orders are refreshed from the carrier when read:

Bash
curl https://api.dialstack.ai/v1/port-orders/port_01jkx... \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41"

You can also list all port orders, optionally filtering by status:

Bash
curl "https://api.dialstack.ai/v1/port-orders?status=submitted" \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41"

Status Reference​

StatusDescription
draftOrder created, not yet approved
approvedCustomer has approved the order, ready to submit
submittedSent to the carrier, awaiting processing
exceptionRejected by the carrier — can be corrected and resubmitted
focScheduled — the carrier has confirmed a completion date and time (in telecom, this confirmation is called a Firm Order Commitment, abbreviated FOC). Your numbers will port at the confirmed date and time.
completeNumbers successfully ported and active in DialStack
cancelledOrder was cancelled

Viewing the Audit Trail​

Every status change is recorded. View the full history:

Bash
curl https://api.dialstack.ai/v1/port-orders/port_01jkx.../events \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41"

Handling Rejections​

If the carrier rejects the port (status becomes exception), the rejection details appear in the order:

JSON
{
"status": "exception",
"details": {
"rejection": {
"code": "address_mismatch",
"message": "Service address mismatch"
}
}
}

An update to a rejected order only saves the correction — the order stays in exception and nothing reaches the carrier until you send "resubmit": true. How many requests that takes depends on what you correct.

The port date only. The requested date and time are not on the signed authorization, so the approval survives and you can save and resubmit in one request:

Bash
curl -X POST https://api.dialstack.ai/v1/port-orders/port_01jkx... \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41" \
-H "Content-Type: application/json" \
-d '{
"requested_foc_date": "2026-09-02",
"resubmit": true
}'

Anything else — the numbers or any subscriber field — changes what the customer signed, so the correction clears the approval and needs a fresh signature before it can go back. That takes three requests. First, save the correction:

Bash
curl -X POST https://api.dialstack.ai/v1/port-orders/port_01jkx... \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41" \
-H "Content-Type: application/json" \
-d '{
"subscriber": {
"btn": "+12025551234",
"business_name": "Smith Consulting",
"approver_name": "Jane Smith",
"address": {
"house_number": "456",
"street_name": "Oak Ave",
"city": "Washington",
"state": "DC",
"zip": "20002"
}
}
}'

Then have the customer approve again with POST /v1/port-orders/{id}/approve (see Approve the Port Order), and resubmit:

Bash
curl -X POST https://api.dialstack.ai/v1/port-orders/port_01jkx... \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41" \
-H "Content-Type: application/json" \
-d '{ "resubmit": true }'

Sending "resubmit": true together with a correction that changes the authorization returns 422 with code port_order_reapproval_required. The correction is still saved; approve and resubmit to finish.

A subscriber you send replaces the stored one, except account_number and pin: leave either out to keep what is stored. A stored account_number or pin can be corrected on a rejected order but not removed, because the carrier keeps it once the order has been submitted.

Changing the Port Date​

For an order in submitted or foc, send only the new port date and time. They are read in the order's requested_foc_timezone, or in Eastern if it has none. You can send requested_foc_timezone too, which moves the cutover even when the date and time stay the same:

Bash
curl -X POST https://api.dialstack.ai/v1/port-orders/port_01jkx... \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41" \
-H "Content-Type: application/json" \
-d '{
"requested_foc_date": "2026-09-02",
"requested_foc_time": "08:00"
}'

The order keeps its current status. Other fields cannot be changed at this stage and result in a 409 response with code: port_order_invalid_state.

Cancelling an Order​

Cancel a port order that hasn't reached a terminal state:

Bash
curl -X POST https://api.dialstack.ai/v1/port-orders/port_01jkx.../cancel \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41"

Orders in complete or cancelled status cannot be cancelled.

Common Rejection Reasons​

CodeReasonHow to Fix
address_mismatchService address mismatchUpdate the address to match the losing carrier's records
account_number_invalidAccount number requiredContact the losing carrier for your account number
name_mismatchApprover name mismatchUse the exact name on file with the losing carrier
business_name_mismatchBusiness name mismatchUse the business name from the carrier's Customer Service Record (CSR), which may differ from the name on the bill
btn_mismatchBTN mismatchVerify the billing telephone number with the losing carrier
pin_requiredPIN requiredContact the losing carrier to get the PIN or passcode for the account, then include it in the pin field
pin_invalidPIN invalidThe PIN provided doesn't match the carrier's records — contact the losing carrier to verify the correct PIN and update the order

Error Handling​

StatusErrorDescription
400Validation errorsInvalid phone numbers, bad date format, etc.
404Port order not foundOrder doesn't exist or belongs to a different account
409Invalid stateOperation not allowed in the order's current status
409Number limit reachedThe port would take the account past its phone number limit — see below
422Approval requiredMust approve the order before submitting

Phone number limit​

Each account has a cap on how many phone numbers it can hold, set by the platform administrator (max_phone_numbers on the account's config, default 25). Porting counts against it just as buying numbers does, so creating a port order — or editing one to add numbers — returns 409 Conflict when the resulting total would exceed the cap. The response says how many numbers the account holds, what the cap is, and how many the request tried to add.

The count includes numbers reserved by port orders already in flight, so two concurrent ports draw on the same allowance. Released numbers don't count. To clear the rejection, either have the platform administrator raise the account's limit, or release numbers the account no longer needs — the count is evaluated live at the time of the request.

This response carries "code": "phone_number_limit_exceeded", so you can tell it apart from the other 409s without reading the message — see Error codes.