Skip to main content

Endpoint Reference

This is the per‑endpoint reference for the read‑only surface of the Native Focus REST API. Every call below requires a bearer token from POST /authWithApiKey — see Getting Started → Authentication.

Read‑only

Only GET and search‑style POST endpoints are listed. Write, update and delete operations exist on the API but are not documented publicly. Contact support if your use case needs one.

All paths are relative to your tenant's {{baseUrl}} (see Base URLs). All authenticated calls require:

Authorization: Bearer YOUR.BEARER.TOKEN
Content-Type: application/json

Authentication

Authenticate with API key

POST /authWithApiKey

Exchange an API key for a short‑lived bearer JWT.

Body

{
"clientId": 1234567,
"apiKey": "YOUR-API-KEY-XYZ",
"username": "YOUR-INTEGRATION-USERNAME"
}
FieldTypeRequiredNotes
clientIdintegeryesYour Focus account id.
apiKeystringyesAPI key generated in the admin portal.
usernamestringyesAuto‑generated integration username shown next to the API key in admin. The token inherits this user's roles & rights.

Response

{
"token": "eyJhbGciOi...",
"refreshToken": "eyJhbGciOi..."
}

When token expires, call this endpoint again with the same body to get a fresh one.


Recordings

The "recording" resource represents a single communication — a phone call, Teams meeting, SMS message, etc. Most endpoints accept either the numeric interactionId (a.k.a. recordingId) or the source‑system thirdPartyId depending on what they do.

Search recordings

POST /client/recording.search

Search and filter recordings. This is the workhorse endpoint for any integration that needs to find calls.

Required right: Recording.View

Body — every field is optional; combine them to narrow your result set.

FieldTypeNotes
recordingIds{ include?: number[], exclude?: number[] }Filter by internal ids.
thirdPartyIds{ include?: string[], exclude?: string[] }Filter by source ids.
endpointIds{ include?, exclude? }Filter by endpoint (phone/email/Teams id) record.
personIds{ include?, exclude? }Filter by person record.
userIds{ include?, exclude? }Calls owned by these Focus users.
dateTimeRange{ from: ISO8601, to: ISO8601 }Most common filter; use a tight window when paging.
durationRangeInSeconds{ min?: number, max?: number }
timeRanges{ include?, exclude? }Time‑of‑day filters.
communicationsBetweenobjectFilter to interactions between two specific endpoint sets.
freeTextstringSearches transcript / metadata.
communicationTypes{ include?: string[], exclude?: string[] }e.g. phone, teams, sms.
direction"In" | "Out"
tagIds{ include?, exclude? }
isPrivateboolean
hasCommentsboolean
legalHoldIdintegerRestrict to a legal hold.
playlistIdintegerRestrict to a playlist.
startTimeDirection"ASC" | "DESC"Sort direction.
page, pageSizenumberPagination.

Example request

curl -X POST "$BASE_URL/client/recording.search" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"dateTimeRange": { "from": "2026-05-12T00:00:00Z", "to": "2026-05-13T00:00:00Z" },
"communicationTypes": { "include": ["phone", "teams"] },
"direction": "In",
"startTimeDirection": "DESC",
"page": 1,
"pageSize": 100
}'

Example response (abridged)

{
"recordings": [
{
"interactionId": 9876543,
"thirdPartyId": "6414aa9f-ZZZAAABBB",
"startDatetimeUTC": "2026-05-12T10:30:00.000Z",
"endDatetimeUTC": "2026-05-12T10:34:21.482Z",
"durationInSeconds": 261.482,
"type": "phone",
"endpoints": [
{ "endpoint": { "id": 81, "type": "phone", "endpoint": "447896423595" } }
]
}
],
"total": 4321,
"page": 1,
"pageSize": 100
}
Full example response

Fields below match the Interaction shape returned by the API. The exact contents of analysis, tags, dataGroups and optional depend on what your account has configured; the values shown are illustrative.

