Skip to main content

Authentication

Free tier​

Create a free account at app.apistash.io and connect with an API key or an OAuth token — MCP requests must be authenticated, so there is no anonymous access. The free tier gives you the built-in tools plus your own custom tools, prompts, resources, and agent definitions, subject to the free-plan rate limits and quota.

Which built-in tools your credentials actually reach is your choice: select them under Settings → MCP Settings in the dashboard. Nothing is selected to begin with, which keeps each agent's tool list short — see Quick Start.

info

Rate limits and the monthly quota apply per account — per organization for organization credentials — not per API key. Upgrade your plan for higher limits.

Account registration and email verification​

To create an account, register at app.apistash.io with your email address and a password.

CAPTCHA verification​

The registration endpoint requires a CAPTCHA token to protect against automated account creation. Pass the token returned by the CAPTCHA widget as the captcha_token field in the request body:

{
"email": "you@example.com",
"password": "your-password",
"first_name": "Jane",
"last_name": "Smith",
"locale": "en",
"captcha_token": "<token-from-captcha-widget>"
}

locale is required and sets the language of every email the account receives. It is a two-letter language code; a regioned tag such as de-CH is accepted and stored as its primary subtag (de).

apistash supports a fixed set of languages — the ones the dashboard's language picker lists. At sign-up the language comes from your browser rather than from a choice you made, so a supported-looking code outside that set is stored as English instead of failing the registration. A value that is not a two-letter code at all is a 400. Note that the supported set is wider than the set apistash has translations for: an email in a language with no translation yet is sent in English.

Registering through the dashboard does not ask for it: the form sends your browser's language. You can change it any time under Settings → Account → Email language, and from then on that choice is what counts — it lives with your account, so it follows you to every browser and device. Changing it there is a deliberate choice, so PATCH /api/user/profile rejects a language outside the supported set with a 400 rather than quietly storing a different one.

If the captcha_token field is missing or the token is invalid, registration returns 422 Unprocessable Entity. The dashboard registration page handles this automatically — the CAPTCHA widget is embedded in the form and the token is submitted with your credentials.

info

CAPTCHA verification is required on account registration — including registering through an organization invitation — when resending the verification email, and when requesting a password reset. Login and token refresh do not require a CAPTCHA token.

After registering, apistash sends a verification email to the address you provided. You must click the link in that email before you can sign in. Attempting to log in with unverified credentials returns:

{
"errors": [
{ "code": "authentication_error", "message": "Email address not verified", "detail": null }
]
}

with HTTP status 401 Unauthorized. (Shown here trimmed to the errors array — every response carries the full envelope.)

tip

If you sign in with a third-party OAuth provider (e.g. Google), your account is automatically verified — no email step is required.

warning

Verification links expire after 24 hours. If your link has expired, you can request a new one — see Resending the verification email below.

Resending the verification email​

If your verification link has expired, request a new one via the login page — when you attempt to sign in with an unverified account, an amber notice appears with a "Resend verification email" button. You can also resend directly via the API:

curl -X POST https://api.apistash.io/api/auth/resend-verification \
-H "Content-Type: application/json" \
-d '{
"email": "you@example.com",
"captcha_token": "<token-from-captcha-widget>"
}'

200 OK means the resend request was accepted. It does not confirm an account's state or guarantee that an email was delivered: an unknown address, an already-verified address, or another suppressed outcome receives the same response, and a later delivery failure does not change that response. This keeps address state private.

The endpoint can also return:

StatusMeaning
400 Bad RequestThe request body is not valid JSON or does not match the documented request format.
413 Payload Too LargeThe request body exceeds the 2 MB limit.
415 Unsupported Media TypeThe Content-Type has neither the json subtype nor a +json suffix.
422 Unprocessable EntityCAPTCHA verification failed.
503 Service UnavailableEmail delivery is temporarily unavailable before the request can be accepted. Nothing was saved, so it is safe to retry.
info

To prevent email flooding, apistash enforces a per-user cooldown between resend requests (5 minutes). Requests within the cooldown window are silently ignored — no email is sent, but the endpoint still returns the same status and body as a successful send.

Password reset​

To request a password reset, send a POST request to /api/auth/password/reset with your email and a CAPTCHA token:

