DevelopersAPI referencePhone

Phone API

REST endpoints for phone in SKUFlow: 7 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.

OperationEndpointRisk
Phone settingsGET /organizations/{organizationId}/phone/settingsread
Change phone settingsPATCH /organizations/{organizationId}/phone/settingswrite
Connect voicePOST /organizations/{organizationId}/phone/connectsensitive
Browser phone tokenPOST /organizations/{organizationId}/phone/softphone-tokenwrite
Calls ringing for meGET /organizations/{organizationId}/phone/liveread
Simulate an inbound call (fake mode)POST /organizations/{organizationId}/phone/simulatewrite
Run phone work nowPOST /organizations/{organizationId}/phone/runwrite

Phone settings

GET /organizations/{organizationId}/phone/settings

The phone system's settings: on/off, the mode (twilio, or fake = simulated calls), business hours in the business's timezone, after-hours voicemail or forwarding, ring timeout and the default ring group, voicemail greeting, call recording with its consent announcement and retention, transcription and AI summaries, missed-call follow-ups and text-back, and the webhook URLs Twilio calls. Read-only.

Permissionphone.manage (Owner, Admin)
Riskread, OAuth scope skuflow.read. Read: reads only.
MCP toolphone_settings (toolset marketing)

Example

curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/settings" \
  -H "Authorization: Bearer $SKUFLOW_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
modeenumfake = simulated calls (development, tests). One of fake, twilio, off.
enabledboolean
twilioSourceenumManaged: the business's SKUFlow Twilio sub-account (billed at Twilio's cost + 20%); OWN: its own Twilio account connected in Settings → Messaging. One of MANAGED, OWN.
managedAccountobject or nullThe SKUFlow sub-account (MANAGED, once made).
managedAccount.accountSidHintstring
managedAccount.statusenumOne of ACTIVE, SUSPENDED, CLOSED.
managedAccount.fakeboolean
managedAccount.suspendReasonstring or null
managedAccount.closeAfterstring (date-time) or null
accountConnectedbooleanThe Twilio account calls run on is ready: the SKUFlow sub-account (active), or the business's own account connected for texts.
browserCallingbooleanThe browser phone's API key and TwiML App exist.
businessHoursobject
afterHoursActionenumOne of VOICEMAIL, FORWARD.
afterHoursForwardNumberstring or null
ringTimeoutSecondsinteger
defaultRingGroupIdstring or null
voicemailGreetingstring or null
recordCallsboolean
announceRecordingboolean
recordingAnnouncementstring or null
businessStatestring or null
recordingRetentionDaysinteger
transcribeCallsboolean
aiSummariesboolean
missedCallFollowUpboolean
missedCallTextBackboolean
missedCallTextBodystring or null
webhookUrlsobject or nullWhere Twilio sends calls (set on bought or assigned numbers automatically).
webhookUrls.voicestring
webhookUrls.statusstring
webhookUrls.browserstring
timeZonestring
updatedAtstring (date-time)

Change phone settings

PATCH /organizations/{organizationId}/phone/settings

Change how calls are handled from the next call on: business hours, after hours, ring timeout, default ring group, voicemail greeting, recording (announced by default; two-party-consent states are always announced), retention, transcription, AI summaries (metered against AI credits), missed-call follow-ups and the missed-call text (sent only to known customers whose number is OK to text). enabled: false stops answering calls.

Permissionphone.manage (Owner, Admin)
Riskwrite, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolphone_updateSettings (toolset marketing)
PlanNeeds the Phone system add-on.

Request body

