Skip to content
Browse the docs

Kalima REST API v1

Read your recordings, transcripts, AI summaries, tasks and moments, upload audio, and organize recordings.

The Kalima REST API lets your own integration read the recordings you own, with their transcripts, AI summaries, tasks and moments, upload audio files for transcription, and organize recordings: projects, names, speakers, share links and summaries.

Authentication

There are two ways in:

  • API key (Pro, Team and Enterprise). For your own scripts and integrations. Send the key as a bearer token with every request.
  • OAuth (Plus and up). Used by the automation platforms Kalima registers, which ask you to approve the connection in a Kalima window. You can remove such a connection at any time under Settings, Integrations, Connected apps.
Authorization: Bearer kal_xxxxxxxxxxxxxxxxxxxx
Note

Create a key under Settings, Integrations, API keys. Kalima shows it only once, so store it securely. Step by step, including n8n and Make: Automate with Zapier, n8n or Make.

An API key carries the read scope, the write scope or both. read covers every list, search, feed and export; add write only when the integration uploads audio or changes recordings and projects. Every request acts as the account the key belongs to and sees only the recordings that account owns.

Endpoints

Base URL: https://meetkalima.com/api/v1

Method and pathPurpose
GET /meThe account the credential belongs to
GET /recordingsList recordings, one page at a time
POST /recordingsUpload audio as a new recording
GET /recordings/searchFind recordings by words in their name or transcript
GET /recordings/{id}One recording with its transcript text and AI summary
PATCH /recordings/{id}Rename a recording or move it
GET /recordings/{id}/transcriptThe transcript as segments with speaker and start time
GET /recordings/{id}/speakersThe speakers of a recording
PUT /recordings/{id}/speakersName speakers
POST /recordings/{id}/summaryCreate the AI summary
GET /recordings/{id}/exportThe recording as a file
POST /recordings/{id}/share-linkTurn on the share link
GET /summariesFeed of new AI summaries
GET /action-itemsFeed of tasks from AI summaries
GET /momentsFeed of moments marked while recording
GET /projectsList projects
POST /projectsCreate a project
GET /workspacesList workspaces
/admin/...Manage your company's members, teams and policies (company keys with admin scopes, see below)
GET /openapi.jsonThe machine-readable OpenAPI contract

Every recording carries url, the link that opens it in Kalima, and the project and workspace it is filed in.

List recordings

By default GET /recordings returns the finished feed. A recording enters it once, as soon as nothing is being recorded into it any more and its transcript has not changed for two minutes. It keeps its place after that, even when you later correct the transcript, and it does not enter the feed a second time if more is recorded into it. The newest entries come first, and each carries finishedAt, the moment it entered the feed. That makes the feed safe to poll: act on each id once, and you never pick up a transcript that is still growing. An uploaded file that turns out to contain no speech enters it too, with segmentCount 0, so the integration that uploaded it hears back. The first time you read it, it reaches back seven days.

Add status=all to list every recording in any state, newest first, or status=failed for only the uploads whose transcription failed. Each recording has a status of recording, processing, failed or ready, and updatedAt says when its transcript or speakers last changed. projectId or workspaceId narrow any of these lists.

Lists are paginated. Pass the nextCursor of a response back as cursor until it is null. limit sets the page size (1 to 100, default 25).

To fetch only what is new, pass the newest sequence you have seen as afterSequence. Each feed item carries one, and unlike finishedAt (which finishedAfter takes) it stays exact when many recordings enter the feed in the same millisecond. Add include=summary or include=transcript,summary to get the content with each item; with the transcript a page holds at most 10 recordings.

GET /recordings/search?q=budget finds recordings whose name or transcript contains the words, best match first, each with a snippet of where the words were found. limit goes up to 25 (default 10), and projectId or workspaceId narrow the search.

Read a recording

GET /recordings/{id} returns the recording with transcriptText (plain lines of the form Speaker: text) and summary. The summary holds an overview, key points, action items with owner and due date, decisions, open questions and the full summary as Markdown. It is null until a summary has been created for the recording.

Use include=transcript, include=summary or an empty include= to fetch less. A transcript longer than 500,000 characters is cut there, and transcriptTruncated is then true. GET /recordings/{id}/transcript returns the complete transcript as segments.

GET /recordings/{id}/export?format=srt returns the recording as a file, the same file the Export dialog in Kalima downloads, with its name in the Content-Disposition header. format is txt, md, srt, vtt, json or summary-md. For a recording with a translation, track=translation exports the translation in md, srt and vtt.

Feeds

Three more feeds work like the finished feed: newest first, every item once, with cursor to page back and createdAfter to ask only for what entered after a given moment (on the summary and task feeds also afterSequence, the exact way). limit goes up to 50 (default 25), and projectId or workspaceId narrow each feed.

  • GET /summaries has one item each time a recording gets its AI summary, and another when the summary is regenerated or edited. Each item carries the summary in full and a version: 1 for a recording's first summary, then 2, 3 and so on. Renaming or moving a recording does not bring its summary back. The feed holds the summaries saved in the last seven days.
  • GET /action-items has one item per task in those summaries, with owner and due when the summary names them. A regenerated summary that keeps a task keeps its id, so the task is not delivered twice. Here limit counts summaries, not tasks.
  • GET /moments has the moments marked during recordings, with their AI title and their position in the recording. A moment waits up to five minutes for its title before it is listed; listedAt says when it was, and createdAfter compares against it.

Organize recordings

These need the write scope.

  • Rename or move: PATCH /recordings/{id} with name, and either projectId or workspaceId (not both). A project move also puts the recording into that project's workspace, like in the app, and needs edit rights in the project; without them the answer is PROJECT_ROLE_REQUIRED. projectId: null takes it out of its project. A refused move changes nothing, not even the name.
  • Name speakers: PUT /recordings/{id}/speakers with { "names": { "Speaker 1": "Anna Weber" } }. Each key is a label as the transcript shows it, a name already given, or a key from GET /recordings/{id}/speakers. An empty name removes the name. A key that matches no speaker is refused with SPEAKER_NOT_FOUND.
  • Create the summary: POST /recordings/{id}/summary answers with the summary when one exists. Otherwise it answers 202 with status: "queued", and the summary arrives within a minute or two in GET /summaries and on the recording. It uses one AI summary from your plan, like the Generate Summary button. When the plan's summaries are used up, it answers QUOTA_EXCEEDED instead of queuing, and while a file is still being transcribed into the recording, RECORDING_NOT_READY.
  • Share: POST /recordings/{id}/share-link turns on the recording's share link and returns it. A recording has one link, so calling this again returns the same one. A new link opens for anyone signed in to Kalima who has it; your organization's sharing rules apply, and you set wider or narrower access in the app.
  • Projects: GET /projects lists the projects you own or that are shared with you, newest first; name finds names that contain it, case and accents ignored, and with exact=true only names equal to it. editable=true keeps the projects you may file recordings into; every project carries canEdit. POST /projects creates one from name and, optionally, description, workspaceId and parentProjectId (one level deep). Your plan's project limit applies (PLAN_LIMIT_REACHED). GET /workspaces lists your workspaces.

Upload audio

Send multipart/form-data with a file field. Optionally add:

FieldMeaning
nameThe recording's name; the file name otherwise
durationMsA duration hint
projectIdFile it into this project (edit rights needed). The project's transcription context applies unless you send context or terms
workspaceIdOr file it into this workspace, without a project
languagesUp to three spoken languages as ISO codes, e.g. de,en
translateToOne language code to translate into alongside
contextWhat the conversation is about, up to 10,000 characters
termsNames and words to recognize, separated by commas or line breaks
curl -X POST "https://meetkalima.com/api/v1/recordings" \
  -H "Authorization: Bearer kal_xxx" \
  -F "file=@meeting.mp3" \
  -F "name=Weekly sync" \
  -F "languages=de,en"

The response (202) contains the new recording's id with the status processing. Poll GET /recordings/{id}?include= until the status is ready or failed. The transcribed duration is charged to the account's transcription balance.

Manage your company (admin API)

