REST · V1

Your meetings, over HTTP.

Notes, summaries, action items, transcript passages and durable memory — as JSON you can build on. Read-only, cursor-paginated, described by an OpenAPI schema.

$ curl public-api.scriben.ai/v1/action-items

BEARER TOKEN · NO SDK REQUIRED

GET /v1/action-items
{
  "data": [
    {
      "text": "Send the deck to Sarah by Friday",
      "note_ref": "n_a41f9c2b0e17",
      "date": "2026-07-28T15:04:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null,
  "as_of": "2026-08-02T09:00:00.000Z"
}

[ Quickstart ]

Three steps, about two minutes.

Everything is read-only, so there is nothing you can break while exploring.

01

Create a key

At app.scriben.ai, under Settings → Developer. Copy it immediately — we store only a hash, so we genuinely cannot show it to you twice.

02

Confirm it works

/v1/me exists for exactly this. It answers whether the key is live, and nothing else.

03

Follow a ref

List notes, then use a returned ref to read one. Refs are the only way to address a note.

first call
export SCRIBEN_API_KEY="scb_live_…"

$ curl https://public-api.scriben.ai/v1/notes?limit=3 \
    -H "Authorization: Bearer $SCRIBEN_API_KEY"

$ curl https://public-api.scriben.ai/v1/notes/n_a41f9c2b0e17 \
    -H "Authorization: Bearer $SCRIBEN_API_KEY"
const res = await fetch(
  "https://public-api.scriben.ai/v1/notes?limit=3",
  { headers: { Authorization: `Bearer ${process.env.SCRIBEN_API_KEY}` } },
);
if (!res.ok) throw new Error((await res.json()).error.code);

const { data, next_cursor } = await res.json();
// next_cursor is opaque — hand it back verbatim, never parse it.
import os, httpx

r = httpx.get(
    "https://public-api.scriben.ai/v1/notes",
    params={"limit": 3},
    headers={"Authorization": f"Bearer {os.environ['SCRIBEN_API_KEY']}"},
)
r.raise_for_status()
notes = r.json()["data"]

[ Authentication ]

One header.

Keys are never accepted in a query parameter. URLs reach access logs, browser history, referrer headers and error trackers — a credential that leaks by being logged leaks everywhere at once.

AuthorizationBearer scb_live_… on every request.
scb_<env>_<43>The vendor prefix is what GitHub and commercial secret scanners match on, so a committed key can be found and revoked automatically.
401Missing, malformed, revoked and expired keys are reported identically — the error never confirms whether a key was real.
live · testThe environment is part of the key, so a test credential pasted into production fails loudly rather than reading real meetings.

[ Conventions ]

The same rules on every endpoint.

Learn them once.

Pagination is cursor-based

Pass limit (1–50) and follow next_cursor until has_more is false. Cursors are opaque — do not construct or parse one. Offsets are computed against a list that changes: record a note between page one and page two and every row shifts, so the caller silently re-reads one item and never sees another. A cursor names where it stopped.

every page, in order
let cursor = null, all = [];
do {
  const url = new URL("https://public-api.scriben.ai/v1/notes");
  url.searchParams.set("limit", 50);
  if (cursor) url.searchParams.set("cursor", cursor);

  const page = await (await fetch(url, { headers })).json();
  all.push(...page.data);
  cursor = page.next_cursor;
} while (cursor);

Errors carry a stable code

Branch on code, never on message — the code is part of the contract, the prose is not. Every error carries a request_id; quote it and we can find the exact request.

404
{
  "error": {
    "code": "not_found",
    "message": "No note with that ref.",
    "request_id": "req_8f2ad41c9b07"
  }
}
CodeStatusMeaning
unauthorized401Key missing, malformed, revoked or expired.
invalid_request400A parameter is missing or malformed, including an undecodable cursor.
not_found404No such resource for this account.
method_not_allowed405The API is read-only.
internal500Our fault. Quote the request id.

Refs, not database ids

Notes are addressed by an opaque per-account ref such as n_a41f9c2b0e17. The same note has a different ref in a different account, so a ref cannot correlate anything across accounts. A ref issued to someone else returns 404 — the same answer an invented one gets.

Every answer shows its work

as_of is on every response, and reads derived from meetings carry source_notes — the notes behind the answer, so a claim about a person is one you can check rather than one you have to trust.

[ Reference ]

Seven endpoints. All of them read.

The full schema is published as OpenAPI 3.1, which is what the SDKs are generated from.

GET /v1/meConfirm a key works.
GET /v1/notesNewest first. Titles, dates and participants — never a transcript.
GET /v1/notes/{ref}Summary and action items for one note.
GET /v1/notes/{ref}/transcriptMatching passages with speaker and timestamp. A query is required.
GET /v1/action-itemsAcross notes, each with the note it came from. Filter by person.
GET /v1/memoriesDurable facts and decisions, honouring private and team visibility.
GET /v1/peopleWhat the account knows about someone.

An ambiguous name is a result, not an error

If two people share a first name, /v1/people returns status: "ambiguous" with candidates and a 200. Blending two people into one confident answer is the failure we refuse to ship — call again with the ref you meant.

[ Clients ]

Generate the client, don't write it.

The schema at docs.scriben.ai/openapi.json is generated from the server's own route table, not maintained beside it — a test fails the build if the two disagree. Point a generator at it and the types are the API's types.

generate
$ npx @hey-api/openapi-ts \
    -i https://docs.scriben.ai/openapi.json \
    -o src/scriben
$ npx @openapitools/openapi-generator-cli generate \
    -i https://docs.scriben.ai/openapi.json \
    -g python -o ./scriben
$ go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest \
    -package scriben \
    https://docs.scriben.ai/openapi.json > scriben.go

We do not publish hand-written SDKs, and that is a choice rather than a gap. A generated client is correct the day the schema changes; a hand-written one is correct until somebody forgets. If you would rather not generate anything, seven endpoints and one header is an afternoon's worth of fetch.

[ Versioning ]

How this changes, and how it won't.

The question worth answering before you build on something: what can break you, and what cannot.

/v1 is frozenNo field in /v1 will change meaning or disappear. A change that would require editing working code gets a new path, not a new release note.
additive, unannouncedNew endpoints, new response fields and new error codes can land at any time. Parse tolerantly: ignore fields you do not know rather than rejecting the response.
codes over proseError message text is written for a human reading a log and may be reworded. Only code is a contract.
the schema is the truthWhere this page and openapi.json disagree, the schema is right — it is generated from the running server.

[ Boundaries ]

What it deliberately will not do.

An interface to your meeting history is only worth having if its limits are exact.

Read-only

Nothing in v1 mutates anything. There is no write path in the surface at all.

Passages, not transcripts

Search returns matching excerpts with speaker and timestamp. Bulk export of everything anyone said is not a feature we want to have built.

One account per key

A key reads exactly one account and cannot be widened. There is no org or team scope to escalate into.

Flagged content withheld

Text that reads as an instruction aimed at an assistant returns a reason instead of the prose, so a poisoned note cannot steer your agent.

Polling, for now

There are no outbound webhooks yet, so a workflow that reacts to a finished meeting has to ask. Signed delivery is designed and next.

No audio

Recordings are not reachable through the API. It is the most-requested endpoint and it is blocked on storage work, not on the interface.

[ Which surface ]

API or MCP.

They are not competing, and they are not for the same job.

Use the API

When you are building something — an agent, a scheduled digest, an internal dashboard. You own the loop: model, prompts, scheduling and storage are yours.

Use MCP

When you want Scriben inside Claude, ChatGPT, Cursor or Zed. One command, no code, and the assistant decides which tool it needs.