{
"recordings": [
{
"interactionId": 9876543,
"thirdPartyId": "6414aa9f-ZZZAAABBB",
"startDatetimeUTC": "2026-05-12T10:30:00.000Z",
"endDatetimeUTC": "2026-05-12T10:34:21.482Z",
"durationInSeconds": 261.482,
"type": "phone",
"source": "teams",
"commentsCount": 1,
"transcriptSnippet": "The call focused on resetting the customer's online banking credentials…",
"analysis": ["Customer Service"],
"tags": ["verified_id"],
"conversationPhases": ["greeting", "resolution", "closing"],
"isPrivate": false,
"isPrivateReason": "",
"mediaAccessStatus": "Permitted",
"retentionGroupId": 12,
"retainUntil": "2033-05-12T10:34:21.482Z",
"legalHolds": [],
"dataGroups": { "Department": "Support" },
"optional": {},
"endpoints": [
{
"isOriginator": true,
"isRecordingSubject": true,
"user": "jane.smith@example.com",
"endpoint": { "id": 81, "type": "phone", "endpoint": "447896423595" },
"person": { "id": 42, "fullName": "Jane Smith", "email": "jane.smith@example.com" }
},
{
"isOriginator": false,
"isRecordingSubject": false,
"user": "",
"endpoint": { "id": 9123, "type": "phone", "endpoint": "447700900111" },
"person": { "id": null, "fullName": "", "email": null }
}
]
}
],
"total": 4321,
"page": 1,
"pageSize": 100,
"recordingsSearchToken": "st_01HQ1WHV01BVANN5NW4PG3N573"
}

Get recording detail

POST /client/recording.detail.get

Fetch the full detail blob for one or more recordings (metadata, endpoints, owners, retention info, encryption metadata).

Required right: Recording.View

Body

{ "interactionIds": [9876543, 9876544] }

Returns an array of detail objects keyed on interactionId. Each entry carries the underlying metadata, audio waveForm summary and encryptionDetail blocks for the recording. Use this after recording.search when you need raw metadata that isn't included in the search projection.

Full example response

The metadata, waveForm and encryptionDetail blocks are passthroughs from the underlying recording pipeline — the exact contents vary by communication source (Teams, telephony, SMS, etc.). The example below is illustrative.

[
{
"interactionId": 9876543,
"metadata": {
"accountGuid": "d6b8…",
"recorder": "teams-bot",
"region": "uk",
"sourceFileDetail": {
"mp3": { "fileSize": 4189312 },
"wav": { "fileSize": 41893120 }
}
},
"waveForm": {
"sampleRate": 8,
"peaks": [0.01, 0.42, 0.55, 0.31, 0.18, 0.0]
},
"encryptionDetail": {
"algorithm": "AES-256-GCM",
"keyId": "kek-2026-05"
}
}
]

Get transcription

POST /client/recording.transcription.get

Fetch transcription segments for one or more recordings.

Required right: Recording.Transcription.View

Body

{ "interactionIds": [9876543] }

The response is an array of { interactionId, analysis } objects. The analysis field is the raw output written by the transcription engine for that interaction — typically a transcript containing time‑aligned segments plus a detected language. An empty or null analysis means transcription is disabled, hasn't run yet, or isn't available for that media type.

Full example response

Real payload taken from a call processed by the Focus transcription pipeline. Segment timings are in seconds (floating point); speaker diarisation is not currently included in the public response.

[
{
"interactionId": 9876543,
"analysis": {
"transcript": {
"language": "en",
"segments": [
{
"text": " Hello. Thank you for holding me on. How can I help? Hi, yeah. I want to know what the progress was with my fellow installation…",
"start": 2.585,
"end": 28.933
},
{
"text": " Perfect, it stays like its waiting an install date, so what I can do is Ill pop you over to the install team…",
"start": 30.435,
"end": 45.111
}
]
}
}
}
]

Get SMS message

POST /client/recording.sms-message.get

Fetch the message body for SMS interactions.

Required right: Recording.View

Body

{ "interactionIds": [9876543] }

Use this for type: "sms" interactions where the "audio" is actually a text payload. The response projects the SMS body and any underlying provider message ids out of the recording's metadata.