curl -X POST https://api.apistash.io/api/auth/password/reset \
-H "Content-Type: application/json" \
-d '{
"email": "you@example.com",
"captcha_token": "<token-from-captcha-widget>"
}'

200 OK means the password-reset request was accepted. It does not confirm that an account exists or guarantee that an email was delivered: suppressed outcomes receive the same response, and a later delivery failure does not change that response. This keeps address state private.

The endpoint can also return:

StatusMeaning
400 Bad RequestThe request body is not valid JSON or does not match the documented request format.
413 Payload Too LargeThe request body exceeds the 2 MB limit.
415 Unsupported Media TypeThe Content-Type has neither the json subtype nor a +json suffix.
422 Unprocessable EntityCAPTCHA verification failed.
503 Service UnavailableEmail delivery is temporarily unavailable before the request can be accepted. Nothing was saved, so it is safe to retry.
info

To prevent email flooding, apistash enforces a per-user cooldown between password reset requests (5 minutes). Requests within the cooldown window are silently ignored — no email is sent, but the endpoint still returns 200 OK.

Signing in with SSO​

If your organization has set up single sign-on, sign in with your work email: enter it on the login page and you will be redirected to your company's identity provider to authenticate. When you return, you are signed in — no separate apistash password required.

If your organization requires SSO for your email domain, password sign-in is disabled for members; use the organization sign-in button instead. A linked personal account can still reset its password, but cannot use that password to sign in while the SSO requirement remains active. Organization-managed accounts are passwordless, so their reset requests are silently ignored. The organization owner remains exempt from the SSO requirement. Single sign-on is an Enterprise-plan feature, configured by an organization administrator.

API keys​

An API key is how an agent authenticates to the MCP endpoint. Generate one from the dashboard after verifying your account. (Rate limits and quota are set by your plan and shared across all your keys — see Plans & Limits.)

Creating an API key​

  1. Sign in at app.apistash.io.
  2. Navigate to API Keys in the sidebar.
  3. Click New API Key.
  4. Enter a descriptive name (e.g. "My MCP Client").
  5. Select the grants, policies, and capabilities the key should carry. A key with no capability selected reaches only the authenticated system tools over MCP and no governed content category.
  6. Optionally assign an agent definition as this key's default for primary tasks. Enable Agents for assignment and use; the key's final scope, team bindings, and any Agent policy must make that Agent available.
  7. Click Create key.

The optional Agent assignment is saved atomically with the key. If the selected Agent is not available to the final credential configuration, no key is created and no one-time secret is issued. You can leave the key unassigned or change its assignment later from the key detail page. The task-start instruction identifies the primary with primary_agent: true. Subagents never inherit the assignment: their parent explicitly supplies a named role, permission to self-select, or no role. Empty Bootstrap arguments are invalid.

For optional persistent task-start setup, the authenticated system tools setup, bootstrap, and agent_context do not require a Tools setting or tool-policy entry. If an assigned agent definition should be selected, offered, or used for conditional behavior, enable Agents. Agent ownership, team exposure, and Agent policies still apply.

After connecting the credential to a client, ask its local agent exactly "Set up apistash." The dashboard's key and OAuth-connection detail pages repeat this step and show the relevant capability or scope configuration. Bootstrap and advertised tools establish current effective access. General setup also offers optional guided starter content, with concrete drafts and targets confirmed before governed writes. See Set up task-start context; the setup does not put secrets in the prompt and does not let the apistash server or dashboard write local client files.

warning

Your new key is shown once only, immediately after creation. Copy it and store it in a secure location (e.g. a password manager or secrets vault) before leaving the page. It cannot be retrieved again.

info

Key names must be unique across your active keys. Attempting to create a second key with the same name returns 409 Conflict. Choose a different name or revoke the existing key first.

API keys are prefixed with sk_ followed by a UUID (e.g. sk_550e8400-e29b-41d4-a716-446655440000). Always store the full string.

What controls an API key​

