VoiceBrain API

The VoiceBrain API is a read-only REST API to your own entries, authenticated with a personal key you generate inside the iOS app.

↓ Download this page as Markdown

What the VoiceBrain API is

The VoiceBrain API lets you read your own captured entries from your own code — scripts, Shortcuts, or your own AI agents. You speak into the app, VoiceBrain transcribes and sorts what you said into notes, tasks, reminders, and events, and the API gives you programmatic access to those entries.

It is a plain REST API. Every request is a standard HTTPS call, responses are JSON, and every read is scoped to your own account — a key can only ever reach your data. Today the API is read-only: you can list entries, fetch a single entry, wait for one to finish processing, and download its audio.

Creating and editing entries still happens in the app. A write API is planned, but for now think of the API as a fast, reliable way to get your captured thoughts out of VoiceBrain and into whatever you are building.

Getting your API key

API keys are generated inside the iOS app, under Settings. There is no public endpoint to create, list, or revoke keys — key management lives in the app only.

Every key starts with the prefix hexp_. The raw key is shown only once, at the moment you create it, so copy it and store it somewhere safe (a password manager or a secrets store). If you lose it, you generate a new one in the app rather than recovering the old value.

Treat a key like a password. It is scoped to your own account and is read-only, but anyone holding it can read your entries, so only put it into tools you trust.

Base URL and authentication

The base URL for every request is https://api.vbrain.app. Authenticate by sending your key in the Authorization header as a Bearer token on every request. An invalid or revoked key returns a 401 response.

That header is the only thing you need to authenticate — there is no separate login step, no token exchange, and no cookies.

Authenticated request
curl https://api.vbrain.app/entries \
  -H "Authorization: Bearer hexp_xxxxxxxxxxxxxxxxxxxxxxxx"

List entries

GET /entries returns your entries newest-first. It supports keyset pagination through a cursor: pass the nextCursor value from the previous response to fetch the next page. When nextCursor is null there are no more entries.

Query parameters: limit (default 50, max 200), cursor (an ISO-8601 datetime used as the keyset position), type (filter by entry type), and status (filter by processing status). The response is an object with an items array of entry objects and a nextCursor field.

The example below fetches 20 entries of type tasks. Each item in the response is a full entry object, described later on this page.

Request
curl "https://api.vbrain.app/entries?limit=20&type=tasks" \
  -H "Authorization: Bearer hexp_xxxxxxxxxxxxxxxxxxxxxxxx"
Response
{
  "items": [
    {
      "id": "01HZY6K8N3QWERTY",
      "status": "ready",
      "type": "tasks",
      "rawTranscript": "Remind the team to send the invoice tomorrow",
      "llmConfidence": 0.94,
      "details": { "text": "Send the invoice", "dueAt": "2026-07-02T09:00:00Z" },
      "reminderAt": null,
      "recordingId": "rec_8f2a",
      "spaceId": "spc_home",
      "authorId": "usr_42",
      "authorName": "Alex",
      "eventStartsAt": null,
      "eventEndsAt": null,
      "eventAllDay": null,
      "eventLocation": null,
      "eventRrule": null,
      "eventTimezone": null,
      "completedAt": null,
      "pinnedAt": null,
      "externalRefs": [],
      "audioAvailable": true,
      "audioDurationMs": 4200,
      "createdAt": "2026-07-01T14:03:11Z",
      "updatedAt": "2026-07-01T14:03:19Z"
    }
  ],
  "nextCursor": "2026-07-01T14:03:11Z"
}

Get one entry

GET /entries/:id fetches a single entry by its id and returns one entry object directly (not wrapped in an items array). If the entry does not exist, or is not yours, you get a 404 not_found response.

Request
curl https://api.vbrain.app/entries/01HZY6K8N3QWERTY \
  -H "Authorization: Bearer hexp_xxxxxxxxxxxxxxxxxxxxxxxx"

Wait for processing

When you capture an entry, VoiceBrain transcribes and sorts it in the background, so a freshly created entry may still have status processing. GET /entries/:id/wait?after=processing long-polls until the entry finishes, so you do not have to poll in a loop.

It returns one of four shapes depending on the stage: still processing, transcribed (with the transcript text), ready (with the full entry object), or error (with an errorMessage).

Request
curl "https://api.vbrain.app/entries/01HZY6K8N3QWERTY/wait?after=processing" \
  -H "Authorization: Bearer hexp_xxxxxxxxxxxxxxxxxxxxxxxx"
Possible responses
{ "stage": "processing" }

{ "stage": "transcribed", "transcript": "Send the invoice tomorrow" }

{ "stage": "ready", "entry": { "id": "01HZY6K8N3QWERTY", "status": "ready", "type": "tasks", ... } }

{ "stage": "error", "errorMessage": "Transcription failed" }

Download audio

