---
title: "Get an email of the history"
method: GET
path: "/v1/accounts/{account_id}/emails/{email_id}"
tags: ["AccountEmails"]
---

# Get an email of the history

`GET /v1/accounts/{account_id}/emails/{email_id}`

## Request

- Base URL: `https://app.beel.es/api`
- URL: `https://app.beel.es/api/v1/accounts/{account_id}/emails/{email_id}`
- Auth: HTTP bearer

Returns one recorded email with its message body (HTML and plain text), its attachments
and, for batch emails, the invoices it carried.

- **`body_available`:** the body is fetched live and is only available while the message
  has a provider message id and the provider still retains it; otherwise it is `false` and
  `html_body` / `text_body` are `null`.
- **An email that never left:** `QUEUED` or `REJECTED`, it has no body for that reason.

## Path parameters

- `email_id` string, uuid, required

## Response `200`

Email retrieved successfully

- EmailDeliveryDetailResponse
  - `success` boolean, required
  - `data` EmailDeliveryDetail, required: Detail of one recorded email, including the message content (HTML / text body) retrieved live from the email provider. The body is only available for emails with a provider id (`external_message_id`) and while the provider retains it; when it cannot be retrieved, `body_available` is `false` and the bodies are null.
    - `id` string, uuid, required
    - `email_type` string, required: Email type (EmailType name or template). e.g. INVOICE_EMITTED
    - `recipients` string[], required: Recipient addresses (To)
    - `cc` string[]: Carbon-copy addresses (CC)
    - `subject` string
    - `related_entity_type` string, nullable: Type of the SINGLE related entity (e.g. INVOICE for an invoice, USER, SUBSCRIPTION). `null` for batch emails (BULK_INVOICES): their relation is the invoices in `related_invoices`, not a single entity.
    - `related_entity_id` string, uuid, nullable: Id of the single related entity. `null` for batch emails.
    - `status` 'QUEUED' | 'REJECTED' | 'SENT' | 'FAILED' | 'DELIVERED' | 'BOUNCED' | 'OPENED', required: Status of an email. The history records every email the system decided to send, not only the ones that went out: an email stopped by policy is listed as REJECTED rather than omitted. - QUEUED: authorised and recorded, not dispatched yet - REJECTED: stopped by policy and never sent (terminal, not retried). In test environments invoices may only be emailed to the account owner's own address (`+tag` aliases included), so a message addressed elsewhere lands here - SENT: successfully sent to the provider - FAILED: sending failed - DELIVERED / BOUNCED / OPENED: reported by the provider's webhooks
    - `sent_at` string, date-time: Time the email was sent
    - `body_available` boolean, required: `true` if the email content could be retrieved from the provider. `false` if the email has no provider id or the provider no longer retains it.
    - `html_body` string, nullable: HTML body of the email as it was sent. Null if not available.
    - `text_body` string, nullable: Plain-text body of the email. Null if not available.
    - `attachments` EmailAttachment[]: Email attachments (e.g. the invoice PDF). In invoice emails the actual content goes here, not in the body.
      - `filename` string, required: File name
      - `content_type` string: MIME type of the attachment
      - `size` integer: Size in bytes, if known
      - `download_url` string, nullable: Temporary download URL signed by the provider (may expire). Null if the provider does not supply it.
    - `related_invoices` RelatedInvoice[]: Invoices included in a batch email (BULK_INVOICES). Each element links to its invoice. Absent/empty in single-entity emails.
      - `id` string, uuid, required
      - `invoice_number` string, required
  - `meta` ResponseMeta
    - `timestamp` string, date-time
    - `request_id` string

## Other responses

- `400`: The request target could not be read as declared, so nothing was looked up. Three causes, all named in `error.details`: a path or query parameter whose value does not parse as its type (`VALIDATION_ERROR` — a UUID that is not a UUID, an unknown enum value, an empty element in a list such as `status=ISSUED,`), a required parameter that was not sent (`MISSING_PARAMETER`), or a header that is meant to carry an identifier and does not (`ACTIVE_COMPANY_HEADER_INVALID`). A list parameter sent entirely empty (`status=`) is not an error: it is the same as omitting it. The fault is in the target, not in the content — which is what `422` is defined over.
- `401`: Missing or invalid authentication. Like every other error, `message`/`detail` is localized according to `Accept-Language` (`es`, `en`, `ca`); defaults to English when the header is missing or asks for none of those.
- `403`: The credential cannot reach the account in the path, or lacks the `emails:read` scope. An account you cannot reach and an account that does not exist answer identically.
- `404`: The resource addressed by the path does not exist, or is not one this credential can see — the two are answered identically, so existence is never disclosed. `error.code` names the kind of resource that was missing, so a client can tell which of several ids in a path failed to resolve: - `INVOICE_NOT_FOUND` — the `{invoice_id}` (invoices, proformas and their sub-resources). - `RECURRING_NOT_FOUND` — the `{recurring_invoice_id}`. - `CLIENT_NOT_FOUND` — the `{customer_id}`. - `INVOICE_CLIENT_NOT_FOUND` — the `customer_id` referenced by an invoice body does not resolve to a customer of the issuing company. - `PRODUCT_NOT_FOUND` — the `{product_id}`. - `SERIES_NOT_FOUND` — the `{series_id}`. - `WEBHOOK_SUBSCRIPTION_NOT_FOUND`, `DELIVERY_LOG_NOT_FOUND` — the `{webhook_id}` and the `{delivery_id}`. - `PROFILE_NOT_FOUND` — the credential's account has no company profile yet: its holder has not completed onboarding, so there is no issuer to act as. - `NOT_FOUND` — the generic fallback, for the few resources that carry no code of their own. Some resources declare a more specific `404` response of their own (`CONNECTION_NOT_FOUND`, `MEMBER_NOT_FOUND`, `GRANT_NOT_FOUND`, `INVITATION_NOT_FOUND`, `REQUEST_LOG_NOT_FOUND`); it is documented on the operation. A `404` with `ENDPOINT_NOT_FOUND` is a different answer: the **path itself** does not exist in this API (a typo in the route, or a resource that was never here). It says nothing about any resource. When the path exists but not with that method, the answer is `405`, not `404`.
- `429`: Rate limit exceeded. `RATE_LIMIT_EXCEEDED` when the request rate of your credential is over its limit; `TOO_MANY_REQUESTS` when an email the operation sends is held back because too many went out to that recipient in a short time. Either way, retry after the `Retry-After` seconds. `QUOTA_EXCEEDED` is the other `429`: the operation would go over a quota of your plan for the current period (issued invoices, for instance). Retrying does not help until the quota resets or the plan changes. `error.details` carries `feature_code`, `current_usage`, `quota_limit` and `reset_date`, and `RateLimit-Reset` the seconds until the reset when it is known.
- `500`: Internal server error
- `default`: Any status code the operation does not list above. Every operation declares it, so a generated client always has a branch to fall into and never loses the cause of a failure it did not anticipate. This is where the transport-level answers land — `405`, `406`, `415` and `429` — together with any status a future version of the API starts returning. All of them carry the same error envelope as the codes listed explicitly, so `error.code` is what tells them apart: switching on the status code alone is not enough. See «Transport-level errors» in the API description for when each one is produced. A `402` carrying `SUBSCRIPTION_REQUIRED` lands here when the account has no active subscription. `error.details` says why and what to do: `current_status` (absent if it never had one), `required_permission` and `action`. A `503` carrying `SERVICE_UNAVAILABLE` lands here too, with a `Retry-After` header: a dependency of BeeL itself (the database, the email provider) was momentarily unavailable and nothing was done. Retry after those seconds. A `502` carrying `EXTERNAL_SERVICE_ERROR` also lands here: an outbound integration the operation depends on failed or did not answer in time. It is a transient condition — retry with the same `Idempotency-Key` where the operation accepts one. One exception to the envelope: a failure of the network edge that never reaches the application (`502`, `503`, `504`, `524`) is generated by Cloudflare and its body is not BeeL's — it may not even be JSON. Treat those as "no answer", and retry.

---

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