Phone numbers API
REST endpoints for phone numbers in SKUFlow: 6 operations with parameters, request and response fields, permissions and curl examples.
Base URL: https://app.skuflow.ai/api. Every call needs Authorization: Bearer skf_… (Authentication); {organizationId} ($ORGANIZATION_ID in the examples) is your business's id.
| Operation | Endpoint | Risk |
|---|---|---|
| List phone numbers | GET /organizations/{organizationId}/phone/numbers | read |
| Buy or assign a number | POST /organizations/{organizationId}/phone/numbers | sensitive |
| Numbers available to buy | GET /organizations/{organizationId}/phone/numbers/available | read |
| Twilio numbers not in use | GET /organizations/{organizationId}/phone/numbers/unassigned | read |
| Change a number | PATCH /organizations/{organizationId}/phone/numbers/{phoneNumberId} | write |
| Release a number | DELETE /organizations/{organizationId}/phone/numbers/{phoneNumberId} | sensitive |
List phone numbers
GET /organizations/{organizationId}/phone/numbers
The business's numbers: the main line and tracking numbers per source (website, Google Business Profile, a campaign, a QR code, a business unit, print), each with its ring group, menu and website number-insertion rule. includeReleased adds released ones. Read-only.
| Permission | phone.read (Owner, Admin, Staff, Member) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | phoneNumbers_list (toolset marketing) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
includeReleased | query | boolean | No |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/numbers" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
items | array of object | |
items[].id | string | |
items[].e164 | string | |
items[].label | string | |
items[].source | enum | One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
items[].fake | boolean | |
items[].campaign | object or null | |
items[].campaign.id | string | |
items[].campaign.name | string | |
items[].businessUnit | object or null | |
items[].businessUnit.id | string | |
items[].businessUnit.name | string | |
items[].qrCode | object or null | |
items[].qrCode.id | string | |
items[].qrCode.name | string | |
items[].ringGroup | object or null | |
items[].ringGroup.id | string | |
items[].ringGroup.name | string | |
items[].ivrEnabled | boolean | |
items[].ivrGreeting | string or null | |
items[].ivrOptions | array of object | |
items[].ivrOptions[].digit | string | |
items[].ivrOptions[].label | string | |
items[].ivrOptions[].ringGroupId | string | |
items[].dniUtmSource | string or null | |
items[].dniUtmMedium | string or null | |
items[].active | boolean | |
items[].releaseAfter | string (date-time) or null | Scheduled release (the phone add-on was removed); null when kept. |
items[].createdAt | string (date-time) | |
items[].updatedAt | string (date-time) |
Buy or assign a number
POST /organizations/{organizationId}/phone/numbers
Money: mode BUY purchases the number in the business's SKUFlow Twilio sub-account (billed monthly at Twilio's cost + 20%), or on its own Twilio account when it chose OWN; ASSIGN takes a number already on that account. Either way its voice webhooks are pointed at SKUFlow and calls to it are tracked under source. Call GET /phone/numbers/available (or /unassigned) first.
| Permission | phone.manage (Owner, Admin) |
| Risk | sensitive, OAuth scope skuflow.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access. |
| MCP tool | phoneNumbers_add (toolset marketing) |
| Plan | Needs the Phone system add-on. |
| Approval | Over MCP, this tool asks for your OK first (in the AI app when it supports it, otherwise in SKUFlow). See Approvals. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
label | string | Yes | Up to 80 characters. |
source | enum | No | Where the number is published; its calls are reported under it. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
campaignId | string or null | No | A marketing campaign whose number this is. |
businessUnitId | string or null | No | A business unit whose calls this number takes. |
qrCodeId | string or null | No | A QR code (printed material) this number is on. |
ringGroupId | string or null | No | Who rings; null = the business's default ring group. |
ivrEnabled | boolean | No | |
ivrGreeting | string or null | No | Up to 500 characters. |
ivrOptions | array of object | No | Up to 9 items. |
ivrOptions[].digit | string | Yes | |
ivrOptions[].label | string | Yes | Up to 80 characters. |
ivrOptions[].ringGroupId | string | Yes | The ring group this choice rings (GET /phone/ring-groups). |
dniUtmSource | string or null | No | Website number insertion: show this number to visitors with this utm_source (e.g. google). Up to 60 characters. |
dniUtmMedium | string or null | No | …and this utm_medium (e.g. cpc); blank = any. Up to 60 characters. |
mode | enum | Yes | One of BUY, ASSIGN. |
e164 | string | Yes | A phone number; +E.164 is safest (+13125550100). 10 digits are read as US. Up to 32 characters. |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/numbers" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"label":"string","mode":"BUY","e164":"string"}'Response
201: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
e164 | string | |
label | string | |
source | enum | One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
fake | boolean | |
campaign | object or null | |
campaign.id | string | |
campaign.name | string | |
businessUnit | object or null | |
businessUnit.id | string | |
businessUnit.name | string | |
qrCode | object or null | |
qrCode.id | string | |
qrCode.name | string | |
ringGroup | object or null | |
ringGroup.id | string | |
ringGroup.name | string | |
ivrEnabled | boolean | |
ivrGreeting | string or null | |
ivrOptions | array of object | |
ivrOptions[].digit | string | |
ivrOptions[].label | string | |
ivrOptions[].ringGroupId | string | |
dniUtmSource | string or null | |
dniUtmMedium | string or null | |
active | boolean | |
releaseAfter | string (date-time) or null | Scheduled release (the phone add-on was removed); null when kept. |
createdAt | string (date-time) | |
updatedAt | string (date-time) |
Numbers available to buy
GET /organizations/{organizationId}/phone/numbers/available
Local US numbers Twilio offers to buy, by area code and / or digits or letters inside the number (contains, e.g. 4210 or PRINT; * for any digit) (fake mode: fictional 555-01xx numbers), and what one more number costs a month (offer: Twilio's cost + 20%, or included). Then POST /phone/numbers with mode BUY. Read-only.
| Permission | phone.manage (Owner, Admin) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | phoneNumbers_available (toolset marketing) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
areaCode | query | string | No | Three-digit US area code, e.g. 512. Optional when contains is given. |
contains | query | string | No | Digits or letters the number must contain, anywhere (Twilio Contains): 2–10 of 0-9, A-Z (phone keypad: PRINT = 77468) and * for any one digit, e.g. 4210 or 555****. Spaces and dashes are ignored. With neither field, any US local number. Up to 20 characters. |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/numbers/available" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
items | array of object | |
items[].e164 | string | +E.164; pass it as buyE164 / e164 to buy it. |
items[].locality | string or null | Twilio's rate center (city), when known. |
items[].region | string or null | Two-letter state, when known. |
offer | object | What one more number costs a month. |
offer.monthlyCents | integer | A local number's monthly price at Twilio's cost + 20%, in cents. |
offer.included | boolean | Included: the phone add-on's number (or the texting number). |
offer.includedBy | enum or null | One of TEXTING, PHONE_ADDON. |
offer.source | enum | One of twilio, fallback. |
Twilio numbers not in use
GET /organizations/{organizationId}/phone/numbers/unassigned
Numbers already on the business's Twilio account that SKUFlow does not use yet: assign one with POST /phone/numbers mode ASSIGN instead of buying. Empty in fake mode. Read-only.
| Permission | phone.manage (Owner, Admin) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | phoneNumbers_unassigned (toolset marketing) |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/numbers/unassigned" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
items | array of object | |
items[].sid | string | |
items[].e164 | string | |
items[].friendlyName | string or null |
Change a number
PATCH /organizations/{organizationId}/phone/numbers/{phoneNumberId}
Change a number's label, tracked source (campaign, QR code, business unit), ring group, menu (press 1 for … → a ring group) and website number-insertion rule. Applies from the next call.
| Permission | phone.manage (Owner, Admin) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | phoneNumbers_update (toolset marketing) |
| Plan | Needs the Phone system add-on. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
phoneNumberId | path | string | Yes |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
label | string | No | Up to 80 characters. |
source | enum | No | Where the number is published; its calls are reported under it. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
campaignId | string or null | No | A marketing campaign whose number this is. |
businessUnitId | string or null | No | A business unit whose calls this number takes. |
qrCodeId | string or null | No | A QR code (printed material) this number is on. |
ringGroupId | string or null | No | Who rings; null = the business's default ring group. |
ivrEnabled | boolean | No | |
ivrGreeting | string or null | No | Up to 500 characters. |
ivrOptions | array of object | No | Up to 9 items. |
ivrOptions[].digit | string | Yes | |
ivrOptions[].label | string | Yes | Up to 80 characters. |
ivrOptions[].ringGroupId | string | Yes | The ring group this choice rings (GET /phone/ring-groups). |
dniUtmSource | string or null | No | Website number insertion: show this number to visitors with this utm_source (e.g. google). Up to 60 characters. |
dniUtmMedium | string or null | No | …and this utm_medium (e.g. cpc); blank = any. Up to 60 characters. |
Example
curl -X PATCH "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/numbers/{phoneNumberId}" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"label":"string","source":"MAIN","campaignId":"campaign_id"}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
e164 | string | |
label | string | |
source | enum | One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
fake | boolean | |
campaign | object or null | |
campaign.id | string | |
campaign.name | string | |
businessUnit | object or null | |
businessUnit.id | string | |
businessUnit.name | string | |
qrCode | object or null | |
qrCode.id | string | |
qrCode.name | string | |
ringGroup | object or null | |
ringGroup.id | string | |
ringGroup.name | string | |
ivrEnabled | boolean | |
ivrGreeting | string or null | |
ivrOptions | array of object | |
ivrOptions[].digit | string | |
ivrOptions[].label | string | |
ivrOptions[].ringGroupId | string | |
dniUtmSource | string or null | |
dniUtmMedium | string or null | |
active | boolean | |
releaseAfter | string (date-time) or null | Scheduled release (the phone add-on was removed); null when kept. |
createdAt | string (date-time) | |
updatedAt | string (date-time) |
Release a number
DELETE /organizations/{organizationId}/phone/numbers/{phoneNumberId}
Destructive: gives the number back to Twilio; it stops ringing and may be taken by someone else. Its call history stays. Cannot be undone.
| Permission | phone.manage (Owner, Admin) |
| Risk | sensitive, OAuth scope skuflow.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access. |
| MCP tool | phoneNumbers_release (toolset marketing) |
| Approval | Over MCP, this tool always waits for your approval in SKUFlow. See Approvals. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
phoneNumberId | path | string | Yes |
Example
curl -X DELETE "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/numbers/{phoneNumberId}" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
e164 | string | |
label | string | |
source | enum | One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
fake | boolean | |
campaign | object or null | |
campaign.id | string | |
campaign.name | string | |
businessUnit | object or null | |
businessUnit.id | string | |
businessUnit.name | string | |
qrCode | object or null | |
qrCode.id | string | |
qrCode.name | string | |
ringGroup | object or null | |
ringGroup.id | string | |
ringGroup.name | string | |
ivrEnabled | boolean | |
ivrGreeting | string or null | |
ivrOptions | array of object | |
ivrOptions[].digit | string | |
ivrOptions[].label | string | |
ivrOptions[].ringGroupId | string | |
dniUtmSource | string or null | |
dniUtmMedium | string or null | |
active | boolean | |
releaseAfter | string (date-time) or null | Scheduled release (the phone add-on was removed); null when kept. |
createdAt | string (date-time) | |
updatedAt | string (date-time) |