API keys do not have an OAuth-style scopes list — scopes belong to OAuth tokens. What an API key can do is governed entirely by the controls described in the sections below:

  • Grants — which management endpoints the key may call.
  • MCP capabilities — which native MCP surfaces (tools, resources, prompts) and whether Agent-definition use the key reaches.
  • Tool, resource, prompt, and agent policies — which specific items the key may use.
  • Organization scope and team bindings — which organization the key runs against and which team content it sees.

To limit a key to a single tool, use a tool policy — not a scope.

The four per-key item policies — tool_policy, resource_policy, prompt_policy, and agent_policy — apply only to personal keys without org_scope. On an org-scoped key, content reach comes from the organization and the key's team bindings instead. Creating an org-scoped key with a non-all item policy, or supplying any item-policy value when updating one (including an explicit {"mode": "all"}), returns 422 Unprocessable Entity. In key list, detail, and update responses, all four flat item-policy fields are omitted for this organization lane. The applicable policies are returned per bound team under teams instead.

Grants​

An API key may declare grants that define which management contexts it is permitted to act in. A grant is either personal (the key can act on your own account) or org-scoped (the key can act within a specific organization). Omitting grants, or passing [], is valid; that key simply has no grant-gated management permissions.

Personal grant​

A personal grant allows the key to authenticate on personal endpoints (e.g. GET /api/me/resources). You can optionally list which fine-grained personal permissions the grant carries:

PermissionWhat it permits
can_view_resourcesRead your personal resources.
can_create_resourcesCreate new personal resources (implies view).
can_update_resourcesEdit existing personal resources (implies view).
can_delete_resourcesDelete personal resources (implies view).
can_view_toolsRead your personal custom-tool inventory.
can_create_toolsCreate personal custom tools (implies view).
can_update_toolsEdit personal custom tools (implies view).
can_delete_toolsDelete personal custom tools (implies view).
can_view_promptsRead your personal prompt inventory.
can_create_promptsCreate personal prompts (implies view).
can_update_promptsEdit personal prompts (implies view).
can_delete_promptsDelete personal prompts (implies view).
can_view_agentsRead your personal Agent inventory.
can_create_agentsCreate personal Agent definitions (implies view).
can_update_agentsEdit personal Agent definitions (implies view).
can_delete_agentsDelete personal Agent definitions (implies view).
can_view_usageRead your activity history.
can_update_settingsUpdate your account settings.
can_view_dashboardRead the personal dashboard summary.

If permissions is omitted or empty, the key can authenticate to personal endpoints but will be denied by any handler that checks a specific permission.

These management permissions are also used by the corresponding MCP mutation tools. They are independent of MCP-use capabilities: Agent mutation tools require Tools plus their action permission, while list_agents and get_agent require Tools plus Agents visibility.

Interactive authentication required​

The following endpoints are only accessible via session cookie or JWT — API key authentication is always rejected with 403 Forbidden, regardless of the permissions on the key:

EndpointDescription
GET /api/keysList API keys
GET /api/keys/{id}Get a single API key
POST /api/keysCreate an API key
DELETE /api/keys/{id}Revoke an API key
POST /api/auth/logoutSign out
POST /api/user/emailInitiate an email address change
DELETE /api/user/accountDelete the account

These endpoints require a browser session or a short-lived JWT to prevent an API key from being used to escalate privileges, rotate credentials, or destroy the account that issued it.

{
"name": "read-only agent",
"grants": [
{
"scope": { "type": "personal" },
"permissions": ["can_view_resources", "can_view_usage"]
}
]
}

Org grant​

An org grant allows the key to act within a specific organization. Pass the organization's UUID in org_id and list the organization permissions the grant should carry:

{
"name": "ci-agent",
"grants": [
{
"scope": { "type": "personal" },
"permissions": []
},
{
"scope": { "type": "org", "org_id": "<org-uuid>" },
"permissions": ["can_view_organization_members", "can_view_organization_api_keys"]
}
]
}

A key with only an org grant (no personal grant) cannot access personal endpoints — any personal endpoint returns 401 Unauthorized. A key with no org grant for a given organization is treated as a non-member — the org endpoint returns 404 Not Found.