Full example response
[
{
"interactionId": 9876543,
"message": "Hi — your appointment is confirmed for 14 May at 10:30. Reply STOP to opt out.",
"rawSmsIds": ["sms-7c2f-001"]
}
]

Download audio

POST /client/recording.playBackByThirdPartyId

Stream or download the audio (or video) for a recording. Returns a binary stream — pipe to a file.

Required right: Recording.Play

Body

FieldTypeRequiredNotes
thirdPartyIdstringyesSource‑system id taken from a recording.search result.
formatstringnoOne of mp3 (default), mp3-mono, wav, wav-mono, raw, mp4.
streamThroughbooleannoStream as the file is decrypted instead of buffering.
commentstringconditionalRequired when the account has forced comments enabled.

Example

curl -X POST "$BASE_URL/client/recording.playBackByThirdPartyId" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "thirdPartyId": "abc-123-call-id", "format": "mp3" }' \
--output recording.mp3

Returns 404 if the thirdPartyId is unknown or not visible to the calling user.

Response details

The response is a binary stream, not JSON. Useful response headers:

HTTP/1.1 200 OK
Content-Type: audio/mpeg
Content-Disposition: attachment; filename="6414aa9f-ZZZAAABBB.mp3"
Content-Length: 4189312

Content-Type follows the requested format (audio/mpeg for mp3/mp3-mono, audio/wav for wav/wav-mono, video/mp4 for mp4, application/octet-stream for raw).


Get recording audit log

POST /client/recording/audit

Read the access audit log for a single recording — every play, download or detail view, with user and timestamp.

Required right: Recording.Audit.View

Body

{ "recordingId": 9876543, "page": 1, "pageSize": 100 }

Use this to evidence who has accessed a particular interaction (e.g. for a regulator request). The response is an array of audit rows; each row describes one HTTP call against this recording.

Full example response

Fields below match the AuditSearchResult shape. metadata is a freeform object recorded by the API at the time of the action and varies by route.

[
{
"auditHeaderId": 5512,
"method": "POST",
"route": "/client/recording.playBackByThirdPartyId",
"description": "Play recording",
"username": "jane.smith@example.com",
"insertedAt": "2026-05-12T11:02:14.000Z",
"ipAddress": "203.0.113.10",
"entityId": 9876543,
"parentEntityId": 9876543,
"entityName": "Recording",
"parentEntityName": "Recording",
"metadata": { "comment": "QA review for ticket #1234", "format": "mp3" },
"responseCode": 200
},
{
"auditHeaderId": 5513,
"method": "POST",
"route": "/client/recording.detail.get",
"description": "Get recording detail",
"username": "compliance-svc",
"insertedAt": "2026-05-12T11:05:01.000Z",
"ipAddress": "10.20.30.40",
"entityId": 9876543,
"parentEntityId": 9876543,
"entityName": "Recording",
"parentEntityName": "Recording",
"metadata": null,
"responseCode": 200
}
]

People & Endpoints

A person is an individual tracked in your Focus tenant; an endpoint is one of their addressable identities (a phone number, email or Teams user id). Endpoints are owned by people, and recordings are linked to endpoints.

POST /client/people.search

Search people with rich filters.

Required right: People.View

Body

FieldTypeNotes
freeTextstringFree‑text match across name / metadata.
personType"staff" | "external"
personIdsnumber[]Restrict to specific people.
thirdPartyIdsstring[]Restrict by source id.
emailsstring[]Restrict by email endpoint.
groupAssignmentarrayFilter by auto‑group rules.
retentionGroupIdnumber | nullFilter by retention group; null means "no group".
byoStorageAccountIdnumber | nullFilter by BYO storage account.
includeInteractionsbooleanIf true, include recent interaction stats per person.
onlyReturnNonAssociatedbooleanOnly people without an associated user.
isActiveboolean
page, pageSizenumber
Full example response
{
"persons": [
{
"id": 42,
"fullName": "Jane Smith",
"email": "jane.smith@example.com",
"thirdPartyId": "AAD-1f2c",
"personType": "staff",
"isActive": true,
"avatar": "https://cdn.example.com/avatars/42.png",
"retentionGroupId": 12,
"mostRecentCommAt": "2026-05-12T10:34:21.482Z",
"endpoints": [
{ "id": 81, "type": "phone", "endpoint": "447896423595", "ownedByPersonId": 42, "isRecorded": true },
{ "id": 82, "type": "email", "endpoint": "jane.smith@example.com", "ownedByPersonId": 42, "isRecorded": false }
],
"autoGroupAssignment": [
{ "groupName": "Department", "groupValue": "Support", "groupNameId": 3, "groupValueId": 17 }
],
"metadata": { "additionalFields": { "costCentre": "CC-204" } }
}
],
"total": 137,
"page": 1,
"pageSize": 25
}

