Extensions
Anything that runs in someone else’s runtime and reaches a collection through the same API key: a browser extension, an editor plugin, a script somebody schedules. There is no separate extension API — you are using the HTTP API or the MCP server, from a place with different constraints.
Your extension cannot keep a secret
This is the one thing that is genuinely different, and it is worth being blunt about: anything you bundle into a published extension is readable by anyone who installs it. Minification is not encryption, and a key in a build artefact is a key you have given away.
So the key belongs to the user, not to you:
- The user creates it on their own account page and pastes it into your extension’s settings. You never see it and never transmit it anywhere but to us.
- Store it in the runtime’s credential store —
chrome.storage.localfor a browser extension, the editor’s secret storage for a plugin — rather than in a config file that gets committed or synced. - Ask for read only unless your extension visibly writes. A user who grants write to a reader has no way to discover the mistake.
- If you ever log requests, redact the header. A key in a log file is a key in a bug report.
What an extension is allowed to do
Exactly what any third-party key is allowed to do. There is no elevated tier: an extension gets the same sixteen MCP tools, or the same HTTP endpoints, and its writes are proposals your user accepts unless they have granted that specific key the right to apply directly. An extension is not a privileged client, and building one does not make it one.
CORS, before you discover it from a failed preflight
The API answers cross-origin requests only from origins it has been told about, and the allowlist is empty by default. Your origin — a chrome-extension://… URL, or wherever your page is served from — has to be added to CORS_ORIGINS on the deployment you are talking to. On a self-hosted instance that is an environment variable; against cardaga.in, ask.
A background script or a native host has no origin and no preflight, so it is unaffected. If you can make the call from there rather than from a content script, do — it is less configuration and one fewer place the key travels.
A worked example
Capture whatever the user has selected on a page and turn it into a note. This is the whole of it: one request, with the key read from the extension’s own settings.
curl -X POST "https://api.cardaga.in/notes" \
-H "Authorization: Bearer $CARDAGAIN_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"deckId": "3f2a1b4c-0000-4000-8000-000000000002",
"noteTypeId": "3f2a1b4c-0000-4000-8000-000000000001",
"fields": {
"Front": "the selected text",
"Back": ""
},
"tags": [
"clipped"
]
}'Two things to get right that the sample cannot show you: read GET /note-types once and cache the field ids, because field values are keyed by id rather than by name and names are editable; and send a requestId on anything the user might double-click, so a retry produces one note rather than two.