---
title: "Decision feed: which policy matched which message (90 days)"
method: GET
path: "/inbound-policies/decisions"
tags: ["Inbound"]
---

# Decision feed: which policy matched which message (90 days)

`GET /inbound-policies/decisions`

## Request

- Base URL: `https://api.oveyon.com/v1`
- URL: `https://api.oveyon.com/v1/inbound-policies/decisions`
- Auth: HTTP bearer

Newest first. The message subject and sender come only when the key also has read:inbound. Scope read:inbound-policies.

## Query parameters

- `limit` integer
- `before` integer
- `policy` integer

## Response `200`

Feed

- InboundPolicyDecisionList
  - `decisions` object[]
    - `id` integer
    - `policyId` integer, nullable: null after the policy was deleted — the frozen name stays.
    - `policyName` string
    - `message` object
      - `id` string, uuid
      - `subject` string, nullable: null unless the key also has read:inbound (the envelope is mailbox content).
      - `fromHeader` string, nullable: null unless the key also has read:inbound.
      - `outcome` string, nullable
    - `recipient` string, nullable: The mailbox this decision was for: one row per policy × recipient, since each recipient is judged by the policies of its own mailbox. null = a decision from before per-mailbox policies.
    - `action` string
    - `effectiveAction` string
    - `degradedReason` 'hold_disabled' | 'hold_limit' | 'limit_unreadable' | 'no_notice' | 'no_channel' | 'no_chat' | 'notify_failed' | 'mailbox_quarantine' | 'null', nullable: Why the action did not apply as written. hold/mute/restrict became a tag because: the platform switch is off (hold_disabled), the policy already holds its cap (hold_limit), the held count could not be read (limit_unreadable — never holds blindly), no channel could carry the hold notice (no_notice), or the restriction would leave a recipient without channels (no_channel). notify reached no one because: the account has no active Telegram chat (no_chat), or Telegram refused after the retries (notify_failed). hold/mute stayed a hold but the mailbox's own security quarantine holds the message (mailbox_quarantine): the notice was the quarantine's, with no policy deadline and outside the policy cap. Never retained silently.
    - `detail` string, nullable: JSON text: the tags (tag), the channels (restrict_channels) or the note (notify).
    - `decidedAt` string, date-time
  - `nextBefore` integer, nullable
  - `retentionDays` integer

## 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.
- `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)