Search people by name

POST /client/people.searchByName

Lightweight prefix search — useful for type‑ahead UIs.

Required right: People.View

Body

{ "prefix": "smi", "personType": "staff", "isActive": true, "page": 1, "pageSize": 25 }

personType is required; prefix is matched against full name.

Full example response
{
"persons": [
{ "id": 42, "fullName": "Jane Smith", "personType": "staff", "isActive": true },
{ "id": 58, "fullName": "Jamie Smithson", "personType": "staff", "isActive": true }
],
"total": 2,
"page": 1,
"pageSize": 25
}

Get people by id

POST /client/people.get

Fetch full person records by id.

Required right: People.View

Body — a JSON array of ids (numeric or string thirdPartyId):

[1, 2, 3]

Response (abridged)

[
{ "id": 1, "fullName": "Jane Smith", "personType": "staff" }
]
Full example response
[
{
"id": 1,
"fullName": "Jane Smith",
"email": "jane.smith@example.com",
"thirdPartyId": "AAD-1f2c",
"personType": "staff",
"isActive": true,
"avatar": "https://cdn.example.com/avatars/1.png",
"retentionGroupId": 12,
"mostRecentCommAt": "2026-05-12T10:34:21.482Z",
"endpoints": [
{ "id": 81, "type": "phone", "endpoint": "447896423595", "ownedByPersonId": 1, "isRecorded": true },
{ "id": 82, "type": "email", "endpoint": "jane.smith@example.com", "ownedByPersonId": 1, "isRecorded": false }
],
"autoGroupAssignment": [
{ "groupName": "Department", "groupValue": "Support", "groupNameId": 3, "groupValueId": 17 }
],
"metadata": { "additionalFields": { "costCentre": "CC-204" } }
}
]

POST /client/people/endpoint/search

Find endpoints (phone numbers, emails, Teams user ids) and the people that own them.

Required right: Endpoints.View

Body

FieldTypeNotes
type"phone" | "email" | "teamsUserId"
endpointSearchstringExact / partial match on the endpoint value.
freeTextstringFree‑text match.
isRecordedbooleanEndpoint has ever been recorded.
isCurrentlyRecordedbooleanRecording is currently active.
onlyReturnOwned / onlyReturnNonOwnedboolean
page, pageSizenumber

Example

curl -X POST "$BASE_URL/client/people/endpoint/search" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "type": "phone", "endpointSearch": "447896" }'
Full example response

Fields below match the RecordedEndpoint shape returned by the API. The handler returns a plain JSON array — there is no total / page wrapper. inflightCall is populated only while a live call is in progress on the endpoint.

[
{
"id": 81,
"type": "phone",
"endpoint": "447896423595",
"metadata": { "label": "Support desk" },
"ownedByFullName": "Jane Smith",
"isPrivate": false,
"isCurrentlyRecorded": true,
"isRecorded": true,
"inflightCall": null,
"mostRecentCommAt": "2026-05-12T10:34:21.482Z"
},
{
"id": 92,
"type": "phone",
"endpoint": "447896000999",
"metadata": {},
"ownedByFullName": "",
"isPrivate": false,
"isCurrentlyRecorded": false,
"isRecorded": false,
"inflightCall": null,
"mostRecentCommAt": null
}
]

Get endpoints by value

POST /client/people/endpoint/get