FieldTypeRequiredDescription
enabledbooleanNo
businessHoursobjectNoOpening hours per weekday, e.g. { "mon": [{ "start": "08:00", "end": "17:00" }] }.
businessHours.sunarray of objectNoUp to 4 items.
businessHours.sun[].startstringYesHh: MM, 24-hour, in the business's timezone.
businessHours.sun[].endstringYesHh: MM, 24-hour, in the business's timezone.
businessHours.monarray of objectNoUp to 4 items.
businessHours.mon[].startstringYesHh: MM, 24-hour, in the business's timezone.
businessHours.mon[].endstringYesHh: MM, 24-hour, in the business's timezone.
businessHours.tuearray of objectNoUp to 4 items.
businessHours.tue[].startstringYesHh: MM, 24-hour, in the business's timezone.
businessHours.tue[].endstringYesHh: MM, 24-hour, in the business's timezone.
businessHours.wedarray of objectNoUp to 4 items.
businessHours.wed[].startstringYesHh: MM, 24-hour, in the business's timezone.
businessHours.wed[].endstringYesHh: MM, 24-hour, in the business's timezone.
businessHours.thuarray of objectNoUp to 4 items.
businessHours.thu[].startstringYesHh: MM, 24-hour, in the business's timezone.
businessHours.thu[].endstringYesHh: MM, 24-hour, in the business's timezone.
businessHours.friarray of objectNoUp to 4 items.
businessHours.fri[].startstringYesHh: MM, 24-hour, in the business's timezone.
businessHours.fri[].endstringYesHh: MM, 24-hour, in the business's timezone.
businessHours.satarray of objectNoUp to 4 items.
businessHours.sat[].startstringYesHh: MM, 24-hour, in the business's timezone.
businessHours.sat[].endstringYesHh: MM, 24-hour, in the business's timezone.
afterHoursActionenumNoOne of VOICEMAIL, FORWARD.
afterHoursForwardNumberstring or nullNoA phone number; +E.164 is safest (+13125550100). 10 digits are read as US. Up to 32 characters.
ringTimeoutSecondsintegerNo5–60.
defaultRingGroupIdstring or nullNoRings when a number has no group of its own.
voicemailGreetingstring or nullNoUp to 500 characters.
recordCallsbooleanNo
announceRecordingbooleanNoAnnounce recording (default on). Off is honoured only when both parties are known to be in one-party-consent states.
recordingAnnouncementstring or nullNoUp to 500 characters.
businessStatestring or nullNoThe business's US state for the recording consent rule.
recordingRetentionDaysintegerNo1–3650.
transcribeCallsbooleanNo
aiSummariesbooleanNo
missedCallFollowUpbooleanNo
missedCallTextBackbooleanNoText a known customer back after a missed call (needs OK to text).
missedCallTextBodystring or nullNoUp to 320 characters.

Example

curl -X PATCH "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/settings" \
  -H "Authorization: Bearer $SKUFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true,"businessHours":{"sun":[{"start":"09:00","end":"09:00"}],"mon":[{"start":"09:00","end":"09:00"}],"tue":[{"start":"09:00","end":"09:00"}]},"afterHoursAction":"VOICEMAIL"}'

Response

200: the answer's fields
FieldTypeDescription
modeenumfake = simulated calls (development, tests). One of fake, twilio, off.
enabledboolean
twilioSourceenumManaged: the business's SKUFlow Twilio sub-account (billed at Twilio's cost + 20%); OWN: its own Twilio account connected in Settings → Messaging. One of MANAGED, OWN.
managedAccountobject or nullThe SKUFlow sub-account (MANAGED, once made).
managedAccount.accountSidHintstring
managedAccount.statusenumOne of ACTIVE, SUSPENDED, CLOSED.
managedAccount.fakeboolean
managedAccount.suspendReasonstring or null
managedAccount.closeAfterstring (date-time) or null
accountConnectedbooleanThe Twilio account calls run on is ready: the SKUFlow sub-account (active), or the business's own account connected for texts.
browserCallingbooleanThe browser phone's API key and TwiML App exist.
businessHoursobject
afterHoursActionenumOne of VOICEMAIL, FORWARD.
afterHoursForwardNumberstring or null
ringTimeoutSecondsinteger
defaultRingGroupIdstring or null
voicemailGreetingstring or null
recordCallsboolean
announceRecordingboolean
recordingAnnouncementstring or null
businessStatestring or null
recordingRetentionDaysinteger
transcribeCallsboolean
aiSummariesboolean
missedCallFollowUpboolean
missedCallTextBackboolean
missedCallTextBodystring or null
webhookUrlsobject or nullWhere Twilio sends calls (set on bought or assigned numbers automatically).
webhookUrls.voicestring
webhookUrls.statusstring
webhookUrls.browserstring
timeZonestring
updatedAtstring (date-time)

Connect voice

POST /organizations/{organizationId}/phone/connect

Turn the phone system on. Makes the business's SKUFlow Twilio sub-account the first time (MANAGED, the default; usage billed at Twilio's cost + 20%), then creates an API key and a TwiML App in that account (or in the business's own Twilio account when it chose OWN) for the browser phone; safe to repeat (the App's URLs are refreshed). In fake mode it records simulated ids. Call GET /phone/settings first to see the mode.

Permissionphone.manage (Owner, Admin)
Risksensitive, OAuth scope skuflow.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolphone_connect (toolset marketing)
PlanNeeds the Phone system add-on.
ApprovalOver MCP, this tool asks for your OK first (in the AI app when it supports it, otherwise in SKUFlow). See Approvals.

Example

curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/connect" \
  -H "Authorization: Bearer $SKUFLOW_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
