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

# Authentication

> How to obtain, use and rotate a VENTRY API key.

Every call carries an API key in the `Authorization` header:

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.ventry.es/v1/event \
    -H "Authorization: Bearer vk_live_8Kq2ZxR7pN3mW9tYbC4vD6fH1jL5sA0e"
  ```

  ```js Node.js theme={null}
  await fetch("https://api.ventry.es/v1/event", {
    headers: { Authorization: `Bearer ${process.env.VENTRY_KEY}` },
  });
  ```

  ```python Python theme={null}
  requests.get(
      "https://api.ventry.es/v1/event",
      headers={"Authorization": f"Bearer {os.environ['VENTRY_KEY']}"},
      timeout=10,
  )
  ```

  ```php PHP theme={null}
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      "Authorization: Bearer " . getenv("VENTRY_KEY"),
  ]);
  ```
</CodeGroup>

## Getting a key

The event organiser issues it from the VENTRY dashboard, under **API /
Integrations**. When creating it they choose:

* **Scopes**: what you can read and write.
* **Restrictions**: whether the key is limited to certain ticket types or days.
* **Rate limit** per minute.
* **Allowed IPs**, if they want to restrict where it can be used from.

<Warning>
  The key is **shown only once**, at the moment it is created. VENTRY only stores a
  hash, so neither the organiser nor the VENTRY team can recover it afterwards. If
  it is lost, it must be revoked and a new one issued.
</Warning>

### Keys with write scopes

Any key requesting a write scope is created **disabled** and responds:

```json theme={null}
{
  "error": {
    "type": "permission_error",
    "code": "key_pending_activation",
    "message": "Esta API key solicita permisos de escritura y todavía no ha sido activada por un administrador del evento.",
    "request_id": "req_9f2c1a4e7b304d51"
  }
}
```

This is deliberate: writing to a live event — creating tickets, accrediting
attendees, moving balance — requires a person to authorise it explicitly. Ask the
organiser to activate it from the dashboard.

## Scopes

| Scope            | What it allows                              | Activation |
| ---------------- | ------------------------------------------- | ---------- |
| `event.read`     | Days, zones, ticket types and event details | Automatic  |
| `tickets.read`   | Read tickets and their state                | Automatic  |
| `tickets.write`  | Create and cancel tickets                   | **Manual** |
| `checkins.read`  | Door scan log                               | Automatic  |
| `checkins.write` | Accredit tickets and validate zones         | **Manual** |
| `cashless.read`  | Balances, purchases and top-ups             | Automatic  |
| `cashless.write` | Top up wristband balance                    | **Manual** |
| `reports.read`   | Aggregate sales summary                     | Automatic  |
| `*`              | Everything, present and future              | **Manual** |

Calling an endpoint without its scope returns `403 insufficient_scope`.

## Key format

```
vk_live_8Kq2ZxR7pN3mW9tYbC4vD6fH1jL5sA0e
└┬┘ └┬─┘ └──────────────┬──────────────┘
 │   │                  └─ secret (32 random bytes)
 │   └──────────────────── environment: live or test
 └──────────────────────── VENTRY prefix
```

The `vk_live_` prefix is not decorative: it lets secret scanners on GitHub and
similar services detect a key published by mistake.

## Best practices

<AccordionGroup>
  <Accordion title="Treat it as a server-side secret">
    A VENTRY API key can read attendees' personal data and, depending on its
    scopes, write to the event. **Never** embed it in a mobile app, in browser
    JavaScript or in a repository. If your door application needs to accredit,
    have it talk to your backend and let the backend call VENTRY.
  </Accordion>

  <Accordion title="Ask only for the scopes you use">
    If you only push tickets, ask for `event.read` and `tickets.write`. The fewer
    the scopes, the less damage a compromised key does.
  </Accordion>

  <Accordion title="Use restrictions">
    If you only handle one ticket type, ask the organiser to restrict the key to
    that type. It then becomes impossible to touch tickets that are not yours by
    mistake.
  </Accordion>

  <Accordion title="Rotate without downtime">
    There is no automatic rotation. To rotate without interrupting service: ask
    for a new key, deploy it, verify it works with `GET /ping`, and only then ask
    for the old one to be revoked. A revoked key stops working immediately.
  </Accordion>

  <Accordion title="Log the request_id">
    Every response carries the `X-Request-Id` header. Store it in your logs: it is
    what lets VENTRY support find your exact request.
  </Accordion>
</AccordionGroup>

## Authentication errors

| Code                     | Situation                                                       |
| ------------------------ | --------------------------------------------------------------- |
| `missing_api_key`        | The `Authorization` header is missing or is not `Bearer vk_...` |
| `invalid_api_key`        | The key does not exist                                          |
| `revoked_api_key`        | The key has been revoked                                        |
| `expired_api_key`        | The key had an expiry date and it has passed                    |
| `key_pending_activation` | It has write scopes that are not activated                      |
| `ip_not_allowed`         | Your IP is not on the key's allowed list                        |
| `insufficient_scope`     | The key lacks the scope that endpoint requires                  |
