Cautesay: caught
AppsHow it findsYour AIPricingHelpDevelopersSign inStart free

For developers

  • Overview
  • The API
  • MCP
  • Build a capture surface
  • Build a processor
  • Processors you can use
  • Recommended keys
  • Webhooks
  • The command line
  • Coding agents
  • The card format
  • Numbers
  • Caute's fetcher

Build a capture surface

How to send things to Caute from wherever you work: a script, a launcher, a menu bar app, a plugin, an automation. The command line, @caute/cli, is the worked example.

Everything here is the public API, https://api.getcaute.com/v1. Caute's own apps use it the same way.

1. Get a credential

Pick the one that fits who is using it.

You're building Use How
A script, cron job or automation for yourself A personal token Caute → Settings → Apps and tokens. Shown once. Send it as Authorization: Bearer <token>.
An app other people install OAuth 2.1 with PKCE Register the app (below), then the authorization code flow with resource set to the API.

Ask for the least you need. A capture tool needs only items:write, and a token with just that can save and nothing else: it can't read or search anything already saved. Add the others only for what you'll use:

Scope Lets it
items:write Save things. Tags it sends arrive as suggestions (below).
notes:write Write a note on what it saved, or on any card in its spaces.
tags:write Apply tags directly instead of suggesting them.
items:read Read cards and their text.
search Search. A token that can search and also write can be steered by what it reads, so the consent screen warns about the pair.

Every credential is also limited to the spaces chosen when it was made. Nothing it does reaches past them.

OAuth, for an app on someone's computer. Use a loopback redirect: listen once on 127.0.0.1 on any free port, and register http://127.0.0.1/callback. A registered loopback address matches any port. Find the endpoints from the API's own metadata, so a development server works the same way:

GET https://api.getcaute.com/.well-known/oauth-protected-resource    → resource, authorization_servers
GET <authorization server>/.well-known/oauth-authorization-server   → authorization_endpoint, token_endpoint

Send resource=https://api.getcaute.com with both the authorization request and the token request; a token issued for another resource is refused. Refresh tokens rotate, and using one twice revokes the whole grant, so never refresh in two places at once. A refresh answered 400 invalid_grant means the person disconnected you in Settings: forget the tokens and ask them to sign in again.

To have your app pre-registered, so the consent screen can name it, write to support@axionarylabs.com. Until then, an app can describe itself with a Client ID Metadata Document: its client_id is the HTTPS address of a JSON document naming it and its redirect addresses.

2. Save something

POST /v1/items with exactly one of url, text or upload_id, or a file as multipart. It answers 202 at once with the new card's id; reading, OCR and indexing happen afterwards.

POST /v1/items
Authorization: Bearer <token>
Idempotency-Key: 6f1c0f0e-3b7e-4a4f-9e61-1d1f0b8a2c57
Content-Type: application/json