Grant rules​

  • Grants are optional. grants: [] is valid and grants no management permissions.
  • You cannot grant more than you hold. If you try to include a permission you do not currently have in that organization, the key creation is rejected with 422. This prevents privilege escalation. Permissions you hold by implication count as held — see below.
  • Owner-only permissions cannot be delegated. The permissions can_delete_organization, can_update_organization_billing, and can_transfer_organization_ownership cannot be placed in any API key grant and are always rejected with 422.
  • One grant per management context. Duplicate contexts (two personal grants, or two org grants for the same org) return 422.
  • You must be a member of the org. Trying to create an org grant for an organization you are not a member of returns 422.

Implied permissions​

Holding a write permission means holding the matching read permission. Personal-lane examples:

  • can_create_resources → implies can_view_resources
  • can_update_resources → implies can_view_resources
  • can_delete_resources → implies can_view_resources

And in an organization grant:

  • can_invite_organization_members → implies can_view_organization_members
  • can_remove_organization_members → implies can_view_organization_members

This works in both directions, and both are useful:

  • You never need to list an implied permission. Grant the write permission and the read comes with it at enforcement time.
  • You may grant an implied permission on its own. If you hold can_update_organization_roles, you also hold can_view_organization_roles — so you can issue a key that only reads roles, without handing it the ability to change them. That is usually what you want for a CI or reporting agent.

Grants only apply to API key authentication. Sessions authenticated via JWT or browser cookie carry full personal and organizational permissions, unchanged from before grants were introduced.

Tool policy​

When creating an API key you can optionally restrict which tools it may call. Three modes are available:

ModeBehaviour
all (default)The key can call every tool.
whitelistThe key can only call the tools listed in tools.
blacklistThe key can call every tool except those listed in tools.

tools and custom_tools take tool IDs, not MCP names. The two lists are separate: tools holds built-in platform tools, custom_tools your own custom tools. In the dashboard's key form you pick tools by name and it submits their IDs; over the REST API, GET /api/me/tools/scopable returns the built-in tools you can scope a key to (each with its id and name), and GET /api/me/custom-tools/scopable does the same for your custom tools.

Set the policy when creating a key via the REST API:

{
"name": "read-only agent",
"mcp_capabilities": ["tools"],
"grants": [{ "scope": { "type": "personal" }, "permissions": [] }],
"tool_policy": {
"mode": "whitelist",
"tools": ["<calculator-tool-id>", "<datetime-now-tool-id>"]
}
}
{
"name": "restricted agent",
"mcp_capabilities": ["tools"],
"grants": [{ "scope": { "type": "personal" }, "permissions": [] }],
"tool_policy": {
"mode": "blacklist",
"tools": ["<blocked-tool-id>"]
}
}

Omitting tool_policy (or setting {"mode": "all"}) applies no per-key tool filter — the key can call everything its tools capability and your account's tool selection allow.

The policy is enforced on every MCP call:

  • tools/list — the server returns only the tools the key is permitted to call.
  • tools/call — calling a tool excluded by the credential's policy returns tool_not_permitted; a genuinely absent or deprecated tool remains method_not_found.

The system tools setup, bootstrap, and agent_context do not appear in a tool policy and are not affected by any tool whitelist or blacklist.

Tools are executed exclusively over MCP — there is no REST tool-execution endpoint. On MCP the key still calls tools by name; IDs appear only in the policy itself.

Organization scope​

Personal API keys can operate in an organization's context. When org_scope is set to an org UUID, the key's usage, quota, and rate limits run against that org instead of your personal account:

{
"name": "org-agent",
"grants": [{ "scope": { "type": "personal" }, "permissions": [] }],
"org_scope": "<org-uuid>",
"mcp_capabilities": ["tools"]
}

You must be an active member of the referenced organization. Passing an org you are not a member of returns 422 Unprocessable Entity.

MCP capabilities​

Every API key must declare which MCP feature categories it may access via mcp_capabilities:

CapabilityWhat it gates
toolsTool listing (tools/list) and execution (tools/call)
resourcesResource listing and reading (resources/list, resources/read)
promptsPrompt listing (prompts/list) and retrieval (prompts/get)
agentsAgent-definition assignment, selection, and content

A key without a capability cannot use that governed category. An empty mcp_capabilities list still permits the authenticated system tools setup, bootstrap, and agent_context, but no other MCP tool, resource, prompt, or agent definition. Tools and Agents are independent. The system tools do not require tools or tool-policy access; Agent selection or content returned by bootstrap and agent_context still requires agents.

