DevelopersAPI referencePhone

Calls API

REST endpoints for calls in ServFlo: 11 operations with parameters, request and response fields, permissions and curl examples.

Base URL: https://app.servflo.ai/api. Every call needs Authorization: Bearer sfk_… (Authentication); {organizationId} ($ORGANIZATION_ID in the examples) is your business's id.

OperationEndpointRisk
Call logGET /organizations/{organizationId}/phone/callsread
Call a customer (click-to-call)POST /organizations/{organizationId}/phone/callssensitive
A callGET /organizations/{organizationId}/phone/calls/{callId}read
Notes, customer and follow-up of a callPATCH /organizations/{organizationId}/phone/calls/{callId}write
Screen popGET /organizations/{organizationId}/phone/calls/{callId}/contextread
Answer a ringing call (simulated phone)POST /organizations/{organizationId}/phone/calls/{callId}/answerwrite
Hang upPOST /organizations/{organizationId}/phone/calls/{callId}/hangupwrite
Link a call to the job it bookedPOST /organizations/{organizationId}/phone/calls/{callId}/linkwrite
Play a recordingGET /organizations/{organizationId}/phone/calls/{callId}/recordingread
Delete a recordingDELETE /organizations/{organizationId}/phone/calls/{callId}/recordingsensitive
Write the AI summary againPOST /organizations/{organizationId}/phone/calls/{callId}/summarywrite

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, ai for only the calls the AI receptionist answered, 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.

Permissionphone.read (Owner, Admin, Dispatcher, Member)
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolcalls_list (toolset marketing)

Parameters

NameInTypeRequiredDescription
directionqueryenumNoOne of INBOUND, OUTBOUND.
outcomequeryenumNoOne of ANSWERED, MISSED, VOICEMAIL, LIVE.
followUpqueryenumNoOne of OPEN, DONE.
aiqueryboolean or enumNoOnly calls the AI receptionist answered.
customerIdquerystringNo
phoneNumberIdquerystringNo
sourcequeryenumNoOne of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER, PERSONAL, UNTRACKED.
fromquerystringNoA calendar date in the business's own calendar, YYYY-MM-DD.
toquerystringNoA calendar date in the business's own calendar, YYYY-MM-DD.
limitqueryintegerNo1–200.
offsetqueryintegerNo

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/phone/calls" \
  -H "Authorization: Bearer $SERVFLO_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 (SERVFLO_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 (PERSONAL: someone's own line); null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER, PERSONAL.
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[].jobobject or null
items[].job.idstring
items[].job.identifierstring
items[].requestobject or null
items[].request.idstring
items[].request.identifierstring
items[].notesstring or null
items[].aiobject or nullSet when the call was routed to the AI receptionist.
items[].ai.startedAtstring (date-time) or null
items[].ai.endedAtstring (date-time) or null
items[].ai.outcomestring or nullWhat the AI receptionist did: BOOKED, RESCHEDULED, REQUEST, MESSAGE, TRANSFERRED, ENDED, ABANDONED, FAILED, TIMEOUT, DRAINED; null while live.
items[].ai.callerTurnsinteger
items[].ai.voiceSecondsinteger or nullAI voice seconds (metered as ai_voice.minutes).
items[].ai.messagestring or nullA message it took for the office.
items[].ai.turnsarray of object or nullThe conversation; only for callers holding phone.recordings, on GET /phone/calls/{callId}.
items[].createdAtstring (date-time)
items[].updatedAtstring (date-time)
totalinteger
openFollowUpsinteger

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. jobId / requestId record what the call was about. Fake mode: the customer answers at once; end it with POST …/hangup.

Permission: phone.calls (Owner, Admin, Dispatcher, Technician (own jobs)); risk sensitive (OAuth scope servflo.sensitive).

Permissionphone.calls
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolcalls_place (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 ServFlo). See Approvals.

Request body

FieldTypeRequiredDescription
tostring or nullNoThe number to call; default the customer's first phone. Up to 32 characters.
customerIdstring or nullNo
contactMethodIdstring or nullNoOne of the customer's phone numbers.
phoneNumberIdstring or nullNoThe business number shown to the customer.
jobIdstring or nullNo
requestIdstring or nullNo
viaenumNoOne of BROWSER, PHONE. Default BROWSER.
bridgeNumberstring or nullNoA phone number; +E.164 is safest (+13125550100). 10 digits are read as US. Up to 32 characters.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/phone/calls" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to":"2026-11-03","customerId":"customer_id","contactMethodId":"contactMethod_id"}'

Response

201: the answer's fields
FieldTypeDescription
callobject
call.idstring
call.directionenumOne of INBOUND, OUTBOUND.
call.statusenumOne of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED.
call.fakebooleanA simulated call (SERVFLO_VOICE=fake).
call.fromNumberstring
call.toNumberstring
call.otherNumberstringThe customer's side of the call.
call.phoneNumberobject or null
call.phoneNumber.idstring
call.phoneNumber.labelstring
call.phoneNumber.e164string
call.sourceenum or nullThe tracked source of the number called (PERSONAL: someone's own line); null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER, PERSONAL.
call.campaignobject or null
call.campaign.idstring
call.campaign.namestring
call.businessUnitobject or null
call.businessUnit.idstring
call.businessUnit.namestring
call.customerobject or null
call.customer.idstring
call.customer.namestring
call.matchedCustomerCountinteger
call.userobject or nullWho answered (inbound) or placed it (outbound).
call.user.idstring
call.user.namestring
call.bridgeNumberstring or null
call.ivrDigitstring or null
call.afterHoursboolean
call.startedAtstring (date-time)
call.answeredAtstring (date-time) or null
call.endedAtstring (date-time) or null
call.durationSecondsinteger or nullTalk time.
call.billedMinutesinteger or null
call.voicemailboolean
call.recordingobject
call.recording.statusenumOne of NONE, PENDING, DONE, FAILED, SKIPPED.
call.recording.announcedboolean
call.recording.durationSecondsinteger or null
call.recording.availableboolean
call.recording.deletedAtstring (date-time) or null
call.recording.errorstring or null
call.transcriptStatusenumOne of NONE, PENDING, DONE, FAILED, SKIPPED.
call.transcriptstring or nullOnly for callers holding phone.recordings, on GET /phone/calls/{callId}.
call.summaryStatusenumOne of NONE, PENDING, DONE, FAILED, SKIPPED.
call.summarystring or nullThe AI call summary (Claude, from the transcript).
call.summaryErrorstring or null
call.followUpobject
call.followUp.statusenumOne of NONE, OPEN, DONE.
call.followUp.assigneeobject or null
call.followUp.doneAtstring (date-time) or null
call.textBackobject or null
call.textBack.statusstring
call.textBack.messageIdstring or null
call.jobobject or null
call.job.idstring
call.job.identifierstring
call.requestobject or null
call.request.idstring
call.request.identifierstring
call.notesstring or null
call.aiobject or nullSet when the call was routed to the AI receptionist.
call.ai.startedAtstring (date-time) or null
call.ai.endedAtstring (date-time) or null
call.ai.outcomestring or nullWhat the AI receptionist did: BOOKED, RESCHEDULED, REQUEST, MESSAGE, TRANSFERRED, ENDED, ABANDONED, FAILED, TIMEOUT, DRAINED; null while live.
call.ai.callerTurnsinteger
call.ai.voiceSecondsinteger or nullAI voice seconds (metered as ai_voice.minutes).
call.ai.messagestring or nullA message it took for the office.
call.ai.turnsarray of object or nullThe conversation; only for callers holding phone.recordings, on GET /phone/calls/{callId}.
call.createdAtstring (date-time)
call.updatedAtstring (date-time)
connectobject
connect.modeenumOne of BROWSER, PHONE, FAKE.
connect.paramsobject
connect.params.callIdstring

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.

Permissionphone.read (Owner, Admin, Dispatcher, Member)
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolcalls_get (toolset marketing)

Parameters

NameInTypeRequiredDescription
callIdpathstringYes

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
idstring
directionenumOne of INBOUND, OUTBOUND.
statusenumOne of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED.
fakebooleanA simulated call (SERVFLO_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 (PERSONAL: someone's own line); null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER, PERSONAL.
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
jobobject or null
job.idstring
job.identifierstring
requestobject or null
request.idstring
request.identifierstring
notesstring or null
aiobject or nullSet when the call was routed to the AI receptionist.
ai.startedAtstring (date-time) or null
ai.endedAtstring (date-time) or null
ai.outcomestring or nullWhat the AI receptionist did: BOOKED, RESCHEDULED, REQUEST, MESSAGE, TRANSFERRED, ENDED, ABANDONED, FAILED, TIMEOUT, DRAINED; null while live.
ai.callerTurnsinteger
ai.voiceSecondsinteger or nullAI voice seconds (metered as ai_voice.minutes).
ai.messagestring or nullA message it took for the office.
ai.turnsarray of object or nullThe conversation; only for callers holding phone.recordings, on GET /phone/calls/{callId}.
ai.turns[].roleenumOne of caller, assistant, tool.
ai.turns[].textstring
ai.turns[].atstring
ai.turns[].toolstring
createdAtstring (date-time)
updatedAtstring (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, Dispatcher, Technician (own jobs)); risk write (OAuth scope servflo.write).

Permissionphone.calls
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolcalls_update (toolset marketing)

Parameters

NameInTypeRequiredDescription
callIdpathstringYes

Request body

FieldTypeRequiredDescription
notesstring or nullNoUp to 4000 characters.
customerIdstring or nullNo
followUpStatusenumNoOne of OPEN, DONE, NONE.
followUpAssigneeIdstring or nullNoA member with phone access.

Example

curl -X PATCH "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"notes":"string","customerId":"customer_id","followUpStatus":"OPEN"}'

Response

200: the answer's fields
FieldTypeDescription
idstring
directionenumOne of INBOUND, OUTBOUND.
statusenumOne of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED.
fakebooleanA simulated call (SERVFLO_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 (PERSONAL: someone's own line); null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER, PERSONAL.
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
jobobject or null
job.idstring
job.identifierstring
requestobject or null
request.idstring
request.identifierstring
notesstring or null
aiobject or nullSet when the call was routed to the AI receptionist.
ai.startedAtstring (date-time) or null
ai.endedAtstring (date-time) or null
ai.outcomestring or nullWhat the AI receptionist did: BOOKED, RESCHEDULED, REQUEST, MESSAGE, TRANSFERRED, ENDED, ABANDONED, FAILED, TIMEOUT, DRAINED; null while live.
ai.callerTurnsinteger
ai.voiceSecondsinteger or nullAI voice seconds (metered as ai_voice.minutes).
ai.messagestring or nullA message it took for the office.
ai.turnsarray of object or nullThe conversation; only for callers holding phone.recordings, on GET /phone/calls/{callId}.
ai.turns[].roleenumOne of caller, assistant, tool.
ai.turns[].textstring
ai.turns[].atstring
ai.turns[].toolstring
createdAtstring (date-time)
updatedAtstring (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, membership, last / next visit, a few lines), open jobs and balance. Book the job from here with the CSR screen, then POST …/link. Read-only.

Permission: phone.calls (Owner, Admin, Dispatcher, Technician (own jobs)); risk read (OAuth scope servflo.read).

Permissionphone.calls
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolcalls_context (toolset marketing)

Parameters

NameInTypeRequiredDescription
callIdpathstringYes

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}/context" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
callobject
call.idstring
call.directionenumOne of INBOUND, OUTBOUND.
call.statusenumOne of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED.
call.fakebooleanA simulated call (SERVFLO_VOICE=fake).
call.fromNumberstring
call.toNumberstring
call.otherNumberstringThe customer's side of the call.
call.phoneNumberobject or null
call.phoneNumber.idstring
call.phoneNumber.labelstring
call.phoneNumber.e164string
call.sourceenum or nullThe tracked source of the number called (PERSONAL: someone's own line); null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER, PERSONAL.
call.campaignobject or null
call.campaign.idstring
call.campaign.namestring
call.businessUnitobject or null
call.businessUnit.idstring
call.businessUnit.namestring
call.customerobject or null
call.customer.idstring
call.customer.namestring
call.matchedCustomerCountinteger
call.userobject or nullWho answered (inbound) or placed it (outbound).
call.user.idstring
call.user.namestring
call.bridgeNumberstring or null
call.ivrDigitstring or null
call.afterHoursboolean
call.startedAtstring (date-time)
call.answeredAtstring (date-time) or null
call.endedAtstring (date-time) or null
call.durationSecondsinteger or nullTalk time.
call.billedMinutesinteger or null
call.voicemailboolean
call.recordingobject
call.recording.statusenumOne of NONE, PENDING, DONE, FAILED, SKIPPED.
call.recording.announcedboolean
call.recording.durationSecondsinteger or null
call.recording.availableboolean
call.recording.deletedAtstring (date-time) or null
call.recording.errorstring or null
call.transcriptStatusenumOne of NONE, PENDING, DONE, FAILED, SKIPPED.
call.transcriptstring or nullOnly for callers holding phone.recordings, on GET /phone/calls/{callId}.
call.summaryStatusenumOne of NONE, PENDING, DONE, FAILED, SKIPPED.
call.summarystring or nullThe AI call summary (Claude, from the transcript).
call.summaryErrorstring or null
call.followUpobject
call.followUp.statusenumOne of NONE, OPEN, DONE.
call.followUp.assigneeobject or null
call.followUp.doneAtstring (date-time) or null
call.textBackobject or null
call.textBack.statusstring
call.textBack.messageIdstring or null
call.jobobject or null
call.job.idstring
call.job.identifierstring
call.requestobject or null
call.request.idstring
call.request.identifierstring
call.notesstring or null
call.aiobject or nullSet when the call was routed to the AI receptionist.
call.ai.startedAtstring (date-time) or null
call.ai.endedAtstring (date-time) or null
call.ai.outcomestring or nullWhat the AI receptionist did: BOOKED, RESCHEDULED, REQUEST, MESSAGE, TRANSFERRED, ENDED, ABANDONED, FAILED, TIMEOUT, DRAINED; null while live.
call.ai.callerTurnsinteger
call.ai.voiceSecondsinteger or nullAI voice seconds (metered as ai_voice.minutes).
call.ai.messagestring or nullA message it took for the office.
call.ai.turnsarray of object or nullThe conversation; only for callers holding phone.recordings, on GET /phone/calls/{callId}.
call.createdAtstring (date-time)
call.updatedAtstring (date-time)
matchesarray of object
matches[].idstring
matches[].namestring
matches[].typestring
customerobject or null
customer.idstring
customer.namestring
customer.typestring
customer.statusstring
customer.citystring or null
customer.balanceCentsinteger
customer.lifetimeValueCentsinteger
customer.healthobject or null
customer.health.scorenumber
customer.health.bandstring
customer.membershipobject or null
customer.membership.planNamestring
customer.membership.statusstring
customer.lastVisitDatestring or null
customer.nextVisitDatestring or null
customer.linesarray of stringCustomer 360's few lines about them.
customer.openJobsarray of object
customer.openJobs[].idstring
customer.openJobs[].identifierstring
customer.openJobs[].titlestring or null
customer.openJobs[].statusstring
customer.openJobs[].scheduledDatestring
customer.openJobs[].scheduledTimestring or null
customer.previousCallsinteger

Answer a ringing call (simulated phone)

POST /organizations/{organizationId}/phone/calls/{callId}/answer

Simulated phone only (SERVFLO_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, Dispatcher, Technician (own jobs)); risk write (OAuth scope servflo.write).

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

Parameters

NameInTypeRequiredDescription
callIdpathstringYes

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}/answer" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
idstring
directionenumOne of INBOUND, OUTBOUND.
statusenumOne of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED.
fakebooleanA simulated call (SERVFLO_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 (PERSONAL: someone's own line); null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER, PERSONAL.
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
jobobject or null
job.idstring
job.identifierstring
requestobject or null
request.idstring
request.identifierstring
notesstring or null
aiobject or nullSet when the call was routed to the AI receptionist.
ai.startedAtstring (date-time) or null
ai.endedAtstring (date-time) or null
ai.outcomestring or nullWhat the AI receptionist did: BOOKED, RESCHEDULED, REQUEST, MESSAGE, TRANSFERRED, ENDED, ABANDONED, FAILED, TIMEOUT, DRAINED; null while live.
ai.callerTurnsinteger
ai.voiceSecondsinteger or nullAI voice seconds (metered as ai_voice.minutes).
ai.messagestring or nullA message it took for the office.
ai.turnsarray of object or nullThe conversation; only for callers holding phone.recordings, on GET /phone/calls/{callId}.
ai.turns[].roleenumOne of caller, assistant, tool.
ai.turns[].textstring
ai.turns[].atstring
ai.turns[].toolstring
createdAtstring (date-time)
updatedAtstring (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, Dispatcher, Technician (own jobs)); risk write (OAuth scope servflo.write).

Permissionphone.calls
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolcalls_hangup (toolset marketing)

Parameters

NameInTypeRequiredDescription
callIdpathstringYes

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}/hangup" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
idstring
directionenumOne of INBOUND, OUTBOUND.
statusenumOne of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED.
fakebooleanA simulated call (SERVFLO_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 (PERSONAL: someone's own line); null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER, PERSONAL.
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
jobobject or null
job.idstring
job.identifierstring
requestobject or null
request.idstring
request.identifierstring
notesstring or null
aiobject or nullSet when the call was routed to the AI receptionist.
ai.startedAtstring (date-time) or null
ai.endedAtstring (date-time) or null
ai.outcomestring or nullWhat the AI receptionist did: BOOKED, RESCHEDULED, REQUEST, MESSAGE, TRANSFERRED, ENDED, ABANDONED, FAILED, TIMEOUT, DRAINED; null while live.
ai.callerTurnsinteger
ai.voiceSecondsinteger or nullAI voice seconds (metered as ai_voice.minutes).
ai.messagestring or nullA message it took for the office.
ai.turnsarray of object or nullThe conversation; only for callers holding phone.recordings, on GET /phone/calls/{callId}.
ai.turns[].roleenumOne of caller, assistant, tool.
ai.turns[].textstring
ai.turns[].atstring
ai.turns[].toolstring
createdAtstring (date-time)
updatedAtstring (date-time)
POST /organizations/{organizationId}/phone/calls/{callId}/link

Record what the call led to: the job booked from the screen pop (counts as booked in the call tracking report, and closes an open follow-up) or a request.

Permission: phone.calls (Owner, Admin, Dispatcher, Technician (own jobs)); risk write (OAuth scope servflo.write).

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

Parameters

NameInTypeRequiredDescription
callIdpathstringYes

Request body

FieldTypeRequiredDescription
jobIdstring or nullNo
requestIdstring or nullNo

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}/link" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jobId":"job_id","requestId":"request_id"}'

Response

200: the answer's fields
FieldTypeDescription
idstring
directionenumOne of INBOUND, OUTBOUND.
statusenumOne of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED.
fakebooleanA simulated call (SERVFLO_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 (PERSONAL: someone's own line); null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER, PERSONAL.
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
jobobject or null
job.idstring
job.identifierstring
requestobject or null
request.idstring
request.identifierstring
notesstring or null
aiobject or nullSet when the call was routed to the AI receptionist.
ai.startedAtstring (date-time) or null
ai.endedAtstring (date-time) or null
ai.outcomestring or nullWhat the AI receptionist did: BOOKED, RESCHEDULED, REQUEST, MESSAGE, TRANSFERRED, ENDED, ABANDONED, FAILED, TIMEOUT, DRAINED; null while live.
ai.callerTurnsinteger
ai.voiceSecondsinteger or nullAI voice seconds (metered as ai_voice.minutes).
ai.messagestring or nullA message it took for the office.
ai.turnsarray of object or nullThe conversation; only for callers holding phone.recordings, on GET /phone/calls/{callId}.
ai.turns[].roleenumOne of caller, assistant, tool.
ai.turns[].textstring
ai.turns[].atstring
ai.turns[].toolstring
createdAtstring (date-time)
updatedAtstring (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).

Permissionphone.recordings (Owner, Admin)
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolcalls_recording (toolset marketing)

Parameters

NameInTypeRequiredDescription
callIdpathstringYes

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}/recording" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
urlstring
downloadUrlstring
contentTypestring
expiresAtstring (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.

Permissionphone.recordings (Owner, Admin)
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolcalls_deleteRecording (toolset marketing)
ApprovalOver MCP, this tool always waits for your approval in ServFlo. See Approvals.

Parameters

NameInTypeRequiredDescription
callIdpathstringYes

Example

curl -X DELETE "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}/recording" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
idstring
directionenumOne of INBOUND, OUTBOUND.
statusenumOne of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED.
fakebooleanA simulated call (SERVFLO_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 (PERSONAL: someone's own line); null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER, PERSONAL.
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
jobobject or null
job.idstring
job.identifierstring
requestobject or null
request.idstring
request.identifierstring
notesstring or null
aiobject or nullSet when the call was routed to the AI receptionist.
ai.startedAtstring (date-time) or null
ai.endedAtstring (date-time) or null
ai.outcomestring or nullWhat the AI receptionist did: BOOKED, RESCHEDULED, REQUEST, MESSAGE, TRANSFERRED, ENDED, ABANDONED, FAILED, TIMEOUT, DRAINED; null while live.
ai.callerTurnsinteger
ai.voiceSecondsinteger or nullAI voice seconds (metered as ai_voice.minutes).
ai.messagestring or nullA message it took for the office.
ai.turnsarray of object or nullThe conversation; only for callers holding phone.recordings, on GET /phone/calls/{callId}.
ai.turns[].roleenumOne of caller, assistant, tool.
ai.turns[].textstring
ai.turns[].atstring
ai.turns[].toolstring
createdAtstring (date-time)
updatedAtstring (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, Dispatcher, Technician (own jobs)); risk write (OAuth scope servflo.write).

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

Parameters

NameInTypeRequiredDescription
callIdpathstringYes

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/phone/calls/{callId}/summary" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
idstring
directionenumOne of INBOUND, OUTBOUND.
statusenumOne of RINGING, IN_PROGRESS, COMPLETED, MISSED, VOICEMAIL, FAILED.
fakebooleanA simulated call (SERVFLO_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 (PERSONAL: someone's own line); null = untracked. One of MAIN, WEBSITE, GOOGLE_BUSINESS, CAMPAIGN, QR_CODE, BUSINESS_UNIT, PRINT, OTHER, PERSONAL.
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
jobobject or null
job.idstring
job.identifierstring
requestobject or null
request.idstring
request.identifierstring
notesstring or null
aiobject or nullSet when the call was routed to the AI receptionist.
ai.startedAtstring (date-time) or null
ai.endedAtstring (date-time) or null
ai.outcomestring or nullWhat the AI receptionist did: BOOKED, RESCHEDULED, REQUEST, MESSAGE, TRANSFERRED, ENDED, ABANDONED, FAILED, TIMEOUT, DRAINED; null while live.
ai.callerTurnsinteger
ai.voiceSecondsinteger or nullAI voice seconds (metered as ai_voice.minutes).
ai.messagestring or nullA message it took for the office.
ai.turnsarray of object or nullThe conversation; only for callers holding phone.recordings, on GET /phone/calls/{callId}.
ai.turns[].roleenumOne of caller, assistant, tool.
ai.turns[].textstring
ai.turns[].atstring
ai.turns[].toolstring
createdAtstring (date-time)
updatedAtstring (date-time)

On this page