Skip to main content

Cursor synchronization API

Once you have enabled the Cursor synchronization API in your account admin section, you can now use this information on this page to connect to the service.

Overviewโ€‹

Cursor APIs are designed to make data synchronization between systems efficient and reliable. Instead of retrieving all records every time you sync, a cursor API allows you to fetch only the new or updated data since your last request. This is achieved by using a โ€œcursorโ€โ€”a unique marker or token that represents a specific point in the data stream.

The Initial Cursorโ€‹

When you first connect to the Cursor API, you typically start with an initial cursor. This cursor will be provided by the API, by default the API assumes you want to start at the beginning, syncing from the very start of the dataset. The API will then return the first batch of data along with a new cursor value. If you wish to start from a specific point you must request this along side your initial cursor request.

Fetching Data with the Cursorโ€‹

Each time you make a request to the API, you include the current cursor value. The API responds with the next set of records and an updated cursor. This process continues, allowing you to โ€œpageโ€ through the data efficiently. If there are no new records, the API will return an empty result set but still provide a cursor for your next check.

Keeping Track of Cursorsโ€‹

It is important to store the latest cursor value after each successful sync. This ensures that, if your integration is interrupted or restarted, you can resume syncing from exactly where you left offโ€”without missing any data or processing the same records twice. Cursors are typically stored in your applicationโ€™s database or persistent storage.

Example Workflowโ€‹

  • Authenticate
  • Request the initial cursor (with or without a starting point).
  • Fetch data using the cursor. Note the request will provide data so this can be skipped.
  • Process the returned records in your system.
  • Store the new cursor value provided by the API.
  • Repeat the process on a schedule, always using the most recent cursor.

Integrationโ€‹

Authenticationโ€‹

Use the credentials provided to request a Bearer token from Focus. This request will provide you with both a token and a refresh token. The refresh token can then be used to request a refreshed token before the session ends.

Request example:โ€‹

  • Request type: POST
  • Content-Type: application/json
  • Request URL: BASE_URL/authWithAPIKey
  • Body:
{ 
"apiKey": "MY-API-KEY-XSDSADSADSADAS",
"username": "MyUserName",
"clientId": 9999 // My Client Id
}

Request response:โ€‹

  • Status: 200 OK
  • Content-Type: application/json
  • Body:
{
"token": "MY.TOKEN",
"refreshToken": "MY.REFRESH.TOKEN"
}

Get initial cursorโ€‹

As per the overview above, the initial cursor request is only expected once. This request will set the synchronization point and allow you to sync data going forwards guaranteeing no data is missed.

Request example:โ€‹

  • Request type: GET
  • Request URL: BASE_URL/client/recording/cursor
  • Params:
    • numberOfResults: 10 - If not supplied the default number of results is 100.
    • fromDate:2025-06-06T14:00:00Z - The UTC date and time to start your sync from.
  • Body: Empty
tip

If you do have an issue with your system or need to start again, you can use the fromDate value to only start back from the point required. If this is not supplied you will start back from the first communication stored in Focus.

Request response:โ€‹

  • Status: 200 OK
  • Content-Type: application/json
  • Body:
{
"recordings": [
{
"interactionId": 110,
"startDatetimeUTC": "2025-05-01T09:01:10.956Z",
"endDatetimeUTC": "2025-05-01T09:09:13.902Z",
"durationInSeconds": 482.946,
"type": "teams",
"commentsCount": 0,
"transcriptSnippet": "The call focused on...",
"thirdPartyId": "6414aa9f-ZZZAAABBB|1682400c-ZZZAAABBB|1746ZZZAAABBB",
"endpoints": [
{
"person": {
"id": 10,
"fullName": "Jane Smith"
},
"endpoint": {
"id": 11,
"type": "teamsUserId",
"endpoint": "6414aa9f-ZZZAAABBB"
},
"user": null,
"isOriginator": true,
"isRecordingSubject": true
},
{
"endpoint": {
"id": 12,
"type": "teamsUserId",
"endpoint": "89fb9d01-ZZZAAABBB"
},
"isRecordingSubject": false,
"isOriginator": false
},
{
"endpoint": {
"id": 10,
"type": "teamsUserId",
"endpoint": "e3a9fb55-fZZZAAABBB"
},
"isRecordingSubject": false,
"isOriginator": false
}
],
"analysis": [
"Project Management"
],
"retentionGroupId": null,
"retainUntil": "2035-04-29T09:09:13.902Z",
"legalHolds": null,
"tags": [
"conversationPhases.categoryNames.Discussion",
"conversationPhases.categoryNames.Opening",
"conversationPhases.categoryNames.Closing",
"callCategory.categoryNames.Project_Management"
],
"isPrivate": false,
"isPrivateReason": null,
"mediaAccessStatus": "Permitted",
"dataGroups": null,
"optional": {}
},
{"NEXT CALL"}
],
"nextCursorId": "MY.NEXT.CURSOR"
}

info

The response has been simplified slightly and any real IDs have been anonymized. However the data and structure are still accurate. A fully data dictionary can be found within the appendix section of this help documents.

Once processed make sure your application keeps the nextCursorId value for use in the following API calls and to avoid downloading duplicate data.

Get next cursorโ€‹

This request is very similar to the get initial cursor request but passes in the nextCursorId provided at the end of your first / initial request.

Request example:โ€‹

  • Request type: GET
  • Request URL: BASE_URL/client/recording/cursor
  • Params:
    • numberOfResults: 10 - If not supplied the default number of results is 100
    • cursorId: MY.NEXT.CURSOR - Your cursor id from above
  • Body: Empty
tip

If you do have an issue with your system or need to start again, you can use the fromDate value to only start back from the point required. If this is not supplied you will start back from the first communication stored in Focus.

Request response:โ€‹

  • Status: 200 OK
  • Content-Type: application/json
  • Body:
{
"recordings": [
{
"interactionId": 111,
"More data":"As above example"
},
{"NEXT CALL"}
],
"nextCursorId": "MY.NEXT.CURSOR"
}

Nextโ€‹

Awesome, well done, now just keep requesting the next cursor (remember to use the new nextCursorId each time) until you get an empty results set back. You will then be up to date with all communications from the focus platform.

Example empty response:โ€‹

{
"recordings": [],
"nextCursorId": "MY.NEXT.CURSOR"
}

warning

Even though this result was empty, you should still use the resulting nextCursorId in your next check.

Downloading mediaโ€‹

If you also want to synchronize the communication audio, this can be done via the download API once you have synchronize the metadata. This can be processed independently of the metadata / Cursor requests.

Request example:โ€‹

  • Request type: POST
  • Request URL: BASE_URL/client/recording.playBackByThirdPartyId
  • Body:
{
"thirdPartyId":"af0744a4-3b70-4093-95c5-b862554d707a"
}

The thirdPartyId will be provided in the body of the above cursor cursor requests.

Request response:โ€‹

  • Status: 200 OK
  • Content-Type: audio/mpeg
  • Body: AUDIO FILE