> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbitforms.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Contact Emails API

> Send one-to-one emails to your contacts and read what your team sent, with delivery, open and reply status.

The same one-to-one email as the composer on a contact's page: each email goes out from the sending address of the person the credential acts as (the key's creator, or the person who authorized the app), through your workspace's verified sending domain, with an unsubscribe link added.

## Scopes

| Scope | Allows |
| - | - |
| `emails:read` | List emails sent to contacts and read their messages and status |
| `emails:send` | Send an email to a contact |

## Endpoints

| Method | Endpoint | Description |
| - | - | - |
| `GET` | `/v1/contact-emails` | List sent emails, newest first (`emails:read`) |
| `GET` | `/v1/contact-emails/{id}` | One email, including its message (`emails:read`) |
| `POST` | `/v1/contact-emails` | Send an email to a contact (`emails:send`) |

## List Emails

Query parameters: `page`, `per_page` (max 100), `contact_id`, `to_email`, `sent_by` (a team member's user ID), `status`, `since` and `until` (ISO 8601, on the sent time).

`status` is one of `queued` (being sent), `sent`, `delivered`, `opened`, `replied`, `bounced`, `complained` or `failed` (the email provider refused it). Emails scheduled from the composer can also be `scheduled` (waiting for `scheduled_at`), `cancelled` (cancelled before they went) or `not_sent` (stopped at send time; `failure_reason` says why). While an email waits, `sent_at` is the time it is due to go.

If the person's record access is limited to their own or their team's contacts, pass `contact_id`: they can list the emails sent to a contact they can see, one contact at a time.

```json theme={null}
{
  "data": [
    {
      "id": "7d1c6a3e-1d5b-4a8e-9a43-2b6f0f1b9c11",
      "contact_id": "a3f1c2d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "sent_by": "0b9e2f6c-3a1d-4c5e-8f7a-6b5c4d3e2f1a",
      "from_email": "jane@acme.com",
      "to_email": "sam@example.com",
      "subject": "Following up on your demo request",
      "status": "opened",
      "sent_at": "2026-10-27T14:30:00.000Z",
      "scheduled_at": null,
      "failure_reason": null,
      "created_at": "2026-10-27T14:29:59.000Z"
    }
  ],
  "meta": { "total": 1, "page": 1, "per_page": 20, "total_pages": 1 }
}
```

`GET /v1/contact-emails/{id}` returns the same fields plus `body_html` and `body_markdown`.

## Send an Email

`POST /v1/contact-emails`

```json theme={null}
{
  "contact_id": "a3f1c2d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "subject": "Following up on your demo request",
  "body_text": "Hi Sam,\n\nThanks for booking a demo. Does Thursday at 2pm work?\n\nJane",
  "idempotency_key": "4f6b2d8e-9c1a-4e3b-8d7f-2a1c5e9b0f3d"
}
```

| Field | Description |
| - | - |
| `contact_id` | Required. The recipient is this contact's email address. The API only sends to contacts already in the workspace |
| `subject` | Required, up to 200 characters |
| `body_text` or `body_html` | Required. Plain text (blank lines start paragraphs) or HTML (scripts and unsafe markup are removed) |
| Merge fields | `subject` and the body may use merge fields such as `{{ contact.first_name }}` or `{{ sender.full_name }}`, filled in for the contact. A field Orbit doesn't know is refused (`400`) |
| `idempotency_key` | Optional, 16-100 letters, digits or dashes. A retry with the same key returns the first result instead of sending again |

Answers `201` with `{ "data": { "id", "contact_id", "to_email", "status": "sent", "replayed": false } }`, or `200` with `"replayed": true` for a repeated `idempotency_key`.

Sending is refused when:

* the contact isn't one the person's record access shows (`404`), or it is in Recently deleted;
* the contact unsubscribed, bounced, reported spam or is blocked, or is marked unsubscribed (`409 suppressed`);
* the person's access to companies or deals is limited and the email uses company or deal merge fields (`403`): send it from the contact page instead;
* the workspace has no verified sending domain, or the sender's address can't be used (`409`, with the reason as `error`);
* the workspace's subscription has ended or sending isn't on its plan (`403`);
* a sending cap is reached (`429`, with `Retry-After`). The caps are shared with the contact page: 20 emails a minute and 100 an hour per person, and 300 an hour per workspace.

Viewers can't send. Each email reaches someone outside your workspace and can't be unsent.

> **[MCP tools](https://docs.orbitforms.ai/mcp/tools.md)**
>
> The same operations as MCP tools

> **[Rate Limits](https://docs.orbitforms.ai/developers/api/rate-limits.md)**
>
> API rate limit details


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.