---
title: "Message detail and timeline"
method: GET
path: "/messages/{uuid}"
tags: ["Messages"]
---

# Message detail and timeline

`GET /messages/{uuid}`

## Request

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

## Path parameters

- `uuid` string, required

## Response `200`

Message

- MessageDetail
  - `id` string
  - `from` string
  - `to` string
  - `subject` string, nullable
  - `status` 'accepted' | 'queued' | 'delivered' | 'deferred' | 'bounced' | 'suppressed' | 'failed' | 'frozen'
  - `statusDetail` string, nullable
  - `route` string, nullable: Which outbound path carried it; null until routed.
  - `acceptedAt` string, date-time, nullable
  - `deliveredAt` string, date-time, nullable
  - `templateId` integer, nullable
  - `templateVersion` integer, nullable
  - `returnPath` 'sender': Present ONLY when the message went out with the envelope (Return-Path) equal to the From address (`return_path: "sender"`, or the key's default). Absent = the platform address, as always.
  - `bounceTracking` false: Present only together with `returnPath: "sender"`, and always false: bounces of this message went to the sender's mailbox, so the platform does not track them — no suppression and no bounce webhook. A refusal our server saw during delivery still shows up in `deliveries`.
  - `sizeBytes` integer, nullable
  - `deliveries` object[]
  - `events` object[]
  - `tracking` object[]: Open and click events, oldest first. `readMsEstimate` is a weak estimate (the gap between beacons), opens only.
    - `type` 'open' | 'click'
    - `url` string, nullable
    - `host` string, nullable
    - `readMsEstimate` integer, nullable
    - `at` string, date-time

## 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`: `not_found` — does not exist, belongs to another account, or the identifier is malformed (identical in all three cases, on purpose). `outside_log_window` — the message is yours but was accepted before your plan's log window (Free 3 days, Starter 15, Plus 20, Growth 30, Business 45, Scale 60, Enterprise 365); `message` says the window. Held messages are never outside it while they wait for a decision. After the platform's own retention period (longer than any plan's window, or the end of yours if yours is longer) the message is deleted and answers `not_found`.
- `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)
