API reference
Calls
Every call of the account, inbound, outbound or from a browser, with its outcome flags, summary, transcript, recording and estimated cost.
List calls
/v1/callsList all calls for the authenticated tenant with pagination, most recently started first. Filter by campaignId to list the calls of a given campaign, by customerPhoneNumber to list the calls exchanged with a given customer, or by outcome (endedReason, minDuration, isReached, isSignificant, isSuccessful, isSilent) to classify results.
Authorization
AuthorizationstringrequiredBearer <API key>, a key created at app.callshift.ai/api-keys.
Query parameters
pageintegerlimitintegeragentIdstring<uuid>campaignIdstring<uuid>customerPhoneNumberstringCustomer phone number in international format. Returns the calls placed to (outbound) or received from (inbound) that number.
statusenum<string>COMPLETEDERRORFORWARDEDIN_PROGRESSREADYREJECTEDSCHEDULEDtypeenum<string>inboundoutboundwebcallfromstring<date-time>tostring<date-time>endedReasonstringExact match on endedReason.
minDurationinteger | nullOnly calls with billingSeconds greater than or equal to this value.
isReachedenum<string>truefalseisSignificantenum<string>truefalseisSuccessfulenum<string>truefalseisSilentenum<string>truefalseResponses
Calls listed successfully
dataobject[]requiredShow child attributes
idstring<uuid>requiredagentIdstring<uuid> | nullrequiredagentNamestring | nullrequiredcustomerPhoneNumberstring | nullrequiredstatusenum<string>requiredCOMPLETEDERRORFORWARDEDIN_PROGRESSREADYREJECTEDSCHEDULEDtypeenum<string>requiredinboundoutboundwebcallstartedAtstring<date-time> | nullrequiredendedAtstring<date-time> | nullrequiredendedReasonstring | nullrequiredWhy the call ended. Known values: agent-line-entity-mismatch, contact-opted-out, cps-limit-exceeded, dial-failed, stuck-in-progress, twilio-call-permission, customer-busy, customer-declined, customer-did-not-answer, customer-number-not-found, customer-unavailable, exceeded-max-duration, max-duration-exceeded, no-international-permission, outbound-call-forbidden, sip-server-error, unknown-reason, voicemail. The value is passed through from the voice provider, so new values can appear; treat unknown values as a generic failure rather than rejecting them.
createdAtstring<date-time>requiredbillingSecondsnumber | nullrequiredBillable duration in seconds, rounded up from the connected time. costEstimationCents prices it at the overage rate of your plan.
costEstimationCentsinteger | nullrequiredIndicative cost of the call in euro cents, rounded to the nearest cent. Not a contractual amount: use it to compare calls and forecast spend, and the billing page for what was actually charged.
It adds up:
billingSecondsat your plan's per-minute overage rate (rounded up to a full minute first on minute-based plans);- the dial fee, on a campaign dial, whatever its outcome.
It ignores the minutes included in your plan: a call they cover is not charged its minutes, but still shows their price here. A dial fee below a cent can round to 0 on a single call.
Null until the call has a billingSeconds, and when your account has no subscription with an overage rate.
recordingUrlstring | nullrequiredDirect, non-expiring URL of the call recording. No authentication is required beyond the URL itself. Null while the call is in progress or when no recording was produced.
summarystring | nullrequiredisSilentboolean | nullrequiredisSuccessfulboolean | nullrequiredisReachedboolean | nullrequiredisSignificantboolean | nullrequiredphoneobject | nullrequiredAgent phone line used for the call. Matches the phone field of the call-ended webhook payload.
Show child attributes
idstring<uuid>requirednumberstring | nullrequiredfromstring | nullrequiredNumber the call originated from: the agent phone for outbound calls, the caller for inbound calls.
tostring | nullrequiredNumber the call was placed to: the customer for outbound calls, the agent phone for inbound calls.
qualificationCriteriaobject | nullrequiredInformation collected during the call, as configured in the agent's call analysis settings. Matches the qualificationCriteria field of the call-ended webhook payload.
pageCountnumberrequiredInvalid query parameters
successenum<boolean>requiredfalseerrorobjectrequiredShow child attributes
nameenum<string>requiredZodErrorissuesobject[]requiredShow child attributes
codestringrequiredpathstring | number[]requiredmessagestringrequiredUnauthorized
errorstringrequiredInternal server error
errorstringrequiredcurl "https://api.callshift.ai/v1/calls?campaignId=550e8400-e29b-41d4-a716-446655440000&isReached=true&limit=50" \
-H "Authorization: Bearer $CALLSHIFT_API_KEY"const res = await fetch("https://api.callshift.ai/v1/calls?campaignId=550e8400-e29b-41d4-a716-446655440000&isReached=true&limit=50", {
headers: {
Authorization: `Bearer ${process.env.CALLSHIFT_API_KEY}`,
},
});
const data = await res.json();import os
import requests
res = requests.get(
"https://api.callshift.ai/v1/calls?campaignId=550e8400-e29b-41d4-a716-446655440000&isReached=true&limit=50",
headers={"Authorization": f"Bearer {os.environ['CALLSHIFT_API_KEY']}"},
)
print(res.json()){
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"agentId": "550e8400-e29b-41d4-a716-446655440000",
"agentName": "Alice",
"customerPhoneNumber": "+33612345678",
"status": "COMPLETED",
"type": "inbound",
"startedAt": "2026-10-30T09:12:00.000Z",
"endedAt": "2026-10-30T09:12:00.000Z",
"endedReason": "customer-did-not-answer",
"createdAt": "2026-10-30T09:12:00.000Z",
"billingSeconds": 84,
"costEstimationCents": 70,
"recordingUrl": "https://recordings.example.com/recordings/<callId>/<recordingId>.ogg",
"summary": "Ada booked a visit for Saturday at 10am and asked for a confirmation SMS.",
"isSilent": true,
"isSuccessful": true,
"isReached": true,
"isSignificant": true,
"phone": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"number": "+33612345678"
},
"from": "+33612345678",
"to": "+33687654321",
"qualificationCriteria": {
"budget": "500k",
"isDecisionMaker": true
}
}
],
"pageCount": 3
}{
"success": false,
"error": {
"name": "ZodError",
"issues": [
{
"code": "invalid_enum_value",
"path": [
"contacts",
0,
"email"
],
"message": "Invalid email"
}
]
}
}{
"error": "Authentication error"
}{
"error": "Internal server error"
}Get call details
/v1/calls/{callId}Retrieve detailed information about a specific call by ID.
Authorization
AuthorizationstringrequiredBearer <API key>, a key created at app.callshift.ai/api-keys.
Path parameters
callIdstring<uuid>requiredResponses
Call details retrieved successfully
idstring<uuid>requiredagentIdstring<uuid> | nullrequiredagentNamestring | nullrequiredcampaignIdstring<uuid> | nullrequiredcustomerPhoneNumberstring | nullrequiredendedReasonstring | nullrequiredWhy the call ended. Known values: agent-line-entity-mismatch, contact-opted-out, cps-limit-exceeded, dial-failed, stuck-in-progress, twilio-call-permission, customer-busy, customer-declined, customer-did-not-answer, customer-number-not-found, customer-unavailable, exceeded-max-duration, max-duration-exceeded, no-international-permission, outbound-call-forbidden, sip-server-error, unknown-reason, voicemail. The value is passed through from the voice provider, so new values can appear; treat unknown values as a generic failure rather than rejecting them.
phoneIdstring<uuid> | nullrequiredphoneobject | nullrequiredAgent phone line used for the call. Matches the phone field of the call-ended webhook payload.
Show child attributes
idstring<uuid>requirednumberstring | nullrequiredfromstring | nullrequiredNumber the call originated from: the agent phone for outbound calls, the caller for inbound calls.
tostring | nullrequiredNumber the call was placed to: the customer for outbound calls, the agent phone for inbound calls.
recordingUrlstring | nullrequiredDirect, non-expiring URL of the call recording. No authentication is required beyond the URL itself. Null while the call is in progress or when no recording was produced.
summarystring | nullrequiredtranscriptstring | nullrequiredPlain-text transcript, one line per turn prefixed by the speaker role. Not yet structured (no per-turn timestamps).
isSilentboolean | nullrequiredisSuccessfulboolean | nullrequiredisReachedboolean | nullrequiredisSignificantboolean | nullrequiredqualificationCriteriaobject | nullrequiredInformation collected during the call, as configured in the agent's call analysis settings. Matches the qualificationCriteria field of the call-ended webhook payload.
createdAtstring<date-time>requiredbillingSecondsnumber | nullrequiredBillable duration in seconds, rounded up from the connected time. costEstimationCents prices it at the overage rate of your plan.
costEstimationCentsinteger | nullrequiredIndicative cost of the call in euro cents, rounded to the nearest cent. Not a contractual amount: use it to compare calls and forecast spend, and the billing page for what was actually charged.
It adds up:
billingSecondsat your plan's per-minute overage rate (rounded up to a full minute first on minute-based plans);- the dial fee, on a campaign dial, whatever its outcome.
It ignores the minutes included in your plan: a call they cover is not charged its minutes, but still shows their price here. A dial fee below a cent can round to 0 on a single call.
Null until the call has a billingSeconds, and when your account has no subscription with an overage rate.
endedAtstring<date-time> | nullrequiredstartedAtstring<date-time> | nullrequiredstatusenum<string>requiredCOMPLETEDERRORFORWARDEDIN_PROGRESSREADYREJECTEDSCHEDULEDtypeenum<string>requiredinboundoutboundwebcallUnauthorized
errorstringrequiredCall not found
errorstringrequiredInternal server error
errorstringrequiredcurl "https://api.callshift.ai/v1/calls/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer $CALLSHIFT_API_KEY"const res = await fetch("https://api.callshift.ai/v1/calls/550e8400-e29b-41d4-a716-446655440000", {
headers: {
Authorization: `Bearer ${process.env.CALLSHIFT_API_KEY}`,
},
});
const data = await res.json();import os
import requests
res = requests.get(
"https://api.callshift.ai/v1/calls/550e8400-e29b-41d4-a716-446655440000",
headers={"Authorization": f"Bearer {os.environ['CALLSHIFT_API_KEY']}"},
)
print(res.json()){
"id": "550e8400-e29b-41d4-a716-446655440000",
"agentId": "550e8400-e29b-41d4-a716-446655440000",
"agentName": "Alice",
"campaignId": "550e8400-e29b-41d4-a716-446655440000",
"customerPhoneNumber": "+33612345678",
"endedReason": "customer-did-not-answer",
"phoneId": "550e8400-e29b-41d4-a716-446655440000",
"phone": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"number": "+33612345678"
},
"from": "+33612345678",
"to": "+33687654321",
"recordingUrl": "https://recordings.example.com/recordings/<callId>/<recordingId>.ogg",
"summary": "Ada booked a visit for Saturday at 10am and asked for a confirmation SMS.",
"transcript": "User: Hello\nAssistant: Hi, how can I help?",
"isSilent": true,
"isSuccessful": true,
"isReached": true,
"isSignificant": true,
"qualificationCriteria": {
"budget": "500k",
"isDecisionMaker": true
},
"createdAt": "2026-10-30T09:12:00.000Z",
"billingSeconds": 84,
"costEstimationCents": 70,
"endedAt": "2026-10-30T09:12:00.000Z",
"startedAt": "2026-10-30T09:12:00.000Z",
"status": "COMPLETED",
"type": "inbound"
}{
"error": "Authentication error"
}{
"error": "Call not found"
}{
"error": "Internal server error"
}