modeenumfake = simulated calls (development, tests). One of fake, twilio, off.
enabledboolean
twilioSourceenumManaged: the business's SKUFlow Twilio sub-account (billed at Twilio's cost + 20%); OWN: its own Twilio account connected in Settings → Messaging. One of MANAGED, OWN.
managedAccountobject or nullThe SKUFlow sub-account (MANAGED, once made).
managedAccount.accountSidHintstring
managedAccount.statusenumOne of ACTIVE, SUSPENDED, CLOSED.
managedAccount.fakeboolean
managedAccount.suspendReasonstring or null
managedAccount.closeAfterstring (date-time) or null
accountConnectedbooleanThe Twilio account calls run on is ready: the SKUFlow sub-account (active), or the business's own account connected for texts.
browserCallingbooleanThe browser phone's API key and TwiML App exist.
businessHoursobject
afterHoursActionenumOne of VOICEMAIL, FORWARD.
afterHoursForwardNumberstring or null
ringTimeoutSecondsinteger
defaultRingGroupIdstring or null
voicemailGreetingstring or null
recordCallsboolean
announceRecordingboolean
recordingAnnouncementstring or null
businessStatestring or null
recordingRetentionDaysinteger
transcribeCallsboolean
aiSummariesboolean
missedCallFollowUpboolean
missedCallTextBackboolean
missedCallTextBodystring or null
webhookUrlsobject or nullWhere Twilio sends calls (set on bought or assigned numbers automatically).
webhookUrls.voicestring
webhookUrls.statusstring
webhookUrls.browserstring
timeZonestring
updatedAtstring (date-time)

Browser phone token

POST /organizations/{organizationId}/phone/softphone-token

What the browser phone needs for the signed-in person: mode TWILIO with a one-hour Twilio Voice access token (identity derived from the user; receives the ring group's calls, places calls through the business number), FAKE (simulated calls), OFF (phone system off) or UNAVAILABLE (connect voice first). Nothing else changes.

Permissionphone.calls (Owner, Admin, Staff)
Riskwrite, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolphone_softphone (toolset marketing)
PlanNeeds the Phone system add-on.

Example

curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/softphone-token" \
  -H "Authorization: Bearer $SKUFLOW_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
modeenumOne of TWILIO, FAKE, OFF, UNAVAILABLE.
identitystring
tokenstring or null
expiresAtstring (date-time) or null

Calls ringing for me

GET /organizations/{organizationId}/phone/live

Calls ringing for the signed-in person's ring groups and calls they are on: what the browser phone and the screen pop show. Poll every few seconds. Read-only (simulated calls nobody answered are ended as Twilio would).

Permissionphone.calls (Owner, Admin, Staff)
Riskread, OAuth scope skuflow.read. Read: reads only.
MCP toolphone_live (toolset marketing)

Example

curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/live" \
  -H "Authorization: Bearer $SKUFLOW_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
itemsarray of object
items[].idstring
items[].directionenumOne of INBOUND, OUTBOUND.
items[].statusenumOne of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED.
items[].fakebooleanA simulated call (SKUFLOW_VOICE=fake).
items[].fromNumberstring
items[].toNumberstring
items[].otherNumberstringThe customer's side of the call.
items[].phoneNumberobject or null
items[].phoneNumber.idstring
items[].phoneNumber.labelstring
items[].phoneNumber.e164string
items[].sourceenum or nullThe tracked source of the number called; null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER.
items[].campaignobject or null
items[].campaign.idstring
items[].campaign.namestring
items[].businessUnitobject or null
items[].businessUnit.idstring
items[].businessUnit.namestring
items[].customerobject or null
items[].customer.idstring
items[].customer.namestring
items[].matchedCustomerCountinteger
items[].userobject or nullWho answered (inbound) or placed it (outbound).
items[].user.idstring
items[].user.namestring
items[].bridgeNumberstring or null
items[].ivrDigitstring or null
items[].afterHoursboolean
items[].startedAtstring (date-time)
items[].answeredAtstring (date-time) or null
items[].endedAtstring (date-time) or null
items[].durationSecondsinteger or nullTalk time.
items[].billedMinutesinteger or null
items[].voicemailboolean
items[].recordingobject
items[].recording.statusenumOne of NONE, PENDING, DONE, FAILED, SKIPPED.
items[].recording.announcedboolean
items[].recording.durationSecondsinteger or null
items[].recording.availableboolean
items[].recording.deletedAtstring (date-time) or null
items[].recording.errorstring or null
items[].transcriptStatusenumOne of NONE, PENDING, DONE, FAILED, SKIPPED.
items[].transcriptstring or nullOnly for callers holding phone.recordings, on GET /phone/calls/{callId}.
items[].summaryStatusenumOne of NONE, PENDING, DONE, FAILED, SKIPPED.
items[].summarystring or nullThe AI call summary (Claude, from the transcript).
items[].summaryErrorstring or null
items[].followUpobject
items[].followUp.statusenumOne of NONE, OPEN, DONE.
items[].followUp.assigneeobject or null
items[].followUp.doneAtstring (date-time) or null
items[].textBackobject or null
items[].textBack.statusstring
items[].textBack.messageIdstring or null
items[].requestobject or null
items[].request.idstring
items[].request.identifierstring
items[].notesstring or null
items[].createdAtstring (date-time)
items[].updatedAtstring (date-time)

Simulate an inbound call (fake mode)

POST /organizations/{organizationId}/phone/simulate

Only with simulated voice (SKUFLOW_VOICE=fake; 409 otherwise): an inbound call from from to one of the business's numbers, through the same steps as Twilio's webhooks (routing, caller match, menu, recording, transcript, AI summary, follow-up, text-back). RING leaves it ringing for the browser phone; ANSWERED, MISSED and VOICEMAIL play it through. now is test-only (refused in production).

Permissionphone.manage (Owner, Admin)
Riskwrite, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolphone_simulate (toolset marketing)
PlanNeeds the Phone system add-on.

Request body

FieldTypeRequiredDescription
fromstringYesA phone number; +E.164 is safest (+13125550100). 10 digits are read as US. Up to 32 characters.
phoneNumberIdstring or nullNoThe number called; default the main line.
digitstring or nullNo
outcomeenumNoOne of RING, ANSWERED, MISSED, VOICEMAIL. Default RING.
answeredByUserIdstring or nullNo
talkSecondsintegerNo1–7200.
transcriptstring or nullNo
callerStatestring or nullNo
nowstring (date-time)No

Example

curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/simulate" \
  -H "Authorization: Bearer $SKUFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"from":"2026-11-03"}'