Where a capability can come from. In an organization, roles grant capabilities on two levels:

  • A capability from one of your organization roles applies organization-wide — it backs any of your credentials in that organization, whatever teams they are attached to.
  • A capability from one of your team roles counts for a credential only when that credential is active on the team the role is granted on — for a key, that means the team is among the key's teams: its bound teams, the teams you are currently in for a personal key that follows your memberships, or every team of the organization for an organization-owned key that follows the org's teams. A team-role capability on some other team does nothing for that credential.

Escalation guard: for keys operating in an organization context, the capabilities you request at key-creation time are bounded by your own effective capabilities in the new key's team context. An organization-role capability always qualifies; a team-role capability qualifies only if the granting team is among the teams the new key will be active on — holding it via a team the key is not attached to is not enough. If you request a capability outside that set, the key creation is rejected with 422 capability_escalation. The same check runs when an organization key's team bindings are edited later, so a key cannot be moved into a team context its capabilities were never authorized for. This prevents a key from having more power than its creator.

With unchanged team bindings, an update checks only newly added capabilities. Removing capabilities does not require you still to hold them. You can clear the capabilities of your personal key after leaving its organization; adding them back requires current membership and sufficient capabilities. Your account and the key's organization must still be active.

System role defaults:

System roleDefault capabilities
Ownertools, resources, prompts, agents
Admintools, resources, prompts, agents
Member(none — add via custom role)

The system team_admin role also grants no MCP capability; add capabilities through a custom organization or team role when needed.

Custom roles carry their own capability set, configured at role creation time.

Resource policy​

resource_policy restricts which personal resources the key may access by matching their paths. It applies in addition to the resources capability gate — both must pass.

Three modes:

ModeBehaviour
all (default)Key may access any resource in its ownership space.
whitelistKey may only access resources whose paths match a listed pattern.
blacklistKey may access resources except those whose paths match a listed pattern.
{
"name": "docs-agent",
"mcp_capabilities": ["resources"],
"resource_policy": {
"mode": "whitelist",
"patterns": ["documents/**", "public/**"]
}
}

Patterns are globs matched against the resource's path, rather than its full URI. For example, "documents/**" matches documents/report.pdf and documents/reports/annual.pdf, while private/secret.txt stays outside that whitelist. Use a resource's exact path to allow or block only that resource.

Prompt policy​

prompt_policy restricts which platform prompts and customer prompts in the caller's personal space a key may use. It applies in addition to the prompts capability gate — both must pass.

Customer prompt visibility is ownership-driven (there is no prompt allowlist). On the MCP surface, prompt names are namespaced by ownership space so they don't shadow each other: personal-in-org prompts appear as me/{name}, team-owned prompts as {team}/{name}, and org-catalog prompts unprefixed.

Platform prompts such as apistash/agent-doctor additionally need selection in the platform-prompt allowlist. The same policy mode filters them through the optional platform_prompts ID list; prompts keeps customer prompt IDs. A restricted policy must select at least one ID across the two lists. Use prompts: [] with platform_prompts: ["<platform-prompt-id>"] for a platform-only whitelist. See Platform prompts.

Same three modes as tool policy, operating on prompt IDs:

{
"name": "restricted-agent",
"mcp_capabilities": ["prompts"],
"prompt_policy": {
"mode": "blacklist",
"prompts": ["<internal-system-prompt-id>"]
}
}

prompts takes the IDs of your own personal-space prompts — GET /api/me/prompts/scopable returns them with their names. On MCP a prompt is still addressed by name (prompts/get); the stored policy references it by ID.

Agent policy​

agent_policy restricts which of your personal agent definitions a personal API key is eligible to use. It has the same three modes as the prompt policy and defaults to all:

ModeBehaviour
all (default)The key is eligible to use every personal agent.
whitelistThe key is eligible to use only the agents listed in agents.
blacklistThe key is eligible to use every agent except those in agents.
{
"name": "review-agent",
"mcp_capabilities": ["agents"],
"agent_policy": {
"mode": "whitelist",
"agents": ["<personal-agent-id>"]
}
}