{ "url": "https://example.com/article",
  "source": "api",
  "context": { "source_app": "my-launcher" },
  "tags": ["reading/later"] }
{ "id": "0190f3a2-7c1d-7e4b-9a10-3f2e1d0c9b8a", "status": "captured", "duplicate": false, "supersedes": null }
  • source is how it arrived. Use api. Say what you are in context.source_app; the card shows it.
  • context is free-form and kept with the card (a selection, the page's title, where it was caught). Keep it small; it is limited in size (413 context_too_large).
  • title is optional. Nothing is required beyond the content itself.
  • space_id chooses where it goes. Leave it out and it goes to the credential's default space, which is the person's private inbox unless they chose otherwise. GET /v1/spaces lists the spaces a credential can see, with access: write on the ones it can save to.
  • duplicate: true means it was already saved in that space: you get the existing card's id, and the context you sent is merged into it. Saving the same thing twice never makes two cards.
  • external_id is for something you'll send again as it changes, such as a document someone keeps editing. The same external_id with new content makes a new version of the same card (supersedes names the one it replaced); with the same content it's a duplicate.

Tags arrive as suggestions

Tags you send with items:write alone arrive as suggestions the person accepts or dismisses in Caute. They don't file anything until a person confirms them. With tags:write they are applied directly. Either way, a tag path is lowercase and /-separated (clients/acme/contracts). A path that doesn't exist yet is created: as the person's own tag in a private space, or as the team's shared tag in a shared one.

Notes

A note isn't part of a save. Save first, then write the note on the card with notes:write:

POST /v1/notes
{ "body": "Read this before Thursday.", "annotates": { "item_id": "0190f3a2-…" } }

3. Files

  • Up to 10 MiB: send it in the request as multipart/form-data, with the file as file and anything else (source, context, tags, space_id) as a JSON string in metadata.
  • Larger: upload it first. POST /v1/uploads with its byte_size and sha256 (hex), and optionally filename and space_id, answers with a url, a method (PUT) and headers. Send the bytes there, exactly as given, with those headers, then save it with { "upload_id": "…" }. Caute checks the size and hash before keeping it. Sending a large file inline is refused with 413 use_uploads.

Don't worry about the file's type: Caute reads it from the bytes.

4. Retry safely

Networks drop answers. Send an Idempotency-Key (any unique string; a UUID is good) on every save, make a new one for each thing the person saves, and send the same key again when you retry that save. Caute answers a repeated key with the first answer, marked Idempotent-Replayed: true, and saves nothing new.

  • The same key with a different request is refused: 422 idempotency_key_mismatch.
  • The same key while the first request is still being handled: 409 idempotency_in_progress with Retry-After. Wait that long and send it again.
  • Retry when the connection fails, and on 429, 502, 503 and 504, waiting Retry-After seconds when it is given and backing off when it isn't. Don't retry other 4xx answers: they won't change.
  • A follow-up request (an upload, a note) needs a key of its own.

5. Handle Caute's answers

Errors are RFC 7807 problem details, with a stable code you can act on and a title (and sometimes detail) you can show to a person:

{ "type": "about:blank", "status": 403, "code": "insufficient_scope",
  "title": "This credential lacks the search scope.", "request_id": "532b81c3a4480e359c844217" }
Status and code What it means What to do
401 The token is wrong, expired or revoked. Refresh once (OAuth); otherwise ask the person to sign in or make a new token.
403 insufficient_scope The credential lacks a scope; the title names it. Say which permission it needs.
403 space_not_writable It can't save to that space. Leave out space_id, or let the person choose another.
402 quota_exceeded Past the plan's storage. Nothing already saved is affected. Show the title and detail.
413 use_uploads A file too large to send inline. Use POST /v1/uploads.
422 invalid_request The request didn't match the API. errors lists each field and why. Fix the request.
422 one_content_field More than one of url, text, upload_id and a file. Send one.
429 rate_limited Too many requests. Wait Retry-After seconds.

code values are part of the API's contract, and new ones may appear: treat one you don't know by its status. Include request_id when you report a problem.

Rate limits. Every answer carries RateLimit-Policy ("ingest";q=…;w=…: how many requests in how many seconds) and RateLimit (r= remaining, t= seconds until it resets). Saving (with uploads) and searching have budgets of their own. Pace a bulk import by them.

6. Be a good guest

  • Save without asking anything. Caute never needs a title, a tag or a folder to save something, and neither should your tool.
  • Say what happened in a line: saved, already saved, or why not.
  • Send only what the person asked you to send. If your tool reads other content to work (a page, a document), say so where the person can see it.
  • A token is a password. Keep it out of logs, repositories and URLs; store it where only its owner can read it.

The worked example

npx @caute/cli (Node 20 or newer):

caute login                                    # OAuth in the browser, loopback hand-back
caute save https://example.com --tag reading/later --note "For Thursday"
echo "an idea worth keeping" | caute save -
caute save report.pdf --space "Research"
caute search kelp forests

Or with a token, for scripts: CAUTE_TOKEN=… caute save notes.md.

Start keeping what you find.

Start freeSign in
Caute, by Axionary LabsAppsAssistantsMemoryTeamsCapture recordCompareHelpSupportPrivacyTermsSecuritySubprocessorsDPADevelopers© 2026 Axionary Labs