Fetch endpoint records by exact value.

Required right: People.View

Body — an array of { type, endpoint } objects:

[
{ "type": "phone", "endpoint": "447896423595" },
{ "type": "email", "endpoint": "jane.smith@example.com" }
]

Returns the matching endpoint records including the owning person id and recording flags. The response is a plain JSON array — entries are returned in the order they're found, not necessarily the order requested.

Full example response
[
{
"id": 81,
"type": "phone",
"endpoint": "447896423595",
"ownedByPersonId": 42,
"isPrivate": false,
"isRecorded": true
},
{
"id": 82,
"type": "email",
"endpoint": "jane.smith@example.com",
"ownedByPersonId": 42,
"isPrivate": false,
"isRecorded": false
}
]

Users

POST /client/user/search

Search Focus portal users.

Required right: Users.View

Body — all fields optional:

FieldTypeNotes
userIdintegerSingle‑user lookup.
usernamestringExact username match.
freeTextstringFree‑text match across user fields.
rolesAndRightsstring[]Filter to users with these rights.
isActive, isLockedboolean
onlyReturnNonAssociatedbooleanUsers with no person record.
searchOrderPropertyobjectSort options.
page, pageSizenumber

Example response

[
{ "id": 42, "username": "alice@example.com", "rolesAndRights": ["Admin"] }
]
Full example response
{
"users": [
{
"id": 42,
"fullName": "Alice Adams",
"username": "alice@example.com",
"email": "alice@example.com",
"externalId": "AAD-9f3a",
"personId": 1042,
"clientId": 1234567,
"isActive": true,
"isLocked": false,
"sso": true,
"lastLoginTime": "2026-05-12T08:14:00.000Z",
"rolesAndRights": ["Admin", "Recording.View", "Recording.Play"],
"policies": ["default-retention"],
"audioPlayback": "streaming"
}
],
"total": 1,
"page": 1,
"pageSize": 1000
}

Retention Groups

List retention groups

GET /admin/retentionGroup

List all retention groups configured on the account, including their default status and assigned people counts.

Required right: RetentionGroup.View

curl -H "Authorization: Bearer $TOKEN" "$BASE_URL/admin/retentionGroup"
Full example response
{
"accountDefault": {
"default": 2555,
"phone": 2555,
"teams": 2555,
"sms": 1095
},
"retentionGroups": [
{
"id": 12,
"name": "Standard 7 years",
"retentionGroup": { "default": 2555, "phone": 2555, "teams": 2555, "sms": 1095 },
"peopleIds": [42, 58, 91]
},
{
"id": 13,
"name": "Short 90 days (training)",
"retentionGroup": { "default": 90 },
"peopleIds": [120, 121]
}
]
}

Duration values are expressed in days. accountDefault applies to anyone not in a specific group.


Comments

Get comments on a recording

GET /client/recording/comment

List comments attached to a recording.

Required right: Recording.Comment.View

Query parameters

ParamRequiredNotes
recordingIdyesInternal recording id.
bIDnoOptional bookmark id filter.
cIdfnoOptional comment‑identifier filter.

Example

curl -H "Authorization: Bearer $TOKEN" \
"$BASE_URL/client/recording/comment?recordingId=9876543"

Comments may include start/end offsets within the recording, a tag and the inserting user.

Full example response

Fields below match the CommentOut shape. tag is omitted when the comment isn't tagged.

[
{
"comment": "Customer ID verified at 00:35.",
"startOffsetInSeconds": 35,
"endOffsetInSeconds": 42,
"recordingId": 9876543,
"insertedByUserId": 42,
"insertedByUsername": "jane.smith@example.com",
"insertedAt": "2026-05-12T10:36:00.000Z",
"tag": { "id": 3, "tag": "verified_id" }
},
{
"comment": "Reviewed for QA — no issues.",
"startOffsetInSeconds": 0,
"recordingId": 9876543,
"insertedByUserId": 7,
"insertedByUsername": "compliance-svc",
"insertedAt": "2026-05-12T11:05:00.000Z"
}
]

Docs AssistantAsk anything about our products