Go Scribi

The API

Everything in a workspace, over HTTP: notes and their contents, the conversations on them, history and undo, search. A program can do what you can do, and never more.

A first call

Make a key under Your account → API keys, then ask who you are. Every call carries the key as a bearer token, and nothing else authenticates — a browser session is not a way in.

curl https://goscribi.com/api/v1/workspaces \
  -H "Authorization: Bearer $GO_SCRIBI_KEY"

That answers with the workspaces the key reaches, each with its id and what the key may do there. Every other address is inside one: /api/v1/w/<workspace>/…

curl https://goscribi.com/api/v1/w/1/notes \
  -H "Authorization: Bearer $GO_SCRIBI_KEY"

Three kinds of credential

A personal key — gsk_live_…
Yours, acting as you, in the workspaces you choose. Made under Your account → API keys. It is shown once; we keep a fingerprint and not the key. Only you can revoke it — not an administrator — and it keeps working in your other workspaces if one shuts its door.
An application key — gsa_live_…
A program that is a member of one workspace in its own right, with its own permission and its own name in every note's history. Made by an administrator under Settings → Integrations. It survives the person who set it up leaving, and removing the application is the whole of revoking it. Its keys have no level of their own: each can do exactly what the application can. A program that needs less is an application with a lower permission.
An OAuth connection — gso_…
For a client that should never hold a copy of your key: it asks, you press Allow, and it gets a token for one workspace. This is what https://goscribi.com/mcp uses. End it under Your account → API keys.

What a credential may do

A credential never grants anything. A personal key or an OAuth connection is a ceiling: it can only lower what its person already has. A read/write key held by somebody with read on a folder is still read there. An application's keys are not levelled — what the application may do is set on the application.

On a keyAs an OAuth scopeWhat it can do
readreadLook at everything it can reach, and change nothing.
commentcommentAnd join the conversation on a note, without changing the note.
createwriteAnd write, make and delete, wherever the holder can.

The two vocabularies are deliberate: write is the word an OAuth client expects, and create is the word this application uses for the same rung.

When a workspace says no

An administrator can shut two doors, and neither of them revokes anything — a key belongs to the person who made it.

Either way the workspace simply becomes absent to that credential, with a sentence saying which door it was — unless the key never named the workspace at all, in which case it is told nothing, because "never given" and "does not exist" must not be tellable apart.

Errors

Every refusal has the same three fields, and the documentation is a link back to this page, at the anchor for that code.

{
  "error": "conflict",
  "message": "Somebody else changed this note first. Read it again and decide again.",
  "documentation": "https://goscribi.com/docs/api#conflict"
}

404 is the refusal, not 403. Anything you cannot read is absent. 403 only ever means you can already see the thing and may not change it — which is why it can afford to say which of three reasons it was.

unauthorized 401
No credential, or not one that is still live. Send `Authorization: Bearer <key>`. Every reason fails the same way on purpose — revoked, expired, mistyped, or never ours — because a caller who learns which one learns whether a key exists.
not_found 404
Absent, or absent to you. The refusal for anything you may not read: a note, a workspace, a conversation. It is deliberately the same answer as something that was never there. A key that names a workspace it may no longer reach is told why; one that never named it is told nothing at all.
forbidden 403
You can see this and may not change it. The only refusal that admits a thing exists, and it is only ever used where you could already read it.
read_only_key 403
The credential's ceiling is read. Not the workspace's doing and not your permissions: the key itself says read. Make a read/write one under Your account → API keys.
workspace_locked 403
The workspace is over its note limit. Reading still works, and so does deleting — that is the way out. The message carries the two numbers and where to change plan.
cannot_comment 403
This credential may read the conversation and not add to it. Comment level is its own rung between read and write. A read key is told the conversation does not exist at all; this is what a reader who may see it but not answer gets.
kind_is_fixed 422
A note's kind cannot change once it is made. Its contents are a body or rows, and one does not become the other by relabelling. Between a checklist, a map and a board the interface can make a NEW note of the other kind from this one, and the message says where. Sending the kind it already has is not a change, and is accepted.
not_yours 403
Somebody else's words, or a thread somebody else has replied to. You may edit only your own comment. An administrator may delete anybody's and may never edit one.
conflict 409
The note moved while you were deciding. Sent a `lock_version` and it was stale. Read the note again and decide again — a program's write is never merged into somebody else's, because a program did not type anything to merge.
invalid 422
The record refused it. The message is the record's own, in full, so it names the field.
missing_parameter 422
A parameter this call cannot do without was absent. `POST /notes` wants its `note` object; the message names what was missing.
over_the_note_limit 422
Making a note when the workspace is full. The same numbers as `workspace_locked`, answered earlier because creating is the one write that cannot be allowed to half-happen.
over_the_storage_limit 422
Uploading a file the workspace has no room for. The sibling of `over_the_note_limit`, counted in bytes rather than notes. Separate from `invalid` because a program can do something about this one: the message carries how much is used and how much the plan allows, and deleting a file makes room at once rather than overnight.
limit_reached 403
The plan's allowance for something is used up. Answered when a workspace already has as many repeating tasks as its plan allows. It names the allowance, so a program can say which plan is in the way rather than reporting a bare refusal — and stopping one makes room at once.
column_not_empty 422
A board column that still holds cards. Nothing is cascaded here. Move the cards, then delete the column.
too_many_requests 429
Too many requests for one credential. 300 a minute per credential, and 60 a minute on `/subjects`. The answer carries `Retry-After: 60`.
no_workspace 404
An MCP connection that reaches no workspace, or more than one. Only `/mcp` answers this. Point the client at `/mcp/w/<id>`; the message lists the workspaces the connection can see.

The reference

Every endpoint, what it needs, what it takes and what it answers with. The list is generated from the application's own routes, so it cannot quietly fall behind them.

Things that are true of every call

Paging
Lists take page and per_page — fifty by default, a hundred at most, and twenty-five on /search, which takes no per_page at all. The paging fields sit beside the collection rather than around it: {"notes": […], "page": 1, "per_page": 50, "total": 120, "pages": 3}. There are no cursors and no Link header.
Times and dates
Every time is UTC, ISO 8601: 2026-09-16T00:05:17Z. A date that is a date — a card's due date, a diary entry's day — is YYYY-MM-DD.
Writing to a note somebody else might be writing to
Every note carries a lock_version. Send it back with a change and a stale one is refused with conflict; leave it out and the last write wins. Every write to a note's contents — a row, a card, a node — advances it and records a version.
A delete is a bin
DELETE /notes/:id answers 200 with "deleted": true and a sentence saying it can be put back for thirty days. Removing a row, a card or a node is an ordinary write, and answers with the whole note.
Markdown, and what a reader saw
Bodies come back as Markdown. Add ?text=true to a single note or a single version and every Markdown field gains a plain-text twin — text, or note_text on a row, card or node. That is the string a comment's quote is anchored against, so a program matching comments wants it.
Putting a picture or a document in a note
Bodies are text, and a data: address in one is refused — a file in a text column is copied into every version the note keeps and counted against nothing. Send the bytes instead, to POST /notes/:note_id/files: multipart in file, which streams and is allowed the plan's whole upload allowance, or base64 in data with a filename, which is capped lower because it is held in memory. The answer carries markdown — ![name](/w/1/files/7) — ready to paste. Uploading does not change the note. Write that Markdown into the body yourself, with PATCH /notes/:id, or nobody will ever see the file. That is deliberate: an upload is not an edit somebody made, so it costs no version and cannot surprise whoever is typing into the note at the time. The type is decided by the bytes, never by the filename or what you call it, and SVG, HTML and XML are refused whatever they are named, because they can carry scripts and we serve from our own origin.
Rate limits
300 requests a minute per credential, and 60 a minute on /subjects. Over it, too_many_requests and Retry-After: 60.
Versions of the API
One, /api/v1. We may add fields and error codes without changing it, so a client should ignore what it does not recognise. Anything that would break a working program gets a new version.

Claude, and other agents

There is an MCP server on top of all of this, at https://goscribi.com/mcp. Paste that into a client that speaks MCP, press Allow, and it has the same reach a key would give it — no secret to copy. It discovers where to ask by reading the WWW-Authenticate header on a 401, which points at /.well-known/oauth-protected-resource.