> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gomry.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Scopes

> Per-resource read/write permissions on API keys

# Scopes

Each Gomry API key carries a **scope map** that controls which resources it can access and at what level. Scopes are configured per-resource and per-action, letting you create keys with exactly the access an integration needs.

## Available Actions

| Action  | Description                                                            |
| ------- | ---------------------------------------------------------------------- |
| `none`  | No access. Requests to this resource return `403 insufficient_scope`.  |
| `read`  | Read-only access (`GET` requests).                                     |
| `write` | Full access (`GET`, `POST`, `PATCH`, `PUT`, `DELETE`). Implies `read`. |

## Available Resources

`contacts`, `events`, `experiences`, `attendees`, `ticket_classes`, and `applications` support `write`. All other resources are read-only — selecting `read` is sufficient to use every endpoint listed.

| Resource         | Endpoints                                                                                                                                                       | Supported actions       |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
| `events`         | `GET/POST /v1/events`, `GET/PATCH /v1/events/:id`                                                                                                               | `none`, `read`, `write` |
| `experiences`    | `GET/POST /v1/experiences`, `GET/PATCH /v1/experiences/:id`, `GET/POST /v1/experiences/:id/recurrences`, `GET/PATCH/DELETE /v1/experiences/:id/recurrences/:id` | `none`, `read`, `write` |
| `attendees`      | `GET/POST /v1/events/:id/attendees`, `GET/PATCH/DELETE /v1/events/:id/attendees/:id`                                                                            | `none`, `read`, `write` |
| `ticket_classes` | `GET/POST /v1/events/:id/ticket-classes`, `GET/PATCH/DELETE /v1/events/:id/ticket-classes/:id`                                                                  | `none`, `read`, `write` |
| `contacts`       | `GET/POST /v1/contacts`, `GET/PATCH/DELETE /v1/contacts/:id`                                                                                                    | `none`, `read`, `write` |
| `applications`   | `GET /v1/applications`, `GET/PATCH /v1/applications/:id`, `GET /v1/contacts/:id/applications`                                                                   | `none`, `read`, `write` |
| `forms`          | `GET /v1/forms`, `GET /v1/forms/:id`                                                                                                                            | `none`, `read`          |
| `lists`          | `GET /v1/lists`, `GET /v1/lists/:id`, `GET /v1/lists/:id/contacts`                                                                                              | `none`, `read`          |
| `custom_fields`  | `GET /v1/custom-fields`, `GET /v1/custom-fields/:id`                                                                                                            | `none`, `read`          |
| `payments`       | `GET /v1/payments`, `GET /v1/payments/:id`                                                                                                                      | `none`, `read`          |

Some endpoints depend on multiple resources. For example, `GET /v1/lists/:id/contacts` requires both `lists:read` and `contacts:read` because the response exposes contact records.

Application responses include a resolved `applicant` object (email, first/last name) — this is part of `applications:read`, there is no separate scope for it. Each field is `null` when no linked record (application, user, contact, or a name/email-tagged answer) supplies it. See [List Applications](/api-reference/applications/list-applications).

API keys created before `events:write` shipped are capped at `events:read` for backwards compatibility — to enable event creation on a legacy key, edit it in the dashboard and select `write`. The same cap applies to `experiences:write`, `attendees:write`, `ticket_classes:write`, and `applications:write`: legacy keys must be opted in explicitly so a key sitting in someone's `.env` cannot silently start mutating tickets, ticket classes, or applications when the write endpoints ship.

## Configuring Scopes on a Key

When creating a key in **Organization Settings → API Keys**, the scope picker lets you set each resource to `none`, `read`, or `write`. Scopes can be updated later from the same screen without rotating the key.

API keys created **before scopes existed** are treated as having full access (`write` on every resource) for backwards compatibility. We recommend tightening these legacy keys to the minimum scopes their integration actually needs.

## Insufficient Scope Errors

If a request hits an endpoint the key isn't authorized for, the API returns:

```json theme={null}
{
  "error": "insufficient_scope",
  "message": "This API key does not have 'read' access to 'payments'.",
  "required_scope": "payments:read"
}
```

Status: `403 Forbidden`. The fix is to grant the missing scope from the dashboard or use a different key.

## Recommendations

* **Principle of least privilege.** Start with `none` everywhere, then enable only what your integration needs.
* **Use separate keys per integration.** A CRM sync needs `contacts:read`, `lists:read`, `custom_fields:read`. A finance reconciliation job needs `payments:read`. Don't share keys.
* **Rotate periodically.** Create a new key with the desired scopes, switch over, then revoke the old one.
