Home › Documentation › API › Authentication

Authentication

A key is created from inside the product. It is shown once, it reaches one desk, and revoking it takes effect on the next request.

Creating a key

Sign in to OutcomeDev and open the account menu in the top right corner. Choose API keys, give the key a name, and create it. The name is the only thing asked for, and it matters more than it looks: it is what appears against the key in the list afterwards, and what appears in the desk's own log when the key is used, so a name like reporting-script or claude-desktop saves time later in a way that key 2 does not.

Creating a key is a signed-in action, on purpose. An API that can mint its own credentials is one where a leaked key becomes a way to make more keys, and the point of scoping a key to one desk is that it stays scoped. There is deliberately no route in version 1 that creates a key.

What a key looks like

od_K7fQ2mVx8sT1pR4nB6wY3zA

Every key begins od_. That prefix is there for one reason: so a leaked string is recognisable in a log, a screenshot or a commit, by a person or by a scanner. The rest is random.

The list of keys in the product shows the first eleven characters — the od_ and eight more — and never the whole thing again. That prefix is enough to match a key against an entry in a log line without being enough to use it.

The secret is shown once. When a key is created, the product shows it a single time and then keeps only a hash of it. There is no screen, no route and no support process that will show it again, because there is nothing left to show. If the secret is lost, create another key and revoke the old one. That is a two-minute job, and it is the intended way to handle it.

Copy it somewhere a program can read before leaving the page. A key pasted into a chat window, a ticket or a screenshot has to be treated as leaked and revoked.

Every request except the OpenAPI description carries the key in a bearer header:

Authorization: Bearer od_K7fQ2mVx8sT1pR4nB6wY3zA

A request with no key, with a key that does not exist, or with a key that has been revoked is answered 401 unauthorized. The header is the only supported way in for a client. One shortcut exists for a person checking something in a browser address bar — ?token=od_... — and it is a diagnostic, not a transport: a query string ends up in browser history, in proxy logs and in the Referer header of anything the page loads. Do not put a key in a URL from code.

Revoking

Choose Revoke beside a key in the same list. Revocation is immediate: the next request carrying that key is answered 401 unauthorized, with the message "That key is not valid, or has been revoked." There is no propagation delay and no cache to wait out.

Revoking is not a delete. The key stays on the record with the date it was revoked, because "which key was that" is a question people ask after an incident and a deleted row cannot answer it. The desk's log records both the creation and the revocation, with the key's prefix and name.

The security rules

Never in a browser

Do not put a key in front-end JavaScript, in a page, in a mobile app bundle or in anything else that is delivered to a user's device. A key is a full credential for one desk: it can read every piece of work, complete it, and write notes. There is no scoped, read-only or public key in version 1, so there is no safe way to ship one.

A browser-facing interface needs a server of its own in between. The server holds the key, the browser talks to the server, and the server decides what the browser may ask for.

Never in source control

Read the key from the environment, not from a file that is committed. The variable names the MCP server expects are the sensible defaults for anything else:

export OUTCOMEDEV_API_KEY=od_K7fQ2mVx8sT1pR4nB6wY3zA
export OUTCOMEDEV_BASE_URL=https://outcomedev.com

If a key is committed, revoke it before doing anything else. Rewriting history is worth doing and is not a substitute: the string has already been pushed, and bots watch public repositories for exactly this pattern.

One key per integration

Create a separate key for each thing that will use one — the reporting script, the agent, the internal dashboard. Not because the API behaves differently, but because revocation is the only lever there is. A key shared between four systems cannot be revoked without breaking all four, which in practice means it never gets revoked at all. Separate keys also make the desk's log readable: an entry names the key that wrote it.

RuleWhy
Never in a browser or an app bundleA key has no read-only mode. Anything shipped to a device is public
Never in source controlOnce pushed, the string is public and cannot be recalled
One key per integrationRevocation must not break unrelated systems
Never in a URL from codeURLs are logged, cached and forwarded
Treat a shared secret as leakedAnything pasted into a chat, a ticket or a screenshot has to be revoked

Transport

The API is served over HTTPS at https://outcomedev.com. Requests are JSON: send Content-Type: application/json on a write, and read the body as JSON. Bodies are limited to 1 MB, which is far more than any request here needs.

Requests are scoped to the desk in the key for their whole duration. There is no combination of path and query in this API that reaches another desk, and no header that changes which desk is being addressed.

Next

  • Quick start — five minutes to a completed row.
  • Errors — what a 401 means and what to do about it.
  • The desk — reading the composition through the same key.