# API tokens

# API tokens

API tokens (Laravel Sanctum) authenticate requests without a session cookie. Jetstream's `Features::api()` is enabled, so any authenticated user can manage tokens at **Profile → API Tokens** (`/user/api-tokens`).

Demo personas cannot create API tokens, and persona-owned tokens are refused at authentication — personas are impersonation-only (see [demo environment](demo-environment.md)).

## Creating a token

1. Enter a name and select the abilities the token should have.
2. The plaintext token is shown **once** — copy it immediately.

## Abilities

The abilities offered are `create`, `read`, `update`, `delete`, `companies.sync`, `areas.sync`, `events.sync`, `widget.manage`, `tables.sync`, `venue_items.sync` (default: `read`).

Ability is necessary but not sufficient: `HasCompanies::hasCompanyPermission` also requires the token's **user** to hold the matching company permission. E.g. `companies.sync` works only when the token has the ability *and* the user holds the `companies.sync` permission on that company (owner/admin by default).

## Using a token

```
Authorization: Bearer <token>
```

Test it against `GET /api/user`:

```bash
curl -H "Authorization: Bearer <token>" https://your-domain/api/user
```

## Sync API

Tokens drive the inbound sync API — `PUT /api/v1/companies/{company}` and the area endpoints under `/api/v1/companies/{company}/areas` — gated by the `ability:` middleware and the `sync` policies, rate-limited to 60 req/min keyed by token. Full endpoint reference: `docs/api-integration.md` in the repo.

## MCP server (`POST /mcp/bookings`)

Every tool call through the MCP server needs the ability mapped to that tool, checked before any query — a `read`-only token gets `{"error": "forbidden"}` on write tools. The route is rate-limited to **60 requests/min per token** (shared with the sync API bucket). The company permission check still applies on top.

| Ability | Tools |
| --- | --- |
| `read` | `list-events`, `get-event`, `list-event-types`, `get-availability`, `next-available-event`, `find-slot`, `list-bookings`, `get-booking`, `search-bookings`, `list-pending-approvals`, `list-areas`, `list-tables`, `list-venue-items`, `list-orders`, `get-order`, `get-kpi`, `list-menu-items`, `list-waitlist-entries`, `get-guest`, `list-inquiries`, `get-stock-levels` |
| `create` | `create-booking` |
| `update` | `update-booking`, `mark-booking-service`, `check-in-booking`, `assign-table-to-booking`, `offer-waitlist-spot`, `publish-event`, `convert-inquiry`, `toggle-menu-item-sold-out` |
| `delete` | `cancel-booking` |

`create-booking` accepts `bookable_type` `event`, `time`, `seat`, `seat_time`, `area` — only bookable leaves: submitting a parent that still owns children returns `{"error": "pick_a_leaf", "children": [...]}` with the leaf ids to retry on.

Beyond tools the server exposes read-only **resources** (`docs://catalog` for the doc index, `docs://{category}/{slug}` for one page, `changelog://index` for releases) and **prompts** — ready-made workflows like `daily-service-brief` and `fill-cancelled-slot` that instruct the client which tools to call. Both need the `read` ability. Full reference: [MCP server](/docs/developer/mcp-server).
## Expiry

New tokens expire after 90 days (`SANCTUM_TOKEN_TTL_DAYS`; set `0` to disable). The expiry date shows next to each token in the profile list — re-issue the token before that date. Tokens created before this change have no expiry (`Never expires`) and keep working until revoked.

## Revoking

Revoke a token from the profile page. Revocation is immediate; in-flight requests with the revoked token fail on their next call.


---
> Source: https://placeful-app.com/en/docs/developer/api-tokens
