API logs
What this workspace's API keys have actually been doing: method, route, status, error code, latency and which key was used. It is the fastest way to answer why an integration is failing without adding logging to it.
The log
One cursor-paginated route, newest first, kept for a bounded window. An entry is not necessarily a key's: api_key_id is null for a session-driven call.
Session credentials only
logs.read is deliberately OUTSIDE the API-key scope set: a key cannot read the log of its own use. Only a signed-in user with the permission can, carrying X-Workspace-Id. See authentication.
An entry
| Field | Type | Description |
|---|---|---|
| method, route, path | string | The matched route PATTERN — including the /api prefix the proxy serves the backend under — and the concrete path with the query string stripped. The pattern is what you group by. |
| status_code | number | The HTTP status that was returned. |
| error_code | string | null | The error code, when the request failed. Null on success. |
| duration_ms | number | Server-side handling time in milliseconds. |
| api_key_id | string | null | Which API key made the request, when one did. |
| api_key_mode | "live" | "test" | null | Whether that key was a live or a test key. |
| request_id | string | null | The id echoed in the response header. This is what to quote in a support request. |
| created_at | string | When the request was handled. |
No bodies are recorded
No request body, no response body, no headers and no query strings are stored. That is a deliberate limit rather than an omission: it means the log can be read by anyone with logs.read without exposing a message's contents.
Filters
| Parameter | Values | Description |
|---|---|---|
| outcome | success, error | Successful or failed requests. |
| status_code | number | One exact HTTP status. |
| method | GET, POST, … | One HTTP method. |
| api_key_id | key_… | One API key. Useful for isolating a single integration. |
| since, until | ISO-8601 | A time window. Both bounds are ISO-8601. |
| limit, after | — | Cursor pagination. |
# logs.read is deliberately outside the API-key scope set.
curl "https://whats.azzamkh.sa/api/v1/api-logs?outcome=error&limit=50" \
-H "Authorization: Bearer <access token>" \
-H "X-Workspace-Id: ws_..."
# 200 OK — no bodies, no headers, no query strings are recorded.
# {
# "data": [
# {
# "id": "log_...",
# "method": "POST",
# "route": "/v1/messages",
# "path": "/v1/messages",
# "status_code": 422,
# "error_code": "VALIDATION_ERROR",
# "duration_ms": 34,
# "api_key_id": "key_...",
# "api_key_mode": "test",
# "request_id": "req_...",
# "created_at": "2026-08-14T09:00:00.000Z"
# }
# ],
# "page": { "next_cursor": "...", "has_more": true }
# }Filtering by outcome=error and grouping by error_code is usually the fastest first question. The errors page explains what each code means.