Authentication and API keys
When to sign in (OAuth) and when to use an account API key, what a key looks like, its abilities, product-scoped keys, limits, revocation and error codes.
View as MarkdownRequest header
Every call authenticates with Authorization: Bearer <key>. The canonical host of the public API is api.gujo.ai.
curl https://api.gujo.ai/api/products \
-H "Authorization: Bearer $GUJO_API_KEY"
Older clients call gujo.ai/api/..., which is forwarded to api.gujo.ai, but that path only passes the authorization, content-type, accept, accept-language and user-agent headers plus X-* headers. New code should always call api.gujo.ai.
Signing in (OAuth) or an API key
To connect an agent to the customer MCP, sign in first. Use a key where nobody can sign in through a browser.
| Signing in (OAuth) | Account API key | |
|---|---|---|
| Works with | The customer MCP (https://api.gujo.ai/api/mcp) only |
The whole account API and the customer MCP |
| Use it for | MCP clients people use, such as Claude Code, Codex, Cursor, VS Code and Claude.ai | CI, scripts on a server, headless agents and direct calls such as curl |
| How you get it | Give your client the server URL, then sign in and approve its permissions in the browser | Create it in Developer settings and put the value, shown once, into an environment variable |
| Permissions | What you approved (by default mcp.read and products.read) |
The abilities you picked at creation |
| Lifetime | Access token one hour, refreshed by the client (expires after 30 days without use) | Until the expiry you set (or until revoked) |
| Disconnect | Connected apps in your account security settings | Revoke it in the key list in Developer settings |
Both use the same permission names and the same checks, so the ability table below applies to sign-in permissions too. Tokens you get by signing in are accepted only by the customer MCP, so calling other account API endpoints with them returns 401 invalid_api_key. Connection steps and token lifetimes are in Connect MCP.
If you build your own MCP client, follow the standard OAuth flow (MCP 2025-11-25 authorization). Start from the protected resource metadata at https://api.gujo.ai/.well-known/oauth-protected-resource/api/mcp and the authorization server metadata at https://account.gujo.ai/.well-known/oauth-authorization-server. Only the authorization code flow with PKCE (S256) is accepted. Register your app with a client ID metadata document (CIMD) or dynamic registration (POST /oauth/register). Send resource=https://api.gujo.ai/api/mcp in the authorization and token requests.
What a key looks like
- Keys start with
gujo_. - The server stores only a SHA-256 hash of the key. The plain value is shown once at creation, and after that not even Gujo can show it again.
- The key list on the Developer settings page shows each key's name, its first 14 characters (the prefix) and its abilities. Use the prefix to tell keys apart.
Abilities
Each key lists the abilities it may use. The ability each endpoint needs is in the ability column of the API reference. When it is empty, any valid key will do.
| Ability | Opens | Keys that have it from the start |
|---|---|---|
products.read |
Library, product list and detail, update checks, install plans, setup progress, product license | Keys you create, device sign-in keys |
orders.read |
Your orders, invoices and payment methods | None (pick it when you create a key) |
subscriptions.read |
Your subscriptions | None (pick it when you create a key) |
mcp.read |
Customer MCP (POST /api/mcp) |
Keys you create |
setup-progress.write |
Recording product setup progress | None (pick it when you create a key) |
device.install |
Install records, app distribution feeds and downloads | Keys you create, device sign-in keys |
device.entitlements |
Owned products, license checks, Pass checks | Device sign-in keys |
device.activate |
Bulk app activation | Device sign-in keys |
device.usage |
Product usage upload | Device sign-in keys |
support.report |
App problem reports | Device sign-in keys |
- A key you create starts with
products.read,mcp.readanddevice.install. - A key from device sign-in in gujoctl or Gujo Cloud Apps gets six:
products.read,device.entitlements,device.activate,device.install,device.usageandsupport.report. If you needorders.read,subscriptions.readormcp.read, create a separate key on the Developer settings page. support.staff,commerce.staffanddesign.staffare abilities for Gujo operators. They do nothing on an ordinary account's key.
Product-scoped keys
Most keys cover the whole account. Some are bound to one product, such as the key Gujo Cloud Apps receives when it activates an app.
GET /api/librarywith a product-scoped key returns only that product.- Calling another product's path returns 403
product_api_key_scope_required.
Limits and expiry
- You can hold up to 3 live keys per scope. Account-wide keys and the keys bound to each product are counted separately.
- If there is no free slot when you create a key, issuing fails and the page shows the limit. Revoking a key frees a slot.
- Device sign-in with no free slot revokes the least recently used key of the same client and issues a new one.
- A key you create works until the expiry date you set (or until you revoke it, if you set none). A device sign-in key expires after 90 days without use, and each use pushes that date back.
Revocation
- Revoke a key at any time from the key list on the Developer settings page.
gujoctl logoutrevokes the gujoctl key on the server, then deletes it from the device.- A revoked or expired key gets 401
invalid_api_keyfrom the next request on.
If you think a key has leaked, revoke it first, then create a new one.
Errors
Authentication and permission errors have the body {"message": "...", "error": "<code>"}. /api/* never redirects to a sign-in page. It always answers with JSON.
| Status | error |
Meaning |
|---|---|---|
| 401 | missing_api_key |
No Authorization header |
| 401 | invalid_api_key |
Unknown, revoked or expired key |
| 403 | missing_api_key_ability |
The key lacks the ability this endpoint needs |
| 403 | product_entitlement_required |
You have no entitlement to that product |
| 403 | product_api_key_scope_required |
A key bound to one product called another product |
| 404 | — | The target does not exist |
| 422 | — | Validation failed. The body is {message, errors} |
| 429 | — | Rate limit exceeded. The whole API allows 60 requests a minute, and some endpoints add a tighter limit of their own |
The ability check runs before the target is looked up, so a key without the ability cannot learn whether an id exists (it gets 403, not 404).
Handling keys
- Send keys only in the header. Never put them in a URL query or path.
- Keep environment variable names, not key values, in config files and repositories. Every key config in Connect MCP points at
GUJO_API_KEY. - Don't send the key to the signed download URL an install plan returns. Keys go to the API host only.
- For an MCP client a person uses, connect by signing in instead of using a key. Then no key needs to sit in a config at all.