Skip to navigation

Scoping API keys to capabilities

Use scopes to limit a Surface API key to selected Capabilities on a Surface. A scoped key invokes only the Capabilities that you specify.

Why scope API keys

Scoping applies least privilege to each integration:

  • Each integration gets only the Capabilities that it needs.
  • A compromised key can call only the Capabilities in its scope.

Common scope patterns:

  • Customer-specific keys. Create one key per customer. For example, give Customer A the FAQ and Order lookup Capabilities, and give Customer B the FAQ Capability only.
  • Separate integrations. Create one key per integration. Scope a mobile app key to the Capabilities that the app calls. Scope a public key to selected Capabilities and set key-level rate limits.
  • Third-party developers. Give a partner selected Capabilities without exposing the entire Product.

Name each key after its integration and scope. For example, use Example Organization - FAQ only instead of API Key 3. Descriptive names help you identify the intended scope when you rotate or delete keys.

Create a scoped key

The dashboard’s Generate API Key dialog sets a key’s name and key type. It does not set scopes. To set scopes, use the API.

Chat Surfaces do not accept Surface API keys. They use client tokens, so the API returns 400 if you create a key on a chat Surface.

  1. Get the Surface’s scope targets:

    Get the Surface
    curl https://api.runtype.com/v1/products/YOUR_PRODUCT_ID/surfaces/YOUR_SURFACE_ID \
    -H "Authorization: Bearer YOUR_API_KEY"

    Each entry in the scopeTargets array has an id and a name. Copy the id of each Capability that the key needs.

  2. Create the key with those IDs in scopes:

    Create a scoped API key
    curl -X POST \
    https://api.runtype.com/v1/products/YOUR_PRODUCT_ID/surfaces/YOUR_SURFACE_ID/keys \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "name": "Example Organization - FAQ only",
    "scopes": ["YOUR_SCOPE_TARGET_ID"],
    "environment": "production",
    "rateLimitPerMinute": 60,
    "expiresAt": "2027-01-01T00:00:00Z"
    }'

    Replace the following:

    • YOUR_PRODUCT_ID: the ID of the Product that contains the Surface.
    • YOUR_SURFACE_ID: the ID of the Surface that accepts the key.
    • YOUR_API_KEY: an account API key with permission to create Surface API keys. See Managing API keys.
    • YOUR_SCOPE_TARGET_ID: an id from the Surface’s scopeTargets array.
  3. Store the key value from the response. A production key’s full value appears only in this response.

  4. Confirm the key’s scope. The response’s scopeTargetIds array lists the targets that the key can call. Compare it with the id values in the Surface’s scopeTargets array.

Request body fields

The request body accepts the following fields:

FieldTypeDescription
namestringOptional. Up to 255 characters.
scopesarray of stringsOptional. Up to 100 entries. Omit it or send an empty array for an unscoped key.
environmentstringtest (default) or production. A test key is limited to 50 executions per UTC day, and you can reveal it again in the dashboard. A production key has no daily cap, and its full value appears only once.
rateLimitPerMinuteintegerOptional. Maximum requests per minute for this key.
rateLimitPerDayintegerOptional. Maximum requests per day for this key.
expiresAtstringOptional. A UTC date and time in ISO 8601 format, such as 2027-01-01T00:00:00Z. The key stops working after this time.

Values accepted in scopes

Use scope target IDs. Each ID matches exactly one target, so the key gets exactly the access that you intend. The API also accepts these values and converts each one to a scope target ID when it creates the key:

  • A Capability ID.
  • A Capability name from the name field in scopeTargets. The name must match exactly one target.

A scopes array that contains "*" is unrestricted.

You can also create a scoped key through the Runtype MCP server. The create_surface_key tool accepts the same scopes values, and the get_surface tool returns scopeTargets. The tool creates test keys only.

Fix scope errors

If a scope value is not valid, the API returns 400 with one of these codes:

  • INVALID_SURFACE_KEY_SCOPE: the value is blank, or it does not match a Capability on this Surface. Check the value against the Surface’s scopeTargets array.
  • AMBIGUOUS_SURFACE_KEY_SCOPE: the name matches more than one target. Use the scope target ID instead.

How scopes apply on each Surface type

When a scoped key calls a Capability outside its scope, the response depends on the Surface type. Handle these errors by status code or error code, not by message text.

Surface typeBehavior for a scoped key
API, webhookAn out-of-scope call returns 403 Forbidden.
MCPThe tool list shows only the tools in the key’s scope. A call to another tool returns a tool error, Access denied to tool. The name of each scope target is the MCP tool name.
A2AA request for a skill outside the key’s scope returns a Skill not found JSON-RPC error that lists only the skills in scope. In Managed mode, the orchestrator skill is not available to a scoped key. The key sees only tasks for skills in its scope.
AG-UIA request for an out-of-scope Capability returns 403. If no Capability on the Surface is in the key’s scope, the request returns 404.

The API 403 response has this form:

Out-of-scope response on an API Surface
{
"error": "Forbidden",
"message": "API key does not have access to capability 'FAQ'"
}

On API, MCP, and A2A Surfaces, each skill of an external agent is a separate scope target. To grant one skill, use that skill’s scope target ID. To grant every skill of the agent, use its Capability ID.

When the Surface changes

A scoped key keeps the scope targets it had when you created it:

  • A Capability that you add to the Surface later is not available to existing scoped keys. Unscoped keys get it automatically.
  • If you remove a Capability from the Surface and add it again, it gets a new scope target ID. Scoped keys lose access to it without an error at the time of the change.

After either change, create a new scoped key and make a test call to confirm access.

Change a key’s scope

You cannot update a key’s scopes after you create it. Neither the API nor the dashboard has a control for it. Delete the key, and then create another key with the scopes that you need.

Use unscoped keys

Use unscoped keys only for integrations that need every Capability exposed through the Surface. A compromised unscoped key can invoke every Capability on that Surface. Rotate unscoped keys on a regular schedule.

Next steps

Continue with these guides: