> ## Documentation Index
> Fetch the complete documentation index at: https://vendo-mintlify-24213046.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Service keys & broker

> The one long-lived secret behind tokenFor: where it lives, what the audit shows, and how to rotate it.

Behind `vendo.tokenFor()` sits one long-lived secret: your service key. It stays
on your backend, your agent never sees it, and every call trades it for a token
that lasts ten minutes. This page is what happens behind that one call.

<svg viewBox="0 0 760 118" role="img" aria-label="A service key path: the key, the token exchange, a ten-minute token, your door" style={{ width: "100%", height: "auto", margin: "1.5rem 0" }}>
  <g fill="none" stroke="currentColor" strokeOpacity="0.2">
    <rect x="1" y="1" width="164" height="52" rx="11" />

    <rect x="199" y="1" width="164" height="52" rx="11" />

    <rect x="397" y="1" width="164" height="52" rx="11" />

    <rect x="595" y="1" width="164" height="52" rx="11" />
  </g>

  <g fill="none" stroke="#6c3bff" strokeWidth="1.5" strokeLinecap="round">
    <path d="M169 27h20" />

    <path d="M367 27h20" />

    <path d="M565 27h20" />
  </g>

  <g fill="#6c3bff">
    <path d="M189 23l7 4-7 4z" />

    <path d="M387 23l7 4-7 4z" />

    <path d="M585 23l7 4-7 4z" />
  </g>

  <g fill="currentColor" fontSize="13.5" fontWeight="600" textAnchor="middle">
    <text x="83" y="32">Service key</text>
    <text x="281" y="32">tokenFor()</text>
    <text x="479" y="32">10-minute token</text>
    <text x="677" y="32">Your door</text>
  </g>

  <g fill="currentColor" fillOpacity="0.55" fontSize="11.5" textAnchor="middle">
    <text x="83" y="76">lives in your backend</text>
    <text x="281" y="76">token exchange</text>
    <text x="479" y="76">acts as one user</text>
    <text x="677" y="76">checks the signature</text>
  </g>
</svg>

## Where the key lives

You never create this key or paste it anywhere. On Vendo Cloud, the first time
anything reaches your door, Cloud mints one for your tenant — which is why init
never asks. For a local dev door, init generates one itself and writes
`VENDO_SERVICE_KEY` into `.env.local`. The console is where you see the Cloud
one, rotate it, or revoke it.

Your agent only ever gets the ten-minute token, never the key, and `tokenFor` is
the only line of your own code that touches either one.

## Who the audit says acted

Every call the token makes is recorded under two names: the user it acts as, and
the key that asked for it.

The key shows up as a short fingerprint — `svc:5c006a4c`, say — so an audit row
can tell you which key acted without the key itself ever landing in it.

To your policy this is that user, not an agent: a write still parks for
approval, and the approval names the tool and the person.

## Rotating a key

Rotating never cuts anything off mid-flight. Create the replacement in the
console, restart your backend so it picks the new one up — the SDK fetches the
key itself, there is nothing to copy — then delete the old one. Tokens the old
key already handed out keep working for up to ten minutes.

## When the mint refuses

Three of these come from the exchange; the last shows up later and gets misread.

| Answer                       | What it means                                                       |
| ---------------------------- | ------------------------------------------------------------------- |
| `invalid_client`             | This server doesn't know that key, or it was deleted                |
| `invalid_request`            | The user id wasn't a real id — `tokenFor` catches it before it asks |
| `invalid_target`             | The `resource` sent names some other MCP server, not this one       |
| `401` on the first tool call | The token is fine; the user id in it isn't someone you recognize    |

`invalid_client` is vague on purpose: anything more specific would tell whoever
is guessing which half of the credential they got right.

The `401` looks like a key problem and isn't. The exchange never checks that the
user id is real, so a token for a stranger mints happily and dies on the first
request, when the door asks your app who that is and gets nobody back.

## The broker

The broker is the piece that hands out the tokens, and on Vendo Cloud you don't
run it — `VENDO_API_KEY` is the whole setup. It asks one thing of you:
`VENDO_BASE_URL` has to be an `https://` origin. Only the sign-in page needs to
be reachable from a browser — `tokenFor` dials out from your backend, so nothing
has to reach in.

Handing your own agent a bearer is one call:

```ts app/api/agent-token/route.ts theme={null}
import { vendo } from "@/lib/vendo";

export async function POST(request: Request) {
  // Pass the incoming request to act as whoever is signed in,
  // or a user id to act as a named user.
  return Response.json({ token: await vendo.tokenFor(request) });
}
```