For an org-scoped key, this personal policy does not apply. The caller can instead reach its own personal-in-organization agents, agents owned by its bound teams, and organization-owned agents that are organization-wide or admitted by at least one bound team's agent policy. Team policies use the same all, whitelist, and blacklist modes for the organization's agent catalog; team-owned agents are visible through ownership and are not filtered by that catalog policy.

Policies and dashboard assignment keep stable Agent IDs internally. Agent-facing MCP tools use the qualified names returned by list_agents or bootstrap (me/name, <team>/name, or a bare personal/catalogue name), and each content read resolves the current active version.

The Agent policy is a secondary filter after the independent agents capability. Assignment choices and set/replace require that capability as well as policy and ownership visibility; a stored assignment that becomes unavailable hides its identity but can still be cleared. Agent bootstrap and agent_context themselves do not require tools, but their Agent selection and content still pass this Agent policy. Organization keys have no per-key Agent policy: team policy and organization exposure control their Agent reach.

Policy request shape​

Tool, resource, prompt, and agent policies all use mode as a discriminator. In all mode, do not send any of that policy's known list fields. In whitelist and blacklist modes, send the required, non-null list: tools for a tool policy (custom_tools remains optional), patterns for a resource policy, prompts for a prompt policy, or agents for an agent policy. Every list that is supplied must be non-null. Known list fields that do not belong to the selected mode are rejected; genuinely unknown extension fields are tolerated and ignored.

A malformed policy shape—such as a missing required list, null, a wrong JSON type, or a value that is not a valid ID—returns 400 Bad Request. A well-shaped policy whose contents violate a domain rule—such as an empty or duplicate list, an unknown ID, or an ID outside the permitted owner space—returns 422 Unprocessable Entity.

Management and MCP access are separate​

An API key can carry controls for two different uses. They are independent, but they do not form one authorization chain:

ControlWhat it controls
grantsWhich REST and governed MCP management operations the key may perform
org_scopeWhich organization context, billing, and limits an MCP request uses
mcp_capabilitiesWhich native MCP surfaces and Agent-definition use the key can reach
Per-item policiesWhich specific items are reachable on a standalone personal key
Team bindingsWhich team-owned and team-admitted items an org-scoped key can reach

For management API requests, the relevant grant and permission checks apply. MCP capabilities and item policies do not grant management access.

For ordinary MCP use, org_scope selects the execution context, mcp_capabilities opens the corresponding MCP category, and either the standalone key's item policy or the org-scoped key's team bindings and team policies narrows what is reachable. Management grants do not make content usable.

Governed MCP management tools add a separate management check. list_*, get_*, and grep_resource use the visibility rules of the surface they read. For create, update, patch, and delete, a standalone personal target requires the corresponding personal permission, while a Team target requires both a Team binding and the corresponding Team permission. A me/ target in an organization requires an acting user with active membership; it does not use a separate personal permission. Every governed management tool still requires Tools and admission by the applicable tool policy.

JWT and cookie sessions are for interactive management access. They do not use API-key MCP capabilities or item policies and are rejected by the MCP endpoint.

Dashboard JWTs are not accepted on MCP endpoints​

The POST /mcp endpoint rejects requests authenticated with a dashboard JWT or session cookie. Only API keys or OAuth tokens are valid credentials on this endpoint.

If you use a dashboard session token on an MCP endpoint, you will receive 403 Forbidden. Use an API key as described above.

Using your API key​

Send the key in the X-API-Key header on every request:

X-API-Key: YOUR_API_KEY

The Authorization: Bearer header is reserved for OAuth access tokens (see OAuth below) — an API key sent that way is rejected.

For MCP clients, set this in your client configuration:

{
"mcpServers": {
"apistash": {
"type": "http",
"url": "https://api.apistash.io/mcp",
"headers": {
"X-API-Key": "YOUR_API_KEY"
}
}
}
}

Listing your keys​

GET /api/keys returns all active keys for your account. GET /api/keys/{id} returns a single key by UUID. Each entry includes a last_used_at timestamp (ISO 8601, null if the key has never been used) so you can identify unused keys for cleanup.

OAuth​

