Skip to main content

Getting Started

This page walks you from a fresh API key to a working end‑to‑end integration that authenticates, finds a call by date range, fetches its transcription and downloads the audio. Every step uses plain curl so you can paste it straight into a terminal.

For a per‑endpoint reference (request shapes, filters, response schemas) see the Endpoint Reference, or the auto‑generated Focus API (OpenAPI) reference if you want the exact schema for a call and an interactive "Try it" console. This walkthrough links each step to its OpenAPI page so you can run the same call live.


Prerequisites​

Before you can call the API you need three things from your Focus account:

ItemWhere it comes fromNotes
API keyAdmin → Account → Integrations → Native Focus REST APIA Super User must enable the API and generate the key. Treat it like a password.
clientIdSame admin page (numeric account id)Identifies your tenant.
Integration usernameAuto‑generated and shown next to the API keyFocus provisions a dedicated service user when you enable the API; you don't choose its name. The token inherits this user's roles and rights.
Base URLDetermined by your tenant's region — see Base URLsThe API is regionally deployed.
Service user permissions

Only grant the rights your integration actually needs. The capability matrix lists the right required for each endpoint. A typical "metadata + audio download" integration needs Recording.View, Recording.Play and Recording.Transcription.View.


Base URLs and environments​

The API is deployed regionally. Your base URL follows the pattern:

https://<region>.focus.subphonic.ai/api

Where <region> is one of:

Region codeRegion
ukUnited Kingdom
euEuropean Union
usUnited States
caCanada

For example, a UK tenant uses https://uk.focus.subphonic.ai/api. Throughout these docs we refer to your base URL as {{baseUrl}}. Routes shown in the reference are exact paths to append, e.g. {{baseUrl}}/client/recording.search.


Authentication​

All authenticated calls take a short‑lived bearer JWT in the Authorization header. You exchange your API key for that JWT (and a refresh token) via POST /authWithApiKey.

Request​

  • Method: POST
  • URL: {{baseUrl}}/authWithApiKey
  • Headers: Content-Type: application/json
  • Body:
{
"clientId": 1234567,
"apiKey": "YOUR-API-KEY-XYZ",
"username": "YOUR-INTEGRATION-USERNAME"
}

All three values are shown together in the admin portal when you enable the API. The username is auto‑generated by Focus — use it exactly as displayed.

Example​

curl -X POST "$BASE_URL/authWithApiKey" \
-H "Content-Type: application/json" \
-d '{
"clientId": 1234567,
"apiKey": "YOUR-API-KEY-XYZ",
"username": "YOUR-INTEGRATION-USERNAME"
}'

Response​

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

Use token as the bearer for subsequent calls:

curl -H "Authorization: Bearer $TOKEN" "$BASE_URL/admin/retentionGroup"

Token lifetime & refresh​

Bearer tokens are short‑lived (minutes, not hours). When a call returns 401 Unauthorized your token has expired — request a new one by calling POST /authWithApiKey again with the same credentials. There is no need to cache or rotate the API key itself.


End‑to‑end walkthrough: search → transcript → download​

The diagram below shows the typical flow for an integration that finds calls and pulls their content. Every numbered step maps to a curl example below.

Step 1 — Authenticate​

See Authentication above. Capture the token value into an environment variable:

TOKEN=$(curl -s -X POST "$BASE_URL/authWithApiKey" \
-H "Content-Type: application/json" \
-d '{"clientId":1234567,"apiKey":"YOUR-API-KEY-XYZ","username":"YOUR-INTEGRATION-USERNAME"}' \
| jq -r .token)

Step 2 — Search for recordings​

Find every recording in a 24‑hour window, newest first, 100 per page:

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"
},
"startTimeDirection": "DESC",
"page": 1,
"pageSize": 100
}'

Each item in the recordings array carries both an interactionId (numeric, used for transcripts and detail) and a thirdPartyId (string, used for audio download). Page through results using the page and pageSize fields and the total count in the response.

See recording.search for the full filter list (free text, person ids, endpoints, communication type, direction, tags, duration, etc.), or Search recordings in the OpenAPI reference to inspect the full schema and run the call live.

Retrieving a whole conversation

A Microsoft Teams meeting recorded for four participants produces four recordings — one per participant — all sharing an Event ID (scenarioId in the API). Filter on that id to pull back every leg of the conversation instead of one participant's recording: see Find a communication by Event ID, or the worked example Find every recording for one Teams meeting.

Step 3 — Fetch the transcription​

curl -X POST "$BASE_URL/client/recording.transcription.get" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "interactionIds": [9876543] }'

The response contains the transcript segments with timestamps and speaker information. If the recording has no transcript (e.g. transcription is disabled, or the call hasn't been processed yet) the array is empty.

→ OpenAPI reference: Get transcriptions.

Step 4 — Download the audio​

Audio download is keyed on thirdPartyId (taken from the search result), and lets you pick the format:

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-mono" }' \
--output recording.mp3

This is the download path as well as the playback one — there is no separate download endpoint, and no download URL. The response is a binary stream; pipe it to a file as shown.

Supported formats are mp3-mono, wav-mono, wav, raw and mp4. The mono formats are a true downmix, so on a two‑channel recording both parties are mixed into the single channel rather than one being dropped. Always set format explicitly: the default (mp3) does not return an MP3, and an unrecognised value is not rejected. See Download audio → Formats for the full behaviour of each value.

→ OpenAPI reference: Download recording media.

Forced comments

If your account has the forced comments feature enabled, every audio download must be accompanied by a justification. Pass it as "comment": "QA review for ticket #1234" in the request body, otherwise the call returns 400 ValidationError.


Pagination​

All search endpoints accept page (1‑based) and pageSize and return a total count alongside the result array. Pick a pageSize that keeps response payloads under a few MB — 100–500 is a sensible default for recording search; 1000 is fine for lighter resources like users.

{
"page": 1,
"pageSize": 100,
"total": 4321,
"recordings": [ /* ... */ ]
}

For long‑running synchronisation jobs, prefer narrowing by dateTimeRange over deep paging — it is more efficient and resilient to data being added concurrently.


Errors​

The API uses standard HTTP status codes. JSON error bodies always include a human‑readable message and, where applicable, a well‑known error prefix you can pattern‑match against.

StatusMeaningTypical cause
400Validation errorBody failed schema validation; required field missing; forced‑comment not supplied.
401UnauthenticatedMissing, malformed or expired bearer token — re‑authenticate.
403ForbiddenThe service user lacks the required right for this endpoint.
404Not foundThe requested recordingId / thirdPartyId doesn't exist or isn't visible to your user.
429Too many requestsRate limit exceeded — back off and retry.
5xxServer errorTransient — retry with exponential backoff.

Always inspect the response body for diagnostic detail; do not rely on status code alone.


Rate limits​

The API applies per‑tenant rate limits to protect overall service health. Exact limits depend on your contract and region — please check with your Focus account manager for the values that apply to you. Build your client to:

  • Treat 429 Too Many Requests as a signal to back off (start with 1s, double on each retry, cap at ~30s).
  • Honour any Retry-After header if present.
  • Avoid bursts: throttle bulk synchronisation jobs to a steady request rate rather than firing in parallel.

Next steps​

Docs AssistantAsk anything about our products