Response

200: the answer's fields
FieldTypeDescription
idstring
directionenumOne of INBOUND, OUTBOUND.
statusenumOne of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED.
fakebooleanA simulated call (SKUFLOW_VOICE=fake).
fromNumberstring
toNumberstring
otherNumberstringThe customer's side of the call.
phoneNumberobject or null
phoneNumber.idstring
phoneNumber.labelstring
phoneNumber.e164string
sourceenum or nullThe tracked source of the number called; null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER.
campaignobject or null
campaign.idstring
campaign.namestring
businessUnitobject or null
businessUnit.idstring
businessUnit.namestring
customerobject or null
customer.idstring
customer.namestring
matchedCustomerCountinteger
userobject or nullWho answered (inbound) or placed it (outbound).
user.idstring
user.namestring
bridgeNumberstring or null
ivrDigitstring or null
afterHoursboolean
startedAtstring (date-time)
answeredAtstring (date-time) or null
endedAtstring (date-time) or null
durationSecondsinteger or nullTalk time.
billedMinutesinteger or null
voicemailboolean
recordingobject
recording.statusenumOne of NONE, PENDING, DONE, FAILED, SKIPPED.
recording.announcedboolean
recording.durationSecondsinteger or null
recording.availableboolean
recording.deletedAtstring (date-time) or null
recording.errorstring or null
transcriptStatusenumOne of NONE, PENDING, DONE, FAILED, SKIPPED.
transcriptstring or nullOnly for callers holding phone.recordings, on GET /phone/calls/{callId}.
summaryStatusenumOne of NONE, PENDING, DONE, FAILED, SKIPPED.
summarystring or nullThe AI call summary (Claude, from the transcript).
summaryErrorstring or null
followUpobject
followUp.statusenumOne of NONE, OPEN, DONE.
followUp.assigneeobject or null
followUp.assignee.idstring
followUp.assignee.namestring
followUp.doneAtstring (date-time) or null
textBackobject or null
textBack.statusstring
textBack.messageIdstring or null
requestobject or null
request.idstring
request.identifierstring
notesstring or null
createdAtstring (date-time)
updatedAtstring (date-time)

Run phone work now

POST /organizations/{organizationId}/phone/run

Run this business's pending phone work now instead of waiting for the worker: recording downloads into storage, AI summaries, simulated calls nobody answered, and recordings past retention. now is test-only (refused in production).

Permissionphone.manage (Owner, Admin)
Riskwrite, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolphone_run (toolset marketing)
PlanNeeds the Phone system add-on.

Request body

FieldTypeRequiredDescription
nowstring (date-time)No

Example

curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/run" \
  -H "Authorization: Bearer $SKUFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"now":"2026-11-03T15:00:00Z"}'

Response

200: the answer's fields
FieldTypeDescription
processedinteger
purgedinteger

On this page