mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
Adds the machine-to-machine auth layer the public REST API needs so external scripts and automations can drive wacrm (issue #245). This is the groundwork release; data endpoints land one at a time on top of it. - Migration 026: account-scoped `api_keys` table. SHA-256 hash at rest (plaintext shown once), display `key_prefix`, `scopes[]`, RLS via `is_account_member` (members read, admin+ manage). - `src/lib/api-keys/`: key gen/hash (wacrm_live_ prefix, timing-safe), scope vocabulary, service-role auth-path store. - `requireApiKey(request, scope?)` (src/lib/auth/api-context.ts): bearer parse -> hash lookup -> liveness -> per-key rate limit -> scopes-only authz. Returns a service-role-scoped account context. - `/api/v1` envelope ({data} / {error:{code,message}}) + GET /api/v1/me reference endpoint that exercises the whole stack. - Dashboard: admin+ key management routes + Settings -> API keys tab. - Docs (docs/public-api.md), CHANGELOG, README, .env note. Checks: typecheck clean, lint 0 errors, full suite green (+27 tests). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
131 lines
4.5 KiB
Markdown
131 lines
4.5 KiB
Markdown
# Public API (`/api/v1`)
|
|
|
|
The public API lets you drive your wacrm instance from your own
|
|
scripts and automations — send messages, manage contacts, launch
|
|
broadcasts — without going through the dashboard UI.
|
|
|
|
> **Status:** groundwork release. Authentication, scopes, rate
|
|
> limiting, and the `GET /api/v1/me` probe ship now. The data
|
|
> endpoints (`messages`, `contacts`, …) land one at a time in
|
|
> follow-up releases — see [Roadmap](#roadmap).
|
|
|
|
## Authentication
|
|
|
|
Every request authenticates with an **API key**, sent as a bearer
|
|
token:
|
|
|
|
```
|
|
Authorization: Bearer wacrm_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
```
|
|
|
|
Keys are **account-scoped**: a key acts on exactly one account, the
|
|
one it was created in. There is no cross-account access.
|
|
|
|
### Creating a key
|
|
|
|
In the dashboard: **Settings → API keys → New API key**. Only
|
|
**admins and owners** can create keys.
|
|
|
|
1. Give the key a name (after the integration that will use it).
|
|
2. Grant the **scopes** it needs — nothing more (see below).
|
|
3. Copy the key. **The full key is shown exactly once.** wacrm
|
|
stores only a SHA-256 hash, so it can never be shown again. If you
|
|
lose it, revoke it and create a new one.
|
|
|
|
### Revoking a key
|
|
|
|
**Settings → API keys → Revoke.** Revocation is effective on the
|
|
key's next request. Revoked keys stay in the list as an audit trail.
|
|
|
|
## Scopes
|
|
|
|
A key can do only what its scopes allow — independent of who created
|
|
it. Grant the minimum.
|
|
|
|
| Scope | Allows |
|
|
| -------------------- | ---------------------------------------- |
|
|
| `messages:send` | Send WhatsApp messages |
|
|
| `messages:read` | Read messages and delivery status |
|
|
| `contacts:read` | List and read contacts |
|
|
| `contacts:write` | Create and update contacts |
|
|
| `conversations:read` | List and read conversations |
|
|
| `broadcasts:send` | Launch broadcast campaigns |
|
|
|
|
A key with **no scopes** still authenticates and can call
|
|
`GET /api/v1/me` — useful for verifying a key works.
|
|
|
|
## Response envelope
|
|
|
|
Every response uses one of two shapes:
|
|
|
|
```jsonc
|
|
// success
|
|
{ "data": { /* ... */ } }
|
|
|
|
// failure
|
|
{ "error": { "code": "forbidden", "message": "This API key is missing the 'messages:send' scope" } }
|
|
```
|
|
|
|
Branch on `error.code` (stable); `error.message` is for humans and
|
|
may be reworded.
|
|
|
|
| Status | `code` | Meaning |
|
|
| ------ | -------------- | ------------------------------------------------ |
|
|
| 401 | `unauthorized` | Missing / malformed / unknown / revoked / expired key |
|
|
| 403 | `forbidden` | Valid key, but missing the required scope |
|
|
| 429 | `rate_limited` | Per-key rate limit exceeded |
|
|
| 400 | `bad_request` | Malformed input |
|
|
| 404 | `not_found` | No such resource |
|
|
| 500 | `internal` | Server error |
|
|
|
|
## Rate limits
|
|
|
|
Requests are limited **per key**: **120 requests per minute**. On a
|
|
`429`, these headers tell you when to retry:
|
|
|
|
- `Retry-After` — seconds until the window resets
|
|
- `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`
|
|
|
|
> The limiter is in-memory and **per process**. A single-instance
|
|
> deploy (the common case for a self-hosted fork) is fine as-is. If
|
|
> you scale to multiple instances, swap the limiter for a shared
|
|
> store (Redis/Upstash) — see the note at the top of
|
|
> `src/lib/rate-limit.ts`. The limit is otherwise unenforced across
|
|
> instances.
|
|
|
|
## Endpoints
|
|
|
|
### `GET /api/v1/me`
|
|
|
|
Returns the account a key is bound to and the scopes it carries.
|
|
Requires only a valid key (no scope). Use it to verify a key works
|
|
and to discover its scopes.
|
|
|
|
```bash
|
|
curl https://your-crm.example.com/api/v1/me \
|
|
-H "Authorization: Bearer wacrm_live_xxx"
|
|
```
|
|
|
|
```json
|
|
{
|
|
"data": {
|
|
"account": { "id": "…", "name": "Acme Inc" },
|
|
"key": { "id": "…", "scopes": ["messages:send"] }
|
|
}
|
|
}
|
|
```
|
|
|
|
## Roadmap
|
|
|
|
Planned endpoints, shipping one per release (tracked in
|
|
[#245](https://github.com/ArnasDon/wacrm/issues/245)):
|
|
|
|
- `POST /api/v1/messages` — send a message to a phone number
|
|
(`messages:send`)
|
|
- `GET/POST /api/v1/contacts`, `GET/PATCH /api/v1/contacts/{id}`
|
|
(`contacts:read` / `contacts:write`)
|
|
- `GET /api/v1/conversations` (`conversations:read`)
|
|
- `POST /api/v1/broadcasts` (`broadcasts:send`)
|
|
- Outbound event webhooks (so automations can react to inbound
|
|
messages)
|