GET /entries/:id/audio returns the entry's original audio recording as an m4a file, as long as it has not expired. VoiceBrain deletes recordings automatically after 7 days, so this endpoint only works while the audio still exists — after that the audio is gone and the request returns 404 not_found.

You can check the audioAvailable field on an entry before requesting the file.

Download to a file
curl https://api.vbrain.app/entries/01HZY6K8N3QWERTY/audio \
  -H "Authorization: Bearer hexp_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -o entry.m4a

The entry object

Every entry endpoint returns entries in the same shape. The core fields are: id, status ('processing', 'ready', or 'failed'), and type ('notes', 'tasks', 'reminder', 'event', or null). rawTranscript holds the transcribed text, and llmConfidence is how confident the model was when sorting it.

details is a per-type object: notes is { text }, tasks is { text, dueAt }, reminder is { text, reminderAt, timezoneRef }, and event is { text, startsAt, endsAt, allDay, location, recurrence, timezoneRef }.

Other fields include reminderAt, recordingId, spaceId, authorId, authorName, the flattened event fields (eventStartsAt, eventEndsAt, eventAllDay, eventLocation, eventRrule, eventTimezone), completedAt, pinnedAt, externalRefs, audioAvailable, audioDurationMs, createdAt, and updatedAt. All timestamps are ISO-8601 strings or null.

A reminder entry
{
  "id": "01HZY7ABCD1234",
  "status": "ready",
  "type": "reminder",
  "rawTranscript": "Remind me to call the dentist at 5pm",
  "llmConfidence": 0.88,
  "details": {
    "text": "Call the dentist",
    "reminderAt": "2026-07-01T17:00:00Z",
    "timezoneRef": "Europe/Berlin"
  },
  "reminderAt": "2026-07-01T17:00:00Z",
  "recordingId": "rec_1c9d",
  "spaceId": "spc_home",
  "authorId": "usr_42",
  "authorName": "Alex",
  "eventStartsAt": null,
  "eventEndsAt": null,
  "eventAllDay": null,
  "eventLocation": null,
  "eventRrule": null,
  "eventTimezone": null,
  "completedAt": null,
  "pinnedAt": null,
  "externalRefs": [],
  "audioAvailable": true,
  "audioDurationMs": 3100,
  "createdAt": "2026-07-01T15:58:02Z",
  "updatedAt": "2026-07-01T15:58:10Z"
}

Errors

Errors come back as JSON with an error message, a machine-readable code, and a requestId you can quote if you need help debugging. Match on code rather than the human-readable message.

The codes are: unauthorized (401, invalid or missing key), validation_failed (400, a bad parameter), forbidden (403), not_found (404), and internal (500).

Error response
{
  "error": "Invalid API key",
  "code": "unauthorized",
  "requestId": "3f1c9a2e-7b40-4d6e-9c11-8a2f0d5e7b21"
}

Read-only today, write API coming

Today the personal API key is read-only. You can read, wait on, and download your entries, but you cannot create, update, or delete them through the API. For now, capturing and editing entries happens in the app.

A write API for creating and updating entries is planned. When it ships, you will be able to add entries from your own code with the same personal key. Until then, this page documents everything the key can do.

Frequently asked questions

How do I get a VoiceBrain API key?

You generate one inside the iOS app, under Settings. There is no public endpoint to create keys — key management is app-only. The raw key is shown once at creation, so copy it and store it safely.

What does an API key look like?

Every key starts with the prefix hexp_. The full value is shown only once, when you create it in the app.

What is the base URL?

https://api.vbrain.app. Send your key in the Authorization header as a Bearer token on every request.

Can I create entries via the API?

Not yet — the key is read-only today, and a write API is coming. For now, create entries in the app.

What can I read from the API?

You can list your entries, fetch a single entry by id, long-poll until an entry finishes processing, and download an entry's audio while it still exists.

How does pagination work?

GET /entries uses keyset pagination. Pass the nextCursor value from the previous response as the cursor query parameter to get the next page. When nextCursor is null, there are no more entries. limit defaults to 50 and maxes out at 200.

How long is audio available to download?

Recordings are deleted automatically after 7 days. GET /entries/:id/audio returns the m4a while it still exists; after that it returns 404. Check the audioAvailable field before requesting it.

Why is my entry's status 'processing'?

VoiceBrain transcribes and sorts entries in the background. Use GET /entries/:id/wait?after=processing to long-poll until the entry is transcribed, ready, or errors out, instead of polling in a loop.

What happens if my key is invalid or revoked?

The request returns a 401 with code unauthorized. Generate a new key in the app if you need to replace it.

Can a key read other people's data?

No. A key is scoped to your own account and can only read your own entries.

Build on your own VoiceBrain

Generate a read-only API key in the app and pull your entries into scripts, Shortcuts, and agents.

Get VoiceBrain