Calls API
REST endpoints for calls in SKUFlow: 11 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 |
|---|---|---|
| Call log | GET /organizations/{organizationId}/phone/calls | read |
| Call a customer (click-to-call) | POST /organizations/{organizationId}/phone/calls | sensitive |
| A call | GET /organizations/{organizationId}/phone/calls/{callId} | read |
| Notes, customer and follow-up of a call | PATCH /organizations/{organizationId}/phone/calls/{callId} | write |
| Screen pop | GET /organizations/{organizationId}/phone/calls/{callId}/context | read |
| Answer a ringing call (simulated phone) | POST /organizations/{organizationId}/phone/calls/{callId}/answer | write |
| Hang up | POST /organizations/{organizationId}/phone/calls/{callId}/hangup | write |
| Link a call to the request it led to | POST /organizations/{organizationId}/phone/calls/{callId}/link | write |
| Play a recording | GET /organizations/{organizationId}/phone/calls/{callId}/recording | read |
| Delete a recording | DELETE /organizations/{organizationId}/phone/calls/{callId}/recording | sensitive |
| Write the AI summary again | POST /organizations/{organizationId}/phone/calls/{callId}/summary | write |
Call log
GET /organizations/{organizationId}/phone/calls
Calls newest first, with the tracked source, the customer matched by caller ID, who answered, talk time, recording and summary status, the follow-up and what the call led to. Filters: direction, outcome (ANSWERED, MISSED incl. voicemails, VOICEMAIL, LIVE), followUp OPEN for missed calls to return, customer, number, source (or UNTRACKED), from/to days in the business's calendar. openFollowUps counts missed calls still to return. Transcripts are not listed. Read-only.
| Permission | phone.read (Owner, Admin, Staff, Member) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | calls_list (toolset marketing) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
direction | query | enum | No | One of INBOUND, OUTBOUND. |
outcome | query | enum | No | One of ANSWERED, MISSED, VOICEMAIL, LIVE. |
followUp | query | enum | No | One of OPEN, DONE. |
customerId | query | string | No | |
phoneNumberId | query | string | No | |
source | query | enum | No | One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER, UNTRACKED. |
from | query | string | No | A calendar date in the business's own calendar, YYYY-MM-DD. |
to | query | string | No | A calendar date in the business's own calendar, YYYY-MM-DD. |
limit | query | integer | No | 1–200. |
offset | query | integer | No |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/calls" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
items | array of object | |
items[].id | string | |
items[].direction | enum | One of INBOUND, OUTBOUND. |
items[].status | enum | One of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED. |
items[].fake | boolean | A simulated call (SKUFLOW_VOICE=fake). |
items[].fromNumber | string | |
items[].toNumber | string | |
items[].otherNumber | string | The customer's side of the call. |
items[].phoneNumber | object or null | |
items[].phoneNumber.id | string | |
items[].phoneNumber.label | string | |
items[].phoneNumber.e164 | string | |
items[].source | enum or null | The tracked source of the number called; null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
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[].customer | object or null | |
items[].customer.id | string | |
items[].customer.name | string | |
items[].matchedCustomerCount | integer | |
items[].user | object or null | Who answered (inbound) or placed it (outbound). |
items[].user.id | string | |
items[].user.name | string | |
items[].bridgeNumber | string or null | |
items[].ivrDigit | string or null | |
items[].afterHours | boolean | |
items[].startedAt | string (date-time) | |
items[].answeredAt | string (date-time) or null | |
items[].endedAt | string (date-time) or null | |
items[].durationSeconds | integer or null | Talk time. |
items[].billedMinutes | integer or null | |
items[].voicemail | boolean | |
items[].recording | object | |
items[].recording.status | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
items[].recording.announced | boolean | |
items[].recording.durationSeconds | integer or null | |
items[].recording.available | boolean | |
items[].recording.deletedAt | string (date-time) or null | |
items[].recording.error | string or null | |
items[].transcriptStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
items[].transcript | string or null | Only for callers holding phone.recordings, on GET /phone/calls/{callId}. |
items[].summaryStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
items[].summary | string or null | The AI call summary (Claude, from the transcript). |
items[].summaryError | string or null | |
items[].followUp | object | |
items[].followUp.status | enum | One of NONE, OPEN, DONE. |
items[].followUp.assignee | object or null | |
items[].followUp.doneAt | string (date-time) or null | |
items[].textBack | object or null | |
items[].textBack.status | string | |
items[].textBack.messageId | string or null | |
items[].request | object or null | |
items[].request.id | string | |
items[].request.identifier | string | |
items[].notes | string or null | |
items[].createdAt | string (date-time) | |
items[].updatedAt | string (date-time) | |
total | integer | |
openFollowUps | integer |
Call a customer (click-to-call)
POST /organizations/{organizationId}/phone/calls
Customer-visible: calls a customer (or any number) showing the business's number (phoneNumberId, default the main line). via BROWSER: the answer's connect.params go to the browser phone (Twilio Voice SDK Device.connect). via PHONE: Twilio rings bridgeNumber (your own phone) first, then the customer. requestId records what the call was about. Fake mode: the customer answers at once; end it with POST …/hangup.
| Permission | phone.calls (Owner, Admin, Staff) |
| Risk | sensitive, OAuth scope skuflow.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access. |
| MCP tool | calls_place (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 |
|---|---|---|---|
to | string or null | No | The number to call; default the customer's first phone. Up to 32 characters. |
customerId | string or null | No | |
contactMethodId | string or null | No | One of the customer's phone numbers. |
phoneNumberId | string or null | No | The business number shown to the customer. |
requestId | string or null | No | |
via | enum | No | One of BROWSER, PHONE. Default BROWSER. |
bridgeNumber | string or null | No | 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/calls" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"to":"2026-11-03","customerId":"customer_id","contactMethodId":"contactMethod_id"}'Response
201: the answer's fields
| Field | Type | Description |
|---|---|---|
call | object | |
call.id | string | |
call.direction | enum | One of INBOUND, OUTBOUND. |
call.status | enum | One of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED. |
call.fake | boolean | A simulated call (SKUFLOW_VOICE=fake). |
call.fromNumber | string | |
call.toNumber | string | |
call.otherNumber | string | The customer's side of the call. |
call.phoneNumber | object or null | |
call.phoneNumber.id | string | |
call.phoneNumber.label | string | |
call.phoneNumber.e164 | string | |
call.source | enum or null | The tracked source of the number called; null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
call.campaign | object or null | |
call.campaign.id | string | |
call.campaign.name | string | |
call.businessUnit | object or null | |
call.businessUnit.id | string | |
call.businessUnit.name | string | |
call.customer | object or null | |
call.customer.id | string | |
call.customer.name | string | |
call.matchedCustomerCount | integer | |
call.user | object or null | Who answered (inbound) or placed it (outbound). |
call.user.id | string | |
call.user.name | string | |
call.bridgeNumber | string or null | |
call.ivrDigit | string or null | |
call.afterHours | boolean | |
call.startedAt | string (date-time) | |
call.answeredAt | string (date-time) or null | |
call.endedAt | string (date-time) or null | |
call.durationSeconds | integer or null | Talk time. |
call.billedMinutes | integer or null | |
call.voicemail | boolean | |
call.recording | object | |
call.recording.status | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
call.recording.announced | boolean | |
call.recording.durationSeconds | integer or null | |
call.recording.available | boolean | |
call.recording.deletedAt | string (date-time) or null | |
call.recording.error | string or null | |
call.transcriptStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
call.transcript | string or null | Only for callers holding phone.recordings, on GET /phone/calls/{callId}. |
call.summaryStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
call.summary | string or null | The AI call summary (Claude, from the transcript). |
call.summaryError | string or null | |
call.followUp | object | |
call.followUp.status | enum | One of NONE, OPEN, DONE. |
call.followUp.assignee | object or null | |
call.followUp.doneAt | string (date-time) or null | |
call.textBack | object or null | |
call.textBack.status | string | |
call.textBack.messageId | string or null | |
call.request | object or null | |
call.request.id | string | |
call.request.identifier | string | |
call.notes | string or null | |
call.createdAt | string (date-time) | |
call.updatedAt | string (date-time) | |
connect | object | |
connect.mode | enum | One of BROWSER, PHONE, FAKE. |
connect.params | object | |
connect.params.callId | string |
A call
GET /organizations/{organizationId}/phone/calls/{callId}
One call with its summary; the full transcript only for callers holding phone.recordings (recordings and transcripts are sensitive). Read-only.
| Permission | phone.read (Owner, Admin, Staff, Member) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | calls_get (toolset marketing) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
callId | path | string | Yes |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
direction | enum | One of INBOUND, OUTBOUND. |
status | enum | One of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED. |
fake | boolean | A simulated call (SKUFLOW_VOICE=fake). |
fromNumber | string | |
toNumber | string | |
otherNumber | string | The customer's side of the call. |
phoneNumber | object or null | |
phoneNumber.id | string | |
phoneNumber.label | string | |
phoneNumber.e164 | string | |
source | enum or null | The tracked source of the number called; null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
campaign | object or null | |
campaign.id | string | |
campaign.name | string | |
businessUnit | object or null | |
businessUnit.id | string | |
businessUnit.name | string | |
customer | object or null | |
customer.id | string | |
customer.name | string | |
matchedCustomerCount | integer | |
user | object or null | Who answered (inbound) or placed it (outbound). |
user.id | string | |
user.name | string | |
bridgeNumber | string or null | |
ivrDigit | string or null | |
afterHours | boolean | |
startedAt | string (date-time) | |
answeredAt | string (date-time) or null | |
endedAt | string (date-time) or null | |
durationSeconds | integer or null | Talk time. |
billedMinutes | integer or null | |
voicemail | boolean | |
recording | object | |
recording.status | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
recording.announced | boolean | |
recording.durationSeconds | integer or null | |
recording.available | boolean | |
recording.deletedAt | string (date-time) or null | |
recording.error | string or null | |
transcriptStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
transcript | string or null | Only for callers holding phone.recordings, on GET /phone/calls/{callId}. |
summaryStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
summary | string or null | The AI call summary (Claude, from the transcript). |
summaryError | string or null | |
followUp | object | |
followUp.status | enum | One of NONE, OPEN, DONE. |
followUp.assignee | object or null | |
followUp.assignee.id | string | |
followUp.assignee.name | string | |
followUp.doneAt | string (date-time) or null | |
textBack | object or null | |
textBack.status | string | |
textBack.messageId | string or null | |
request | object or null | |
request.id | string | |
request.identifier | string | |
notes | string or null | |
createdAt | string (date-time) | |
updatedAt | string (date-time) |
Notes, customer and follow-up of a call
PATCH /organizations/{organizationId}/phone/calls/{callId}
Add notes, set the customer the call was with (when caller ID matched none or several), and work the missed-call follow-up: assign it, mark it DONE (or reopen it).
| Permission | phone.calls (Owner, Admin, Staff) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | calls_update (toolset marketing) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
callId | path | string | Yes |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
notes | string or null | No | Up to 4000 characters. |
customerId | string or null | No | |
followUpStatus | enum | No | One of OPEN, DONE, NONE. |
followUpAssigneeId | string or null | No | A member with phone access. |
Example
curl -X PATCH "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"notes":"string","customerId":"customer_id","followUpStatus":"OPEN"}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
direction | enum | One of INBOUND, OUTBOUND. |
status | enum | One of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED. |
fake | boolean | A simulated call (SKUFLOW_VOICE=fake). |
fromNumber | string | |
toNumber | string | |
otherNumber | string | The customer's side of the call. |
phoneNumber | object or null | |
phoneNumber.id | string | |
phoneNumber.label | string | |
phoneNumber.e164 | string | |
source | enum or null | The tracked source of the number called; null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
campaign | object or null | |
campaign.id | string | |
campaign.name | string | |
businessUnit | object or null | |
businessUnit.id | string | |
businessUnit.name | string | |
customer | object or null | |
customer.id | string | |
customer.name | string | |
matchedCustomerCount | integer | |
user | object or null | Who answered (inbound) or placed it (outbound). |
user.id | string | |
user.name | string | |
bridgeNumber | string or null | |
ivrDigit | string or null | |
afterHours | boolean | |
startedAt | string (date-time) | |
answeredAt | string (date-time) or null | |
endedAt | string (date-time) or null | |
durationSeconds | integer or null | Talk time. |
billedMinutes | integer or null | |
voicemail | boolean | |
recording | object | |
recording.status | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
recording.announced | boolean | |
recording.durationSeconds | integer or null | |
recording.available | boolean | |
recording.deletedAt | string (date-time) or null | |
recording.error | string or null | |
transcriptStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
transcript | string or null | Only for callers holding phone.recordings, on GET /phone/calls/{callId}. |
summaryStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
summary | string or null | The AI call summary (Claude, from the transcript). |
summaryError | string or null | |
followUp | object | |
followUp.status | enum | One of NONE, OPEN, DONE. |
followUp.assignee | object or null | |
followUp.assignee.id | string | |
followUp.assignee.name | string | |
followUp.doneAt | string (date-time) or null | |
textBack | object or null | |
textBack.status | string | |
textBack.messageId | string or null | |
request | object or null | |
request.id | string | |
request.identifier | string | |
notes | string or null | |
createdAt | string (date-time) | |
updatedAt | string (date-time) |
Screen pop
GET /organizations/{organizationId}/phone/calls/{callId}/context
Who is calling: every customer whose contact has the caller's number (the first is chosen, or the one staff linked), and that customer's Customer 360 summary (health, a few lines), open quotes and balance. Record what the call led to with POST …/link. Read-only.
| Permission | phone.calls (Owner, Admin, Staff) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | calls_context (toolset marketing) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
callId | path | string | Yes |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}/context" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
call | object | |
call.id | string | |
call.direction | enum | One of INBOUND, OUTBOUND. |
call.status | enum | One of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED. |
call.fake | boolean | A simulated call (SKUFLOW_VOICE=fake). |
call.fromNumber | string | |
call.toNumber | string | |
call.otherNumber | string | The customer's side of the call. |
call.phoneNumber | object or null | |
call.phoneNumber.id | string | |
call.phoneNumber.label | string | |
call.phoneNumber.e164 | string | |
call.source | enum or null | The tracked source of the number called; null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
call.campaign | object or null | |
call.campaign.id | string | |
call.campaign.name | string | |
call.businessUnit | object or null | |
call.businessUnit.id | string | |
call.businessUnit.name | string | |
call.customer | object or null | |
call.customer.id | string | |
call.customer.name | string | |
call.matchedCustomerCount | integer | |
call.user | object or null | Who answered (inbound) or placed it (outbound). |
call.user.id | string | |
call.user.name | string | |
call.bridgeNumber | string or null | |
call.ivrDigit | string or null | |
call.afterHours | boolean | |
call.startedAt | string (date-time) | |
call.answeredAt | string (date-time) or null | |
call.endedAt | string (date-time) or null | |
call.durationSeconds | integer or null | Talk time. |
call.billedMinutes | integer or null | |
call.voicemail | boolean | |
call.recording | object | |
call.recording.status | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
call.recording.announced | boolean | |
call.recording.durationSeconds | integer or null | |
call.recording.available | boolean | |
call.recording.deletedAt | string (date-time) or null | |
call.recording.error | string or null | |
call.transcriptStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
call.transcript | string or null | Only for callers holding phone.recordings, on GET /phone/calls/{callId}. |
call.summaryStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
call.summary | string or null | The AI call summary (Claude, from the transcript). |
call.summaryError | string or null | |
call.followUp | object | |
call.followUp.status | enum | One of NONE, OPEN, DONE. |
call.followUp.assignee | object or null | |
call.followUp.doneAt | string (date-time) or null | |
call.textBack | object or null | |
call.textBack.status | string | |
call.textBack.messageId | string or null | |
call.request | object or null | |
call.request.id | string | |
call.request.identifier | string | |
call.notes | string or null | |
call.createdAt | string (date-time) | |
call.updatedAt | string (date-time) | |
matches | array of object | |
matches[].id | string | |
matches[].name | string | |
matches[].type | string | |
customer | object or null | |
customer.id | string | |
customer.name | string | |
customer.type | string | |
customer.status | string | |
customer.city | string or null | |
customer.balanceCents | integer | |
customer.lifetimeValueCents | integer | |
customer.health | object or null | |
customer.health.score | number | |
customer.health.band | string | |
customer.lines | array of string | Customer 360's few lines about them. |
customer.openQuotes | array of object | |
customer.openQuotes[].id | string | |
customer.openQuotes[].identifier | string | Q-n |
customer.openQuotes[].title | string | |
customer.openQuotes[].status | string | |
customer.previousCalls | integer |
Answer a ringing call (simulated phone)
POST /organizations/{organizationId}/phone/calls/{callId}/answer
Simulated phone only (SKUFLOW_VOICE=fake; with Twilio the browser phone answers through the Voice SDK): answer a call ringing for your ring group.
| Permission | phone.calls (Owner, Admin, Staff) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | calls_answer (toolset marketing) |
| Plan | Needs the Phone system add-on. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
callId | path | string | Yes |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}/answer" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
direction | enum | One of INBOUND, OUTBOUND. |
status | enum | One of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED. |
fake | boolean | A simulated call (SKUFLOW_VOICE=fake). |
fromNumber | string | |
toNumber | string | |
otherNumber | string | The customer's side of the call. |
phoneNumber | object or null | |
phoneNumber.id | string | |
phoneNumber.label | string | |
phoneNumber.e164 | string | |
source | enum or null | The tracked source of the number called; null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
campaign | object or null | |
campaign.id | string | |
campaign.name | string | |
businessUnit | object or null | |
businessUnit.id | string | |
businessUnit.name | string | |
customer | object or null | |
customer.id | string | |
customer.name | string | |
matchedCustomerCount | integer | |
user | object or null | Who answered (inbound) or placed it (outbound). |
user.id | string | |
user.name | string | |
bridgeNumber | string or null | |
ivrDigit | string or null | |
afterHours | boolean | |
startedAt | string (date-time) | |
answeredAt | string (date-time) or null | |
endedAt | string (date-time) or null | |
durationSeconds | integer or null | Talk time. |
billedMinutes | integer or null | |
voicemail | boolean | |
recording | object | |
recording.status | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
recording.announced | boolean | |
recording.durationSeconds | integer or null | |
recording.available | boolean | |
recording.deletedAt | string (date-time) or null | |
recording.error | string or null | |
transcriptStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
transcript | string or null | Only for callers holding phone.recordings, on GET /phone/calls/{callId}. |
summaryStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
summary | string or null | The AI call summary (Claude, from the transcript). |
summaryError | string or null | |
followUp | object | |
followUp.status | enum | One of NONE, OPEN, DONE. |
followUp.assignee | object or null | |
followUp.assignee.id | string | |
followUp.assignee.name | string | |
followUp.doneAt | string (date-time) or null | |
textBack | object or null | |
textBack.status | string | |
textBack.messageId | string or null | |
request | object or null | |
request.id | string | |
request.identifier | string | |
notes | string or null | |
createdAt | string (date-time) | |
updatedAt | string (date-time) |
Hang up
POST /organizations/{organizationId}/phone/calls/{callId}/hangup
End a ringing or live call (Twilio: every leg; the status callback finishes the log). A ringing call that nobody answered ends as missed.
| Permission | phone.calls (Owner, Admin, Staff) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | calls_hangup (toolset marketing) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
callId | path | string | Yes |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}/hangup" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
direction | enum | One of INBOUND, OUTBOUND. |
status | enum | One of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED. |
fake | boolean | A simulated call (SKUFLOW_VOICE=fake). |
fromNumber | string | |
toNumber | string | |
otherNumber | string | The customer's side of the call. |
phoneNumber | object or null | |
phoneNumber.id | string | |
phoneNumber.label | string | |
phoneNumber.e164 | string | |
source | enum or null | The tracked source of the number called; null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
campaign | object or null | |
campaign.id | string | |
campaign.name | string | |
businessUnit | object or null | |
businessUnit.id | string | |
businessUnit.name | string | |
customer | object or null | |
customer.id | string | |
customer.name | string | |
matchedCustomerCount | integer | |
user | object or null | Who answered (inbound) or placed it (outbound). |
user.id | string | |
user.name | string | |
bridgeNumber | string or null | |
ivrDigit | string or null | |
afterHours | boolean | |
startedAt | string (date-time) | |
answeredAt | string (date-time) or null | |
endedAt | string (date-time) or null | |
durationSeconds | integer or null | Talk time. |
billedMinutes | integer or null | |
voicemail | boolean | |
recording | object | |
recording.status | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
recording.announced | boolean | |
recording.durationSeconds | integer or null | |
recording.available | boolean | |
recording.deletedAt | string (date-time) or null | |
recording.error | string or null | |
transcriptStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
transcript | string or null | Only for callers holding phone.recordings, on GET /phone/calls/{callId}. |
summaryStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
summary | string or null | The AI call summary (Claude, from the transcript). |
summaryError | string or null | |
followUp | object | |
followUp.status | enum | One of NONE, OPEN, DONE. |
followUp.assignee | object or null | |
followUp.assignee.id | string | |
followUp.assignee.name | string | |
followUp.doneAt | string (date-time) or null | |
textBack | object or null | |
textBack.status | string | |
textBack.messageId | string or null | |
request | object or null | |
request.id | string | |
request.identifier | string | |
notes | string or null | |
createdAt | string (date-time) | |
updatedAt | string (date-time) |
Link a call to the request it led to
POST /organizations/{organizationId}/phone/calls/{callId}/link
Record what the call led to: a request (counts as booked in the call tracking report, and closes an open follow-up).
| Permission | phone.calls (Owner, Admin, Staff) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | calls_link (toolset marketing) |
| Plan | Needs the Phone system add-on. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
callId | path | string | Yes |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
requestId | string or null | No |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}/link" \
-H "Authorization: Bearer $SKUFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"requestId":"request_id"}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
direction | enum | One of INBOUND, OUTBOUND. |
status | enum | One of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED. |
fake | boolean | A simulated call (SKUFLOW_VOICE=fake). |
fromNumber | string | |
toNumber | string | |
otherNumber | string | The customer's side of the call. |
phoneNumber | object or null | |
phoneNumber.id | string | |
phoneNumber.label | string | |
phoneNumber.e164 | string | |
source | enum or null | The tracked source of the number called; null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
campaign | object or null | |
campaign.id | string | |
campaign.name | string | |
businessUnit | object or null | |
businessUnit.id | string | |
businessUnit.name | string | |
customer | object or null | |
customer.id | string | |
customer.name | string | |
matchedCustomerCount | integer | |
user | object or null | Who answered (inbound) or placed it (outbound). |
user.id | string | |
user.name | string | |
bridgeNumber | string or null | |
ivrDigit | string or null | |
afterHours | boolean | |
startedAt | string (date-time) | |
answeredAt | string (date-time) or null | |
endedAt | string (date-time) or null | |
durationSeconds | integer or null | Talk time. |
billedMinutes | integer or null | |
voicemail | boolean | |
recording | object | |
recording.status | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
recording.announced | boolean | |
recording.durationSeconds | integer or null | |
recording.available | boolean | |
recording.deletedAt | string (date-time) or null | |
recording.error | string or null | |
transcriptStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
transcript | string or null | Only for callers holding phone.recordings, on GET /phone/calls/{callId}. |
summaryStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
summary | string or null | The AI call summary (Claude, from the transcript). |
summaryError | string or null | |
followUp | object | |
followUp.status | enum | One of NONE, OPEN, DONE. |
followUp.assignee | object or null | |
followUp.assignee.id | string | |
followUp.assignee.name | string | |
followUp.doneAt | string (date-time) or null | |
textBack | object or null | |
textBack.status | string | |
textBack.messageId | string or null | |
request | object or null | |
request.id | string | |
request.identifier | string | |
notes | string or null | |
createdAt | string (date-time) | |
updatedAt | string (date-time) |
Play a recording
GET /organizations/{organizationId}/phone/calls/{callId}/recording
Sensitive: ten-minute links to play and download a call's recording or voicemail. 404 when there is none (not recorded, deleted, or past retention).
| Permission | phone.recordings (Owner, Admin) |
| Risk | read, OAuth scope skuflow.read. Read: reads only. |
| MCP tool | calls_recording (toolset marketing) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
callId | path | string | Yes |
Example
curl "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}/recording" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
url | string | |
downloadUrl | string | |
contentType | string | |
expiresAt | string (date-time) |
Delete a recording
DELETE /organizations/{organizationId}/phone/calls/{callId}/recording
Destructive: deletes the call's recording and its transcript from storage (the AI summary stays). Cannot be undone.
| Permission | phone.recordings (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 | calls_deleteRecording (toolset marketing) |
| Approval | Over MCP, this tool always waits for your approval in SKUFlow. See Approvals. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
callId | path | string | Yes |
Example
curl -X DELETE "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}/recording" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
direction | enum | One of INBOUND, OUTBOUND. |
status | enum | One of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED. |
fake | boolean | A simulated call (SKUFLOW_VOICE=fake). |
fromNumber | string | |
toNumber | string | |
otherNumber | string | The customer's side of the call. |
phoneNumber | object or null | |
phoneNumber.id | string | |
phoneNumber.label | string | |
phoneNumber.e164 | string | |
source | enum or null | The tracked source of the number called; null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
campaign | object or null | |
campaign.id | string | |
campaign.name | string | |
businessUnit | object or null | |
businessUnit.id | string | |
businessUnit.name | string | |
customer | object or null | |
customer.id | string | |
customer.name | string | |
matchedCustomerCount | integer | |
user | object or null | Who answered (inbound) or placed it (outbound). |
user.id | string | |
user.name | string | |
bridgeNumber | string or null | |
ivrDigit | string or null | |
afterHours | boolean | |
startedAt | string (date-time) | |
answeredAt | string (date-time) or null | |
endedAt | string (date-time) or null | |
durationSeconds | integer or null | Talk time. |
billedMinutes | integer or null | |
voicemail | boolean | |
recording | object | |
recording.status | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
recording.announced | boolean | |
recording.durationSeconds | integer or null | |
recording.available | boolean | |
recording.deletedAt | string (date-time) or null | |
recording.error | string or null | |
transcriptStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
transcript | string or null | Only for callers holding phone.recordings, on GET /phone/calls/{callId}. |
summaryStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
summary | string or null | The AI call summary (Claude, from the transcript). |
summaryError | string or null | |
followUp | object | |
followUp.status | enum | One of NONE, OPEN, DONE. |
followUp.assignee | object or null | |
followUp.assignee.id | string | |
followUp.assignee.name | string | |
followUp.doneAt | string (date-time) or null | |
textBack | object or null | |
textBack.status | string | |
textBack.messageId | string or null | |
request | object or null | |
request.id | string | |
request.identifier | string | |
notes | string or null | |
createdAt | string (date-time) | |
updatedAt | string (date-time) |
Write the AI summary again
POST /organizations/{organizationId}/phone/calls/{callId}/summary
Ask Claude for the call's summary again from its transcript (metered against AI credits). 409 while the call is live or without a transcript.
| Permission | phone.calls (Owner, Admin, Staff) |
| Risk | write, OAuth scope skuflow.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | calls_summarize (toolset marketing) |
| Plan | Needs the Phone system add-on. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
callId | path | string | Yes |
Example
curl -X POST "https://app.skuflow.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}/summary" \
-H "Authorization: Bearer $SKUFLOW_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | |
direction | enum | One of INBOUND, OUTBOUND. |
status | enum | One of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED. |
fake | boolean | A simulated call (SKUFLOW_VOICE=fake). |
fromNumber | string | |
toNumber | string | |
otherNumber | string | The customer's side of the call. |
phoneNumber | object or null | |
phoneNumber.id | string | |
phoneNumber.label | string | |
phoneNumber.e164 | string | |
source | enum or null | The tracked source of the number called; null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER. |
campaign | object or null | |
campaign.id | string | |
campaign.name | string | |
businessUnit | object or null | |
businessUnit.id | string | |
businessUnit.name | string | |
customer | object or null | |
customer.id | string | |
customer.name | string | |
matchedCustomerCount | integer | |
user | object or null | Who answered (inbound) or placed it (outbound). |
user.id | string | |
user.name | string | |
bridgeNumber | string or null | |
ivrDigit | string or null | |
afterHours | boolean | |
startedAt | string (date-time) | |
answeredAt | string (date-time) or null | |
endedAt | string (date-time) or null | |
durationSeconds | integer or null | Talk time. |
billedMinutes | integer or null | |
voicemail | boolean | |
recording | object | |
recording.status | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
recording.announced | boolean | |
recording.durationSeconds | integer or null | |
recording.available | boolean | |
recording.deletedAt | string (date-time) or null | |
recording.error | string or null | |
transcriptStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
transcript | string or null | Only for callers holding phone.recordings, on GET /phone/calls/{callId}. |
summaryStatus | enum | One of NONE, PENDING, DONE, FAILED, SKIPPED. |
summary | string or null | The AI call summary (Claude, from the transcript). |
summaryError | string or null | |
followUp | object | |
followUp.status | enum | One of NONE, OPEN, DONE. |
followUp.assignee | object or null | |
followUp.assignee.id | string | |
followUp.assignee.name | string | |
followUp.doneAt | string (date-time) or null | |
textBack | object or null | |
textBack.status | string | |
textBack.messageId | string or null | |
request | object or null | |
request.id | string | |
request.identifier | string | |
notes | string or null | |
createdAt | string (date-time) | |
updatedAt | string (date-time) |
Phone ring groups API
REST endpoints for phone ring groups in SKUFlow: 4 operations with parameters, request and response fields, permissions and curl examples.
Phone reports API
REST endpoints for phone reports in SKUFlow: 1 operations with parameters, request and response fields, permissions and curl examples.