Skip to main content

How to make your first call to the Sally API

This quickstart takes you from nothing to the summary of a real meeting in four steps. Every example works as it stands once your own token is in place, and every step shows the response you get back, so you know what to pick from it for the next call.

You need a Sally account with access to at least one company account and one meeting that Sally has already summarized. No app registration and no approval step are required. Pick your language once, every example on the API pages follows.

Quick navigation

  1. Create a token
  2. Find your company account
  3. List your recordings
  4. Read the summary
  5. Before you build on it

1. Create a token​

In Sally, open Settings, select Integrations under Configuration and switch to the Personal Access Token tab. Create a token and copy it right away.

The token starts with sally_user_api_. It belongs to you and acts with your permissions: it reaches exactly the company accounts you are a member of and sees exactly what you would see in the app. Store it in an environment variable, all examples read it from there:

export SALLY_TOKEN="sally_user_api_..."

Every request sends it as a bearer token in the Authorization header. Authentication explains the details.

2. Find your company account​

Nearly every path needs a directoryId, the id of the Sally company account the data belongs to. This call lists the company accounts your token can address:

curl "https://api.sally.io/v1.0/me/directories/memberships" \
-H "Authorization: Bearer $SALLY_TOKEN"

The answer is a plain JSON array with one entry per company account:

200 OK
[
{
"directoryId": "5daeda15-8fc7-4693-a1e1-13119cceb461",
"directoryName": "Example GmbH",
"roleId": "164531fc-9be4-4de4-a183-89cc85d6a98c",
"licenseId": "f5f084e9-b993-425f-ad36-bfd5dc3f0e65",
"isBlocked": false
}
]
FieldTypeDescription
directoryIdstringId of the company account. This is what the paths need.
directoryNamestringDisplay name of the company account.
roleIdstringYour role in this company account, resolved via GET /v1.0/roles.
licenseIdstringYour license in this company account, resolved via GET /v1.0/licenses.
isBlockedbooleanWhether you are blocked in this company account.

If you get a 401 instead, the token is missing or invalid. Check that SALLY_TOKEN is set in the shell you run the example in.

Copy the directoryId you want to work with:

export DIRECTORY_ID="5daeda15-8fc7-4693-a1e1-13119cceb461"

Reference: GET/v1.0/me/directories/memberships

3. List your recordings​

There is one recording per recorded meeting, newest first. Ask only for the fields you need and the answer stays small:

curl "https://api.sally.io/v1.0/directories/$DIRECTORY_ID/recordings?pageSize=5&fields=recordingId,name,createdOn" \
-H "Authorization: Bearer $SALLY_TOKEN"

The list is paged, so the recordings sit in items inside the paging envelope. Because of fields, each entry only carries the three fields you named:

200 OK
{
"page": 1,
"pageSize": 5,
"total": 137,
"hasMore": true,
"items": [
{
"recordingId": "53e2e041-4b34-41c7-a285-634d80b3675d",
"name": "Weekly team meeting",
"createdOn": "2026-09-24T09:30:00Z"
},
{
"recordingId": "8f0c2b7a-3d51-4e0f-9a6c-2b1e7d4c9f30",
"name": "Quarterly planning",
"createdOn": "2026-09-23T13:00:00Z"
}
]
}

The query parameters of this endpoint:

ParameterTypeDefaultDescription
pageinteger1Page number, starting at 1.
pageSizeinteger25Entries per page, at most 100.
createdAfterstringOnly recordings created at or after this UTC timestamp (YYYY-MM-DDTHH:MM:SSZ).
createdBeforestringOnly recordings created at or before this UTC timestamp.
fieldsstringall fieldsComma-separated list of the fields to return, see Field selection.

Pagination & search shows how to fetch all pages. Copy the recordingId of a meeting that already has a summary:

export RECORDING_ID="53e2e041-4b34-41c7-a285-634d80b3675d"

Reference: GET/v1.0/directories/{directoryId}/recordings

4. Read the summary​

