# Authenticating requests

> Send an organization API key as Authorization: Bearer eb_sk_… (or X-API-Key), the 401/403 challenges you'll get, and the per-IP rate limit of 300 requests/minute.

Every request to the [Organization API](/docs/api-reference/organization/) authenticates with an [API key](/docs/api-keys/overview/). Send the key on each call — there is no session, no cookie, and no CSRF token; the key itself is the credential.

## Sending the key

Use the standard `Authorization: Bearer` header, with the key supplied by your environment or secret manager (the examples assume `ENTRYBIT_API_KEY` is set — never paste the literal key into a command line or source file):

```bash
curl https://entrybit.net/api/v1/org/facilities \
  -H "Authorization: Bearer $ENTRYBIT_API_KEY"
```

Or the `X-API-Key` header, if that's easier for your HTTP client:

```bash
curl https://entrybit.net/api/v1/org/facilities \
  -H "X-API-Key: $ENTRYBIT_API_KEY"
```

Both are equivalent. Always over TLS — **never put a key in a URL**, a query string, or a log line.

The same call from server-side code:

```ts
// Node.js 18+
const res = await fetch('https://entrybit.net/api/v1/org/facilities', {
  headers: { Authorization: `Bearer ${process.env.ENTRYBIT_API_KEY}` },
});
if (!res.ok) throw new Error(`EntryBit ${res.status}`); // 401/403/429 — see below
const { facilities } = await res.json();
```

```python
# Python
import os
import requests

res = requests.get(
    "https://entrybit.net/api/v1/org/facilities",
    headers={"Authorization": f"Bearer {os.environ['ENTRYBIT_API_KEY']}"},
    timeout=10,  # requests has no default timeout — always set one
)
res.raise_for_status()
facilities = res.json()["facilities"]
```

## Challenges

| Status | `error` | Meaning | What to do |
|---|---|---|---|
| `401` | `invalid_token` | Missing, unknown, revoked, expired, or IP-restricted key (indistinguishable by design). | Check the key and its expiry / IP allowlist; mint a new one if needed. |
| `403` | `insufficient_scope` | The key lacks a required [`org:*` scope](/docs/oauth/scopes/). | Mint a key with the needed scope — scopes are fixed at creation. |
| `429` | `temporarily_unavailable` | Rate limited. | Back off and retry. |

`401` and `403` carry a `WWW-Authenticate: Bearer` challenge; on `403` it names the missing scope:

```http
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope", scope="org:passes:write"
```

A `401` is **deliberately ambiguous** — an unknown key, a revoked key, an expired key, and a key used from a disallowed IP all look identical, so an attacker can't probe which of those is true. See [Errors](/docs/api-reference/errors/) for the full catalog.

## Rate limits

Organization API requests are rate-limited **per source IP** at **300 requests per minute**. Exceeding it returns `429` — back off (ideally with exponential backoff and jitter) and retry. This is a separate bucket from the per-user `120/min` limit on the [user-delegated API](/docs/api-reference/passes/).

## Scope enforcement

The key's scopes gate what it can do:

- `org:passes:read` → [`GET /api/v1/org/passes`](/docs/api-reference/organization/)
- `org:passes:write` → [`POST` / `DELETE /api/v1/org/passes`](/docs/api-reference/organization/)
- `org:facilities:read` → [`GET /api/v1/org/facilities`](/docs/api-reference/organization/)

A key acts only within its own organization — it can never read or modify another tenant's data, regardless of scope. Next: the [Organization API](/docs/api-reference/organization/) reference.