Open Caven

Reference

Requests and responses

Response format, pagination, incremental sync, errors and rate limits

3 min read

Response format

Every successful response is a JSON object with a data field. Lists also carry a meta object:

{
  "data": [ ... ],
  "meta": { "total": 410, "limit": 20, "offset": 0 }
}

A single object comes back as { "data": { ... } }. The only exceptions are four connector endpoints, which return a bare object.

  • Timestamps are ISO 8601 in UTC, for example 2026-10-05T15:24:30.542Z.
  • Fields without a value are null. They are never left out.
  • IDs are opaque strings. Don't parse them.

Pagination

Offset pagination

All list endpoints accept:

ParameterDefaultNotes
limit201 to 100. Higher values are capped at 100; 0 or a non-number means 20.
offset0Number of items to skip

meta.total is the total number of matching items. Recordings are returned newest first.

curl "https://developers.caven.io/public/v1/recordings?limit=50&offset=50" \
  -H "X-API-Key: sk_your_key_here"

Cursor pagination and incremental sync

Offset pagination isn't reliable for syncing: each new recording shifts every page by one, so you may see some recordings twice and miss others. For syncing, GET /recordings also supports cursor pagination. It switches on as soon as you pass updatedSince or cursor.

ParameterDescription
updatedSinceISO 8601 date. Only recordings changed at or after this moment.
cursorThe meta.nextCursor from the previous page
limit1 to 100, default 20

In cursor mode, results are ordered by last change (oldest first), and meta looks like this:

{ "meta": { "limit": 20, "hasMore": true, "nextCursor": "MjAyNi0wMS0yMVQxMzoxODoxMy4zOTJafGNta28x..." } }

To sync:

  1. Request /recordings?updatedSince=<last sync time>.
  2. While hasMore is true, request /recordings?cursor=<nextCursor>.
  3. Save the time you started as the next updatedSince.

A recording also counts as changed when its transcript or summary is finished, so a recording that is still being processed comes back once it's done.

The cursor is opaque. Pass it back exactly as you received it. A cursor that has been modified returns 400 INVALID_CURSOR.

Errors

Errors use standard HTTP status codes and a consistent body:

{
  "message": "Invalid status: completed. Expected one of: RECORDED, PROCESSING, TRANSCRIBED, SUMMARIZED, FAILED",
  "code": "INVALID_PARAM",
  "details": { "field": "status", "allowed": ["RECORDED", "PROCESSING", "TRANSCRIBED", "SUMMARIZED", "FAILED"] }
}

Branch on code, not on message. Messages can change, codes don't.

StatuscodeWhen
400INVALID_PARAMA query parameter has an invalid value, such as a bad date or an unknown status
400INVALID_CURSORThe cursor or updatedSince value can't be read
401API_KEY_REQUIRED, API_KEY_INVALID, API_KEY_EXPIREDSee Authentication
403INSUFFICIENT_SCOPEThe key lacks the scope for this endpoint
403ONPREM_NOT_ENABLED, ONPREM_METHOD_NOT_DOCKERMedia and connector endpoints, when the organization doesn't use Docker on-prem delivery
404NOT_FOUNDThe object doesn't exist, or the key can't see it
404TRANSCRIPT_NOT_AVAILABLE, SUMMARY_NOT_AVAILABLE, NOTES_NOT_AVAILABLEThe recording exists, but this content doesn't (yet)
404NO_ORGANIZATIONAn organization endpoint, called with a key that has no organization
409HOSTED_SOURCE_UNAVAILABLEThe content is stored only on your organization's own server and isn't available through Caven
429RATE_LIMITEDToo many requests. See below.
500INTERNAL_ERRORSomething went wrong on our side
503HOSTED_SOURCE_UNREACHABLEThe content is stored outside Caven (for example in SharePoint) and that source couldn't be reached. Try again later.

A recording that belongs to someone outside the key's scope returns 404, not 403. The API doesn't reveal whether it exists.

A 403 INSUFFICIENT_SCOPE for one of the media or connector scopes, which must be granted explicitly, carries an extra detail field (singular) with a plain explanation:

{
  "message": "Scope 'media:download' required",
  "code": "INSUFFICIENT_SCOPE",
  "detail": "This scope must be granted explicitly on the API key. Keys with no scopes listed do not receive it. ..."
}

The media and connector endpoints have more codes of their own. See On-prem connector.

Rate limits

Limits are counted per API key, so other customers' traffic never affects yours.

EndpointsLimit
All metadata endpoints (recordings, transcripts, organization, and so on)300 requests per minute
Media inventory and downloads1,200 requests per minute
Media keys600 requests per minute

Every response includes the standard rate-limit headers:

RateLimit-Policy: 300;w=60
RateLimit-Limit: 300
RateLimit-Remaining: 299
RateLimit-Reset: 60

When you exceed a limit you get 429 with code: "RATE_LIMITED". Wait RateLimit-Reset seconds before trying again.

On developers.caven.io there is also a coarse limit of 600 requests per minute per IP address for unauthenticated traffic, so a flood of requests without a valid key is stopped early.

CORS

On developers.caven.io the API sends Access-Control-Allow-Origin: *, so browsers can reach it. api.caven.io does not: it only accepts cross-origin requests from Caven's own web apps, and doesn't allow the X-API-Key header from a browser. Even so, never put an API key in code that runs in a browser: anyone who opens the page can read it. Call the API from your own server instead.

© 2026 Caven BV. All rights reserved. Built in Belgium.EU · GDPR · AI Act · Privacy-first · ISO 27001

On this page