Alongside API keys, apistash supports OAuth 2.0 for agent tools — CLIs and coding agents — that authorize to the MCP server on a user's or organization's behalf. OAuth access tokens are accepted on the MCP endpoint as Authorization: Bearer <token>.

Flows​

apistash implements three OAuth grant types:

GrantUse
Authorization Code + PKCEInteractive apps acting for a user. PKCE (S256) is required for public clients and available to confidential clients.
Refresh TokenObtain a fresh access token without re-prompting. Refresh tokens rotate on each use.
Client CredentialsMachine-to-machine access for an organization — a userless client authenticating as itself. Public clients cannot use this grant.

Client types​

  • Confidential clients have a client secret and authenticate with client_secret_post.
  • Public clients have no secret, use PKCE only, and are limited to the authorization-code grant.

Registering a client​

OAuth clients are registered in the dashboard by a signed-in user. Dynamic Client Registration (RFC 7591) is not supported — clients cannot self-register.

Choose the registration lane that matches the connection:

  • Authorization Code + PKCE creates a delegated connection only after a user completes consent. Reauthorizing the same client creates a separate connection; each connection keeps its own Agent assignment.
  • Client Credentials creates an organization machine-to-machine connection for the registered client. It has no user or personal content lane. A hybrid organization client keeps this M2M connection separate from every delegated connection it creates.

The connection detail page in the dashboard shows its granted scope envelope, team binding, Agent assignment, and the optional task-start setup. A client registration's allowed scopes are only a ceiling: an issued delegated token still carries the scopes the user actually authorized.

Token lifetime and ongoing sessions​

Every MCP HTTP request needs a currently valid credential, including requests on an existing SSE session. An OAuth access token is rejected with HTTP 401 at its signed expiry (exp), without a grace period; send the next request with a fresh access token. Keeping a session open does not preserve old grants, team bindings or connection permissions.

A team write requires both an active binding to that team and the matching action permission; granting a permission does not add a team binding. See Management permissions.

Management scopes​

An OAuth management scope is one exact permission written in OAuth's string format. Mutation actions are independent: selecting create does not also select update or delete. A mutation may imply the matching view permission at authorization time, but it never implies another content family.

Personal OAuth clients can request these 21 management scopes:

AreaExact scopes
Resourcesread:resources, create:resources, update:resources, delete:resources
Toolsread:tools, create:tools, update:tools, delete:tools
Promptsread:prompts, create:prompts, update:prompts, delete:prompts
Agentsread:agents, create:agents, update:agents, delete:agents
Accountread:usage, read:dashboard, write:settings, read:mcp_settings, write:mcp_settings

Organization OAuth clients can request these 47 organization-management scopes:

AreaExact scopes
Organizationread:organization, write:organization, read:organization_mcp_settings, write:organization_mcp_settings
Membersread:organization_members, invite:organization_members, remove:organization_members, update:organization_members
Organization rolesread:organization_roles, create:organization_roles, update:organization_roles, delete:organization_roles
Team-role catalogread:organization_team_roles, create:organization_team_roles, update:organization_team_roles, delete:organization_team_roles
Teamsread:organization_teams, write:organization_teams, write:all_organization_teams
API keysread:organization_keys, create:organization_keys, update:organization_keys, revoke:organization_keys
OAuth clientsread:organization_oauth_clients, create:organization_oauth_clients, update:organization_oauth_clients, delete:organization_oauth_clients
Resourcesread:organization_resources, create:organization_resources, update:organization_resources, delete:organization_resources
Toolsread:organization_tools, create:organization_tools, update:organization_tools, delete:organization_tools
Promptsread:organization_prompts, create:organization_prompts, update:organization_prompts, delete:organization_prompts
Agentsread:organization_agents, create:organization_agents, update:organization_agents, delete:organization_agents
Reporting and SSOread:organization_usage, read:organization_billing, read:organization_sso, write:organization_sso

Organization OAuth clients can also request these 40 Team-management scopes:

