# Passes

> The user-delegated guest-pass API: list, create, get, and revoke passes with an OAuth access token. Request bodies, response shapes, the passes:read / passes:write scopes, and 402 on quota.

The passes API manages the **signed-in user's** guest passes. It is user-delegated: authenticate with an [OAuth access token](/docs/oauth/token/) and the `passes:*` [scopes](/docs/oauth/scopes/). All four endpoints are owner-scoped — a user only ever sees and touches passes they created.

**Base:** `https://entrybit.net` · **Auth:** `Authorization: Bearer <access_token>` · see [Conventions](/docs/api-reference/conventions/).

> The user must also still hold the organization's self-invite role. If that role is revoked, calls return `403 insufficient_scope` even with a valid token. To act across the whole organization instead, use the [Organization API](/docs/api-reference/organization/).

## The Pass object

| Field | Type | Notes |
|---|---|---|
| `public_id` | string | Public identifier, e.g. `gst_9f1c2ab34cd56ef7`. |
| `first_name` | string | |
| `last_name` | string | |
| `email` | string \| null | |
| `phone` | string \| null | |
| `status` | string | `expected`, `registered`, `checked_in`, `checked_out`, or `cancelled`. |
| `created_at` | string | |
| `arrival_date` | string | `YYYY-MM-DD`. |
| `arrival_time` | string \| null | `HH:MM`. |
| `facility_id` | integer \| null | |
| `facility_name` | string \| null | |
| `created_by` | integer | Inviting user's id — present on organization listings only. |

Status flows `expected → registered → checked_in → checked_out`, or `cancelled` if revoked.

## List passes

```http
GET /api/v1/passes?limit&cursor&search
```

**Scope:** `passes:read`. Keyset-paginated, newest first. The first page carries the monthly-allowance `usage` block.

Parameters: `limit` (1–100, default 30), `cursor`, `search` — see [pagination](/docs/api-reference/conventions/).

```bash
curl "https://entrybit.net/api/v1/passes?limit=30" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

```json
{
  "success": true,
  "items": [ { "public_id": "gst_9f1c…", "first_name": "Dana", "status": "expected", "arrival_date": "2026-07-12", "facility_name": "North Gate" } ],
  "total": 12,
  "next_cursor": "eyJpZCI6…",
  "has_more": true,
  "usage": { "used": 8, "limit": 50, "remaining": 42 }
}
```

## Create pass(es)

```http
POST /api/v1/passes
Content-Type: application/json
```

**Scope:** `passes:write`. Creates 1–10 passes with distinct QR credentials scoped to a facility's doors, and delivers **one** email/SMS carrying a single pass link. The batch counts against the user's monthly invite allowance **all-or-nothing**.

### Request body

| Field | Required | Notes |
|---|---|---|
| `first_name` | ✓ | Max 100 chars. |
| `last_name` |  | Max 100 chars. |
| `email` | ✓* | Delivery channel — the pass email. |
| `phone` | ✓* | Delivery channel — the pass SMS. 7–15 digits. |
| `arrival_date` | ✓ | `YYYY-MM-DD` (or `YYYY-MM-DDTHH:MM`, whose time seeds `arrival_time`). |
| `arrival_time` |  | `HH:MM`; access starts here. Default: start of the arrival day. |
| `access_until` |  | Pass expiry. Default: end of the arrival day. Must not precede `arrival_date`. |
| `facility_id` | ✓ | Which facility's entry doors the QR opens. Must be active. |
| `quantity` |  | 1–10. Default `1`. One email/SMS with a single batch link. |
| `country` |  | Optional guest metadata. |
| `vehicle_number` |  | Optional guest metadata. |
| `notes` |  | Optional guest metadata. |
| `invited_by` |  | Display name shown to the guest. |

*At least one of `email` or `phone` is required so the guest can receive the pass.

```bash
curl -X POST https://entrybit.net/api/v1/passes \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Dana",
    "last_name": "Cohen",
    "email": "dana@example.com",
    "arrival_date": "2026-07-12",
    "arrival_time": "14:30",
    "facility_id": 42,
    "quantity": 1
  }'
```

### Response — `201`

| Field | Type | Notes |
|---|---|---|
| `success` | boolean | |
| `id` | integer | First guest's internal id (backward compatibility). |
| `public_id` | string | First pass's public id. |
| `qr_value` | string \| null | First pass's QR value (also in the delivered email). |
| `qr_values` | string[] | Every issued QR value, in creation order. |
| `quantity` | integer | |
| `qr_sent` | boolean | Whether the pass email was delivered. |
| `sms_sent` | boolean | Whether the pass SMS was delivered. |
| `pass_link` | string \| null | The single shareable pass page (shows all QRs in the batch). |
| `message` | string | |

```json
{
  "success": true,
  "id": 90210,
  "public_id": "gst_9f1c2ab34cd56ef7",
  "qr_values": ["EB-…"],
  "quantity": 1,
  "qr_sent": true,
  "sms_sent": false,
  "pass_link": "https://entrybit.net/p/…"
}
```

### Errors

| Status | Meaning |
|---|---|
| `400` | Validation failed — the `message`/`error` says which field. |
| `402` | Monthly invite allowance can't fit the batch (all-or-nothing). Body carries `limit`/`current`/`requested`. |
| `404` | Facility not found. |
| `409` | Facility deactivated or suspended. |

## Get one pass

```http
GET /api/v1/passes/{public_id}
```

**Scope:** `passes:read`. Owner-scoped — returns `404` for a pass the user didn't create.

```json
{ "success": true, "pass": { "public_id": "gst_9f1c…", "status": "registered", "arrival_date": "2026-07-12", "facility_name": "North Gate" } }
```

## Revoke a pass

```http
DELETE /api/v1/passes/{public_id}
```

**Scope:** `passes:write`. Cancels the pass **and** deactivates its QR credential on the door hardware. Only works while the pass is still active (`expected`/`registered`).

```json
{ "success": true, "public_id": "gst_9f1c2ab34cd56ef7", "revoked": true }
```

Returns `409` when the pass is no longer active, `404` when it isn't the user's.

## Scope summary

| Endpoint | Scope |
|---|---|
| `GET /api/v1/passes` | `passes:read` |
| `POST /api/v1/passes` | `passes:write` |
| `GET /api/v1/passes/{public_id}` | `passes:read` |
| `DELETE /api/v1/passes/{public_id}` | `passes:write` |

Full error semantics: [Errors](/docs/api-reference/errors/).