---
title: "One API call with its request and response bodies"
method: GET
path: "/logs/{id}"
tags: ["Logs"]
---

# One API call with its request and response bodies

`GET /logs/{id}`

## Request

- Base URL: `https://api.oveyon.com/v1`
- URL: `https://api.oveyon.com/v1/logs/{id}`
- Auth: HTTP bearer

## Path parameters

- `id` integer, required

## Response `200`

The call

- ApiLogDetail
  - `id` integer, required: The log entry id (GET /v1/logs/{id}).
  - `createdAt` string, date-time, required: When the request ARRIVED (milliseconds).
  - `method` 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', required
  - `route` string, required: The route PATTERN, the same for every id (`/v1/messages/:uuid`). Filter by it.
  - `path` string, required: The path as called, with the ids and without the query string.
  - `status` integer, required: The HTTP status we answered.
  - `durationMs` integer, required: Server-side time until the response was ready.
  - `credential` object, required: The API key that made the call (`name` and `prefix` are null once the key is deleted).
    - `id` integer, required
    - `name` string, nullable, required
    - `prefix` string, nullable, required
  - `ip` string, nullable, required: The caller IP as seen by our edge.
  - `userAgent` string, nullable, required: The User-Agent header (up to 255 characters), with any key-shaped value redacted.
  - `sdk` object, nullable, required: The client family detected from the User-Agent; null = no User-Agent. `other` = not recognized.
    - `name` 'oveyon-node' | 'oveyon-python' | 'oveyon-php' | 'oveyon-mcp' | 'curl' | 'node' | 'python' | 'php' | 'go' | 'java' | 'ruby' | 'postman' | 'insomnia' | 'browser' | 'other', required
    - `version` string, nullable, required
  - `error` string, nullable, required: The `error` code of the response, on 4xx/5xx.
  - `messageId` string, uuid, nullable, required: The message this call created (POST /v1/send, reply, survey send) or acted on (GET/approve/reject of /v1/messages/{uuid}).
  - `mcpCallId` string, nullable, required: Set on calls an AI agent made through the MCP server: the `callId` of the MCP message (tool call) that made it — find it in GET /v1/logs/mcp.
  - `query` object, nullable, required: The query string as received, redacted by the same rules as the bodies. null = no query string.
  - `request` ApiLogBody, required: A body as stored: TEXT (usually JSON), with secrets replaced by «redigido» and message bodies and attachments — file names included — (`html`, `text`, `content`, `filename`…) and the detail of safety signals (`signals[].detail`) replaced by `{ "omitted": "content", "length": N }`, and template variables (`data`) keeping their names but not their values. Inbound content routes and the log routes store only `{ "omitted": …, "bytes": N }`. On the marketing routes (`/v1/contacts…`, `/v1/lists…`) the request and the query keep only field names and ids — the values become `{ "omitted": "personal" }` — and a success response is stored as `{ "omitted": "personal", "bytes": N }`. Request headers are never stored.
    - `body` string, nullable, required: null = the call had no body.
    - `bytes` integer, nullable, required: The ORIGINAL size in bytes, when known.
    - `truncated` boolean, required: true when the stored text was cut at the 32 KB cap — it may then no longer be valid JSON.
  - `response` ApiLogBody, required: A body as stored: TEXT (usually JSON), with secrets replaced by «redigido» and message bodies and attachments — file names included — (`html`, `text`, `content`, `filename`…) and the detail of safety signals (`signals[].detail`) replaced by `{ "omitted": "content", "length": N }`, and template variables (`data`) keeping their names but not their values. Inbound content routes and the log routes store only `{ "omitted": …, "bytes": N }`. On the marketing routes (`/v1/contacts…`, `/v1/lists…`) the request and the query keep only field names and ids — the values become `{ "omitted": "personal" }` — and a success response is stored as `{ "omitted": "personal", "bytes": N }`. Request headers are never stored.
    - `body` string, nullable, required: null = the call had no body.
    - `bytes` integer, nullable, required: The ORIGINAL size in bytes, when known.
    - `truncated` boolean, required: true when the stored text was cut at the 32 KB cap — it may then no longer be valid JSON.

## Other responses

- `401`: Missing or invalid API key.
- `403`: Insufficient scope (`insufficient_scope`, with `required` and `have`), a scope the key holds but cannot use while its human-approval lock is on (`scope_blocked_by_approval_lock`, with `required` — approve:holds, write:send-policies, manage:webhooks, write:inbound, write:templates, write:surveys, write:domains, write:contacts), API key switched off by its owner in the panel (`key_disabled` — the key is valid; turn it back on or use another one, do not rotate), sending disabled, account suspended (`account_suspended`) or unknown (`account_unknown`), or the key used from an IP outside its allowed ranges (`ip_not_allowed`, with the `allowed` list — see /v1/credentials/ip-rules). 403 is not a limit: do not retry.
- `404`: Does not exist, belongs to another account, or the identifier is malformed — the response is identical in all three cases, on purpose.
- `429`: Per-key limit (`rate_limited`, 600 requests/min by default per API key — or per MCP OAuth connection —, from any IP), per-IP limit for requests WITHOUT a valid credential (`rate_limited`, 600/min by default), too many unknown or malformed credentials from your address (`too_many_auth_failures`, 20/min by default, counted per IPv4 address or per IPv6 /48; a missing `Authorization`, an expired OAuth token or a disabled key do not count, and anything without the shape of one of our keys counts without being checked; from an address over that limit a valid key still gets through within 120 credential checks/min, and above them every credential not validated in the last 5 minutes gets this 429 until the minute turns) — all checked before the route, on every operation —, account quota (`quota_exceeded` with `window`, `used`, `limit`), warmup cap (`warmup_cap_reached`) or saturation (`queue_full`). Honor `Retry-After` with exponential backoff and jitter.
- `500`: internal_error — an unexpected failure on our side. The detail stays in our logs, never in the body. Retry reads; retry a write only when it is idempotent or carries an idempotency key.
- `503`: ip_rules_unavailable — the IP ranges bound to your key could not be checked right now (caution, not a denial). Retry after `Retry-After` (30 s).

---

[API](https://skmtc.dev/oveyon/apis/oveyon-api.md) · [All operations](https://skmtc.dev/oveyon/apis/oveyon-api/llms.txt) · [OpenAPI document](https://skmtc.dev/oveyon/apis/oveyon-api/revisions/abf64c09d297?raw)