For companies that do not use SCIM: a company API key with admin scopes manages your company's members, teams and policies. An owner or admin creates it in the Kalima Console under Integrations & API, Create key, in the Company administration section, where each area can get no access, read access, or read and change access. See Integrations and webhooks.

ScopeEndpoints
admin:members:readGET /admin/members, GET /admin/members/{userId}
admin:members:writePATCH /admin/members/{userId}, POST /admin/invitations
admin:teams:readGET /admin/teams, GET /admin/teams/{teamId}
admin:teams:writePOST /admin/teams, PATCH /admin/teams/{teamId}, POST /admin/teams/{teamId}/members, PATCH and DELETE /admin/teams/{teamId}/members/{userId}
admin:policies:readGET /admin/policies
admin:policies:writePATCH /admin/policies

The key acts as the person who created it, with their role, and these paths refuse it (ADMIN_ROLE_REQUIRED) as soon as that person is no longer an active owner or admin. The same rules apply as in the console: the seat limit, only an owner changes an owner, at least one active owner, and while your identity provider manages people and teams through SCIM, it suspends and reactivates those people and decides who is in those teams and what they are called (SCIM_MANAGED); roles and team leads stay with Kalima. A refusal carries the console's reason as code, for example SEAT_CAP_REACHED, OWNER_PROTECTED or LAST_OWNER. Every change appears in your company's audit log under "API key" and the key's name. These paths never return recordings, transcripts, or anyone's activity or usage. OAuth tokens (API_KEY_REQUIRED) and keys of private accounts (COMPANY_KEY_REQUIRED) are refused.

  • Members: GET /admin/members lists active and suspended members, owners first; status and email narrow the list, and it pages with cursor like the other lists (limit 1 to 500, default 100). PATCH /admin/members/{userId} makes one change per request: { "role": "member" } (admin, member or viewer) or { "status": "suspended" } (or active). Ownership changes hands only by a transfer the new owner accepts, started in the console's settings or in the app. Suspending a person keeps every recording where it is.
  • Invite: POST /admin/invitations with email, role and optionally teamId. The person gets the usual invitation mail, which names the key's creator as the inviting person.
  • Teams: POST /admin/teams with name and optionally description; PATCH /admin/teams/{teamId} renames it. POST /admin/teams/{teamId}/members with { "userId": "...", "role": "lead" } puts an active member in, PATCH /admin/teams/{teamId}/members/{userId} with { "role": "member" } changes the team lead, and DELETE takes the person out of the team. Deleting a team stays in the console.
  • Policies: GET /admin/policies returns the sections of the console's Policies page (data, ai, sharing, members, recording, projectSharing). PATCH takes the same sections and merges each over what is stored, so a setting you leave out stays as it is. The deletion rules are changed in the console only, because a second factor is needed there: deleting recordings and audio automatically is not part of the body, and a PATCH that would switch zero data retention on or off is refused with CONSOLE_ONLY.

The request quota is the same as for every other call. If your company allows Kalima only from certain networks, whether for the console alone or for everything, these paths answer NETWORK_NOT_ALLOWED from anywhere else. See Allowed networks.

On a company account, the company's rules also apply to ordinary API keys: if Zapier and the public API or API keys is turned off in the console's Policies, requests with keys of that company's accounts are refused with 403 and ORG_POLICY_BLOCKED. See Policies.

Errors and limits

Every error has the same shape:

{ "error": { "code": "RECORDING_NOT_FOUND", "message": "Recording not found" } }
StatusMeaning
400Invalid parameter or unsupported audio file
401Missing, invalid or expired credentials, or a removed connection
402No transcription time left for an upload
403Missing scope, a plan without access, a plan limit reached (QUOTA_EXCEEDED, PLAN_LIMIT_REACHED), no edit rights in a project (PROJECT_ROLE_REQUIRED), or a request from a network your company does not allow (NETWORK_NOT_ALLOWED)
404No such recording, project or workspace for this account
409Not possible right now, for example a summary for a recording that is still being transcribed
413The file is too large
429API key request quota or OAuth rate limit reached; retry later
503Briefly unavailable; retry after a short wait

Revoke a key immediately if it may have been exposed.