Scoping API keys to capabilities

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

Why scope API keys

Scoping an API key helps you apply least privilege and separate usage:

  • Least privilege: Give each integration only the Capabilities that it needs.
  • Reduced attack surface: Limit what a compromised key can call.
  • Usage tracking: Track calls by API key and Capability.
  • Billing isolation: Track costs by integration or customer.

Create a scoped key

The dashboard’s Generate API Key dialog accepts a name and key type. It does not accept scopes.

To set scopes, send an authenticated POST request to the surface key endpoint. Use an exposed Capability name or a scope target ID in scopes. This example uses an exposed Capability name:

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": "Mobile app - FAQ only",
> "scopes": ["YOUR_CAPABILITY_NAME"]
>}'

Replace YOUR_CAPABILITY_NAME with a unique exposed Capability name on the Surface. You can use a scope target ID instead. Replace the other placeholders in the request with these values:

  • 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: a management API key with permission to create surface keys.

The API accepts scope target IDs from the Surface’s scopeTargets response. It also accepts linked surface-item IDs, Capability IDs, and unique exposed names. Prefer scope target IDs because they provide stable authorization targets.

The create response includes the full key. Store it securely because later key-list responses do not include the full value. A key without scopes or with an empty scopes array is unscoped. It can invoke every Capability on the Product. The API also treats scopes set to * as unrestricted.

Handle access denied errors

When a scoped key calls a Capability outside its scope, the API returns 403 Forbidden with an error response similar to this:

Insufficient capability scope response
1{
2 "error": "Forbidden",
3 "message": "API key does not have access to capability 'FAQ'"
4}

Change a key’s scope

After you create a key, you cannot update its scopes. The API has no update endpoint or dashboard control for surface key scopes. Delete the key. Then create another key with the desired scopes.

Use scoped keys

Create customer-specific keys

Create one key per customer and restrict each key to the Capabilities that the customer needs. For example:

  • Customer A: the FAQ and Order lookup Capabilities.
  • Customer B: the FAQ Capability only.

Separate integrations

Create separate keys for each integration. Use these scope patterns:

  • Mobile app key: all Capabilities.
  • Public API key: selected Capabilities with key-level rate limits.
  • Internal tools key: administrative and diagnostic Capabilities.

Give third-party developers limited access

Give a partner access to 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.

Use unscoped keys

Use unscoped keys only for integrations that need access to every Capability on the Product. A compromised unscoped key can invoke every Capability. Rotate unscoped keys on a regular schedule.

Next steps

Continue with these guides: