---
title: "Get an inbound email request"
method: GET
path: "/inbound-requests/{requestId}"
tags: ["Inbound Requests"]
---

# Get an inbound email request

`GET /inbound-requests/{requestId}`

## Request

- Base URL: `https://inkseal.net/api/v1`
- URL: `https://inkseal.net/api/v1/inbound-requests/{requestId}`
- Auth: HTTP bearer

One conversation in full: what the parser understood (`intent`), what it still needs (`missingInformation`), what was attached and whether each file was stored or refused, and the public ids of the document and envelope the request produced.

A request id belonging to another workspace answers exactly like an id that never existed, because a 403 would confirm it exists.

Costs both `envelopes:read` and `account:read`.

## Response `200`

The inbound request.

- object
  - `data` InboundRequest, required: One inbound mail conversation as the API publishes it: the workflow, not the mail. No bodies, no storage keys, no reply token, no provider message ids, no row ids.
    - `id` string, required
    - `state` 'received' | 'parsing' | 'parsed' | 'drafted' | 'awaiting_approval' | 'approved' | 'cancelled' | 'expired' | 'needs_info' | 'rejected' | 'failed', required: Where the conversation is. `awaiting_approval` is the only actionable state. `approved`, `cancelled`, `expired`, `rejected`, and `failed` are terminal. `needs_info` means the agent has emailed the sender for missing detail and is waiting.
    - `error` string, nullable, required: Why the request failed or was rejected, or a non-fatal warning recorded while drafting. Null when nothing went wrong.
    - `intent` InboundRequestIntent, required: What the parser understood from the email. Nothing the model was given is echoed back, and a stored intent whose shape this API no longer recognizes is published as null rather than forwarded verbatim.
      - `signers` object[], required
        - `name` string, nullable, required
        - `email` string, email, required
        - `order` integer, nullable, required: 1-based position for a sequential send, or null when the sender did not order them.
      - `signingOrder` 'parallel' | 'sequential', required
      - `subject` string, nullable, required
      - `message` string, nullable, required
      - `expiresInDays` integer, nullable, required
      - `attachmentIndex` integer, nullable, required: Which attachment the sender chose, by position in `attachments`.
      - `notes` string, nullable, required: Anything the parser recorded that does not fit the other fields.
    - `missingInformation` string[], required: What the parser could not determine. This is the field to branch on when deciding whether to reply to the sender with the missing detail.
    - `attachments` InboundRequestAttachment[], required
      - `filename` string, required
      - `contentType` string, required
      - `size` integer, required: Bytes stored, or the size the provider declared for a refused file.
      - `status` 'stored' | 'refused', required
      - `refusedReason` 'null' | 'too_many_attachments' | 'file_too_large' | 'message_too_large', nullable, required: Why the file was skipped. Null on a stored attachment.
    - `document` InboundRequestResourceRef, required: A resource this conversation produced, by public id.
      - `id` string, required
    - `envelope` InboundRequestResourceRef, required: A resource this conversation produced, by public id.
      - `id` string, required
    - `clarificationRoundTrips` integer, required: How many `needs_info` rounds the conversation has used. The loop is capped, and hitting the cap fails the request.
    - `createdAt` string, date-time, required
    - `updatedAt` string, date-time, required

## Other responses

- `401`: The credential is missing, malformed, unknown, expired, revoked, or past its rotation grace window. Code: `unauthorized`. Every authentication failure is deliberately indistinguishable.
- `403`: The credential is valid but may not do this. Codes: `insufficient_scope` (the route's scopes are not all held; `details` names `requiredScopes` and `missingScopes`), `forbidden` (the capability check on the resolved resource failed), `scope_escalation` (a grant wider than the caller's own was requested).
- `404`: The resource does not exist, was deleted, or belongs to another workspace. Code: `not_found`. The three cases are answered identically on purpose, so a 404 never confirms that an identifier exists elsewhere.
- `429`: A limit was reached. Codes: `rate_limited` (the credential's per-bucket HTTP limit, or the per-workspace detection throttle), `workspace_limit_exceeded` (a workspace content or send limit; `details.limit` names it), `daily_budget_exceeded` (the workspace's daily detection budget). The four rate-limit headers are sent on this response and on no successful one, so a client cannot read its remaining budget without being refused. Retrying after `Retry-After` is always safe: a rate-limit refusal happens before the handler runs.
- `503`: A dependency the request needs is unreachable and nothing was changed. Codes: `queue_unavailable` (a background job could not be enqueued; retry the call), `billing_unavailable` (Stripe would not open a checkout session; nothing was charged, retry the call), `service_unavailable` (the rate-limit store is down, so the request was refused before running).

---

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