AreaExact scopes
Membersread:team_members, create:team_members, update:team_members, delete:team_members
Resourcesread:team_resources, create:team_resources, update:team_resources, delete:team_resources, promote:team_resources
Toolsread:team_tools, create:team_tools, update:team_tools, delete:team_tools, promote:team_tools
Promptsread:team_prompts, create:team_prompts, update:team_prompts, delete:team_prompts, promote:team_prompts
Agentsread:team_agents, create:team_agents, update:team_agents, delete:team_agents
Usageread:team_usage
Rolesread:team_roles, create:team_roles, update:team_roles, delete:team_roles
Team settingsupdate:team_general, delete:team_general
All exposure policiesread:team_policies, write:team_policies
Resource policiesread:team_resource_policies, write:team_resource_policies
Tool policiesread:team_tool_policies, write:team_tool_policies
Prompt policiesread:team_prompt_policies, write:team_prompt_policies
Agent policiesread:team_agent_policies, write:team_agent_policies

promote exists only for Team Resources, Tools, and Prompts; Agents have no promote scope. General Team policy scopes expand to all four policy families. A family policy scope affects only that family and never grants content-management authority.

write:all_organization_teams grants every Team permission, but only inside the OAuth connection's independent Team binding. For Authorization Code connections, the authorizing user must also still have live access to each bound Team; for Client Credentials, the registered Team binding is the boundary.

Scopes and exposure policies do different jobs. A management scope permits the corresponding dashboard/API operation; a Team exposure policy is a separate rule deciding which concrete entities a credential can use through MCP. The token must also have the matching mcp_* capability, and organization credentials can act only on their bound Teams. None of those controls substitutes for another.

OAuth uses mcp_tools, mcp_resources, mcp_prompts, and mcp_agents for the same four independent MCP-use categories. Management scopes such as read:agents and update:agents control Agent inventory; they do not grant mcp_agents. Only these exact mcp_* names grant a category; unknown or wildcard scope values grant none. For an organization registration containing Client Credentials—including a hybrid registration—the requested management and mcp_* scopes cannot exceed the registrar's current permission and capability authority in the selected team context. An Authorization Code-only registration is bounded by the authorizing user's live authority whenever its token is used instead, so a later role change takes effect on the next request. A userless Client Credentials token has no live user-role intersection; its authority is therefore capped when the client and its team bindings are registered.

Endpoints and discovery​

EndpointPurpose
GET https://api.apistash.io/oauth/authorizeAuthorization endpoint
POST https://api.apistash.io/oauth/tokenToken endpoint
https://api.apistash.io/.well-known/oauth-authorization-serverAuthorization-server metadata (RFC 8414)
https://api.apistash.io/.well-known/oauth-protected-resource/mcpProtected-resource metadata for the MCP endpoint (RFC 9728)

MCP clients that support OAuth can discover these endpoints automatically: the MCP endpoint advertises its protected-resource metadata, which points to the authorization server. Discovery does not register the client. Supply the client ID — and the secret for a confidential client — from a dashboard registration. Claude Desktop's custom connector is one such confidential-client flow; see MCP Clients.

info

What an OAuth token can reach on MCP is governed by its scopes and — for organization clients — its team bindings, then filtered by the access model.

Persistent task-start setup does not require mcp_tools: setup, bootstrap, and agent_context are always available to an authenticated token. The token must carry mcp_agents for Agent selection or content, and the applicable Agent visibility policies still apply.

Rate limits and usage quotas​

Every tool call is subject to a per-minute, per-hour, and per-day rate limit, and each account has a monthly tool-invocation quota. Both scale with your plan.

Fetching prompts and reading resources has its own, more generous rate limit and does not consume the monthly tool quota.

See Plans & Limits for the current tiers and the full list of enforced limits.

Revoking an API key​

To revoke a key from the dashboard:

  1. Navigate to API Keys in the sidebar.
  2. Click Revoke next to the key you want to remove.
  3. Confirm the action in the dialog.

A revoked key is rejected on the next request, without a grace period. A request that was already authenticated and authorized may finish. The same request boundary applies when a user is deactivated or permissions are removed. The request must still satisfy the operation's consistency rules. For example, an earlier authorization does not allow a role to be changed in an organization that has since been deleted, or a credential to receive rights beyond the current delegation limits.

Security recommendations​

  • Do not commit API keys to source control. Use environment variables or secret managers.
  • Use one key per agent or application so you can revoke access selectively.
  • Rotate keys regularly, especially if they may have been exposed.