Add includeDetails=true to get the section items of your meeting template as well. Without it, sectionItems is null and you only get the summary text.

curl "https://api.sally.io/v1.0/directories/$DIRECTORY_ID/recordings/$RECORDING_ID/summaries?includeDetails=true" \
-H "Authorization: Bearer $SALLY_TOKEN"

The answer is a plain JSON array with one summary per language:

200 OK
[
{
"recordingSummaryId": "731180f9-257a-46cf-a7e3-8ca508fb9c35",
"recordingId": "53e2e041-4b34-41c7-a285-634d80b3675d",
"appointmentId": "d4398f97-cd92-4d21-a6ec-4aab0a85441b",
"languageCode": "de-DE",
"summary": "The team agreed on the rollout plan for October and assigned the launch checklist.",
"isSummarizationCompleted": true,
"isSummarizationSucceeded": true,
"summarizationCompletionReason": "succeeded",
"createdOn": "2026-09-24T09:30:00Z",
"sectionItems": [
{
"sectionItemId": "39ec96ee-9578-4082-a103-aa168fcd18f6",
"meetingTemplateSectionId": "0ba1ea56-516d-485a-a738-e65e51578bbe",
"title": "Decisions",
"sectionType": "decisions",
"outputFormat": "html",
"sortOrder": 1,
"content": "<p>We agreed on the rollout plan for October.</p>",
"subject": null,
"description": null,
"responsibleUserName": null,
"responsibleUserEmail": null,
"dueDate": null,
"tableVariant": null,
"tableData": null,
"followUpMeetingStartTime": null,
"followUpMeetingEndTime": null,
"followUpMeetingParticipants": null,
"emailRecipients": null
},
{
"sectionItemId": "c2a7e4b1-6f38-4d0a-8e95-1b7c3f2d6a84",
"meetingTemplateSectionId": null,
"title": "Tasks",
"sectionType": "tasks",
"outputFormat": "html",
"sortOrder": 2,
"content": null,
"subject": "Prepare the launch checklist",
"description": "Collect the open points from all teams before the rollout.",
"responsibleUserName": "Anna Berger",
"responsibleUserEmail": "anna.berger@example.com",
"dueDate": "2026-10-01T00:00:00Z",
"tableVariant": null,
"tableData": null,
"followUpMeetingStartTime": null,
"followUpMeetingEndTime": null,
"followUpMeetingParticipants": null,
"emailRecipients": null
}
]
}
]

The fields you need first:

FieldTypeDescription
recordingSummaryIdstringId of the summary.
languageCodestring | nullLanguage of the summary, for example de-DE or en-US.
summarystring | nullThe summary text.
isSummarizationCompletedbooleantrue once the summarization run has finished, whether or not it produced anything.
isSummarizationSucceededbooleantrue when there is something to read: summary text or at least one section item.
summarizationCompletionReasonstringWhy the run ended: succeeded, noTranscript, emptyResult, error or unknown while it is still running. noTranscript is an expected outcome, not a failure.
sectionItemsarray | nullThe structured results per section of your meeting template. null without includeDetails=true.

Each section item has a sectionType (summary, tasks, topics, decisions, customList, freeText, table, appointment, email or unknown) and an outputFormat (html, markdown or unknown) that tells you how to render content. Task-like items carry subject, description, responsibleUserName, responsibleUserEmail and dueDate instead. The types table, appointment and email keep their result in dedicated fields and also put a readable Markdown version into content, so reading content alone still shows every section.

Reference: GET/v1.0/directories/{directoryId}/recordings/{recordingId}/summaries

5. Before you build on it​

Important

A recording can have several summaries, one per language. Always identify a summary by the recording together with its languageCode, never by the recording alone. Meetings, recordings & summaries explains why, and why the same meeting can show up several times as an appointment.

Note

Version 1.0 is still under active development and can change. Ignore fields you do not know and tolerate new enum values, so your code keeps working when something is added. Versioning has the details.

The three endpoints of this quickstart at a glance:

From here, pick the guide for what you are building: