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.
Prerequisites
Before you can call the API you need three things from your Focus account:
| Item | Where it comes from | Notes |
|---|---|---|
| API key | Admin → Account → Integrations → Native Focus REST API | A Super User must enable the API and generate the key. Treat it like a password. |
clientId | Same admin page (numeric account id) | Identifies your tenant. |
Integration username | Auto‑generated and shown next to the API key | Focus 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 URL | Determined by your tenant's region — see Base URLs | The API is regionally deployed. |
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 code | Region |
|---|---|
uk | United Kingdom |
eu | European Union |
us | United States |
ca | Canada |
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.).
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.
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" }' \
--output recording.mp3
Supported formats: mp3, mp3-mono, wav, wav-mono, raw, mp4. The response is a binary stream; pipe it to a file as shown.
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.
| Status | Meaning | Typical cause |
|---|---|---|
400 | Validation error | Body failed schema validation; required field missing; forced‑comment not supplied. |
401 | Unauthenticated | Missing, malformed or expired bearer token — re‑authenticate. |
403 | Forbidden | The service user lacks the required right for this endpoint. |
404 | Not found | The requested recordingId / thirdPartyId doesn't exist or isn't visible to your user. |
429 | Too many requests | Rate limit exceeded — back off and retry. |
5xx | Server error | Transient — 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 Requestsas a signal to back off (start with 1s, double on each retry, cap at ~30s). - Honour any
Retry-Afterheader if present. - Avoid bursts: throttle bulk synchronisation jobs to a steady request rate rather than firing in parallel.
Next steps
- Browse the full Endpoint Reference for every documented call.
- Review the Data Dictionary to understand the fields returned in
recording.searchandrecording.detail.get. - For bulk metadata sync without per‑call polling, look at Cursor Synchronisation.