---
name: shipmail
version: 0.1.0
description: Real email inboxes on your own domain for agents, automations, and internal tools.
homepage: https://shipmail.to
docs: https://shipmail.to/docs
metadata:
  { "api_base": "https://shipmail.to/api/v1", "openapi": "https://shipmail.to/openapi.json" }
---

# Shipmail

Shipmail gives agents and automation systems a real email inbox on a domain you control. Use it to send outbound email, receive replies, process attachments, and drive workflows over normal email.

**Base URL:** `https://shipmail.to/api/v1`

**Docs:** [shipmail.to/docs](https://shipmail.to/docs)

**OpenAPI:** [shipmail.to/openapi.json](https://shipmail.to/openapi.json)

---

## What It Is Good For

Use Shipmail when you need:

- A real mailbox on your own domain, not a disposable inbox
- Programmatic sending and receiving over a REST API
- Thread-aware replies
- Webhooks for inbound and delivery events
- A mailbox that humans can also access through standard mail clients

Common use cases:

- Send transactional or operational email from your product
- Give an agent its own inbox for customer or vendor communication
- Watch an inbox for replies, confirmations, invoices, or support requests
- Build automations that react to inbound email and delivery events

---

## Quick Start

### 1. Create an API key

Create an API key in the Shipmail dashboard, choose the scopes your automation needs, and store it
securely.

```bash
export SHIPMAIL_API_KEY="sm_live_..."
```

### 2. Add a domain

```bash
curl -X POST https://shipmail.to/api/v1/domains \
  -H "Authorization: Bearer $SHIPMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"example.com"}'
```

The response includes the DNS records Shipmail needs for inbound and outbound email.

### 3. Verify the domain

After the DNS records are in place, trigger verification:

```bash
curl -X POST https://shipmail.to/api/v1/domains/DOMAIN_ID/verification \
  -H "Authorization: Bearer $SHIPMAIL_API_KEY"
```

For ongoing automation, subscribe to `domain.verified` instead of polling.

### 4. Create a mailbox

```bash
curl -X POST https://shipmail.to/api/v1/mailboxes \
  -H "Authorization: Bearer $SHIPMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "domain_id":"DOMAIN_ID",
    "address":"agent",
    "display_name":"Ops Agent"
  }'
```

This creates a mailbox like `agent@example.com`.

### 5. Send your first email

```bash
curl -X POST https://shipmail.to/api/v1/messages \
  -H "Authorization: Bearer $SHIPMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mailbox_id":"MBX_ID",
    "to":[{"address":"user@example.net","name":"User"}],
    "subject":"Hello from Shipmail",
    "text":"Plain text version",
    "html":"<p>Plain text version</p>"
  }'
```

Always send `text` and `html` together when possible.

### 6. Handle replies

For a simple pull-based workflow, list inbound JMAP messages for the mailbox:

```bash
curl "https://shipmail.to/api/v1/mailboxes/MBX_ID/inbox/messages?folder_role=inbox&limit=25" \
  -H "Authorization: Bearer $SHIPMAIL_API_KEY"
```

For a persistent workflow, create a webhook and handle `message.received`.

---

## CLI Shortcut

For terminal-driven setup, use the official CLI:

```bash
npx -y shipmail-cli --help

export SHIPMAIL_API_KEY="sm_live_..."
shipmail status
shipmail domains create example.com
shipmail mailboxes create --domain-id DOMAIN_ID --address agent
shipmail messages send --mailbox-id MBX_ID --to user@example.net --subject "Hello" --text "It works."
shipmail inbox list MBX_ID --folder-role inbox --limit 25
```

Use `shipmail api <METHOD> <PATH> --data JSON|@file` when an automation needs direct access to any REST endpoint.

---

## Authentication

All API requests except `GET /status` require:

```text
Authorization: Bearer YOUR_API_KEY
```

Never send your Shipmail API key anywhere except `https://shipmail.to/api/v1`.

---

## Core Workflow

The normal Shipmail flow is:

1. Add a domain
2. Verify DNS
3. Create one or more mailboxes
4. Send mail with `POST /messages`
5. Read replies with `GET /messages` or react with webhooks
6. Reply with `POST /messages/{id}/reply` or `POST /threads/{id}/reply`

If you need a mailbox immediately and do not want to manage external DNS, you can register a domain through Shipmail instead of bringing your own.

---

## Key Endpoints

### Domains

- `POST /domains` create a domain
- `GET /domains` list domains
- `GET /domains/{id}` get a domain
- `PATCH /domains/{id}` update a domain
- `DELETE /domains/{id}` delete a domain
- `POST /domains/{id}/verification` verify DNS records
- `POST /domains/search` search available domains
- `POST /domains/register` register a domain through Shipmail

### Mailboxes

- `POST /mailboxes` create a mailbox
- `GET /mailboxes` list mailboxes
- `GET /mailboxes/{id}` get a mailbox
- `PATCH /mailboxes/{id}` update a mailbox
- `DELETE /mailboxes/{id}` delete a mailbox
- `PATCH /mailboxes/{id}/password` reset mailbox password
- `GET /mailboxes/{id}/folders` list mailbox folders
- `POST /mailboxes/{id}/folders` create a custom mailbox folder
- `PATCH /mailboxes/{id}/folders/{folder_id}` rename a custom mailbox folder
- `DELETE /mailboxes/{id}/folders/{folder_id}` delete a custom mailbox folder
- `GET /mailboxes/{id}/identities` list mailbox sending identities
- `GET /mailboxes/{id}/inbox/messages` list inbound JMAP messages
- `PATCH /mailboxes/{id}/inbox/messages/{message_id}` set read or starred state
- `POST /mailboxes/{id}/inbox/messages/{message_id}/move` move a message
- `DELETE /mailboxes/{id}/inbox/messages/{message_id}` permanently delete a message already in Trash or Junk
- `GET /mailboxes/{id}/inbox/threads/{thread_id}` get full inbound thread content
- `GET /mailboxes/{id}/inbox/attachments` download raw attachment bytes
- `GET /mailboxes/{id}/rules` get inbox rules and folder targets
- `PUT /mailboxes/{id}/rules` replace inbox rules
- `PATCH /mailboxes/{id}/auto-reply` configure auto-reply
- `PATCH /mailboxes/{id}/spam-filter` configure spam threshold

### Messages

- `POST /messages` send a message
- `GET /messages?mailbox_id=...` list mailbox messages
- `GET /messages/{id}` get a message
- `POST /messages/{id}/reply` reply to a specific message

### Threads

- `GET /threads?mailbox_id=...` list thread summaries
- `GET /threads/{id}` get messages in a thread
- `POST /threads/{id}/reply` reply to a thread

### Suppressions

- `GET /suppressions` list suppressed recipients
- `DELETE /suppressions/{email}` remove a suppression entry

### Webhooks

- `POST /webhooks` create a webhook
- `GET /webhooks` list webhooks
- `GET /webhooks/{id}` get a webhook
- `PATCH /webhooks/{id}` update a webhook
- `DELETE /webhooks/{id}` delete a webhook
- `POST /webhooks/{id}/rotate-secret` rotate the signing secret
- `POST /webhooks/{id}/test` send a test event
- `GET /webhooks/{id}/deliveries` inspect delivery attempts

---

## Webhooks

Use webhooks when you need Shipmail to push events to your application over time.

Create one:

```bash
curl -X POST https://shipmail.to/api/v1/webhooks \
  -H "Authorization: Bearer $SHIPMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url":"https://yourapp.com/api/webhooks/shipmail",
    "events":["message.received","message.delivered","message.bounced"]
  }'
```

Important event types:

- `message.received`
- `message.sent`
- `message.delivered`
- `message.bounced`
- `message.complained`
- `domain.verified`
- `domain.verification_failed`
- `domain.degraded`
- `org.reputation_warning`
- `org.sending_throttled`
- `org.sending_suspended`
- `org.reputation_recovered`

Verify webhook signatures using your webhook secret. Shipmail provides signed payloads and the SDKs include verification helpers.

---

## Polling Vs Webhooks

- Use polling when you need a simple one-off check for new mail and do not have a public webhook endpoint.
- Use webhooks when you need continuous inbound processing, delivery tracking, or domain verification automation.

Shipmail does not currently document a public WebSocket event stream. Prefer webhooks for real-time integrations.

---

## Sending Rules

Read these carefully.

- Do not send spam, phishing, or deceptive email.
- Do not ignore bounces or complaints.
- Do not remove suppression entries unless you know the recipient is valid and wants your email.
- New accounts warm up gradually. Do not assume full sending capacity on day one.
- Each mailbox has its own recipient-rate limits.
- Monthly send quota depends on plan.

If your bounce or complaint rates get too high, Shipmail can warn, throttle, or suspend outbound sending.

---

## Message Guidelines

- Provide `mailbox_id` or `from` when sending.
- Provide at least one of `text` or `html`. Prefer both.
- Maximum 50 total recipients across `to`, `cc`, and `bcc`.
- Attachments are base64 payloads with filename and optional content type.
- Replies should usually use the reply endpoints so Shipmail preserves thread context.

---

## Common Patterns

### Wait for inbound confirmation

1. Send an email from a dedicated mailbox
2. Subscribe to `message.received`
3. Parse the inbound body or attachment
4. Continue the workflow

### Run a shared operational inbox

1. Create a mailbox like `ops@yourdomain.com`
2. Receive inbound events through webhooks
3. List thread history when a new message arrives
4. Reply in-thread from the same mailbox

### Process invoices or documents

1. Watch `message.received`
2. Fetch the inbound thread with `GET /mailboxes/{id}/inbox/threads/{thread_id}`
3. Use attachment `download_path` or `GET /mailboxes/{id}/inbox/attachments` for raw bytes
4. Classify, summarize, or route the document

### Monitor deliverability

1. Subscribe to `message.bounced` and `message.complained`
2. Inspect your suppression list
3. React to `org.reputation_warning` and `org.sending_throttled`

---

## Error Shape

Shipmail errors are returned as:

```json
{
  "error": {
    "type": "validation_error",
    "message": "Human-readable message",
    "request_id": "req_..."
  }
}
```

Common types include:

- `validation_error`
- `authentication_error`
- `authorization_error`
- `quota_exceeded`
- `not_found`
- `rate_limit_error`
- `conflict`
- `internal_error`

---

## Learn More

Use these as the canonical references:

- [shipmail.to/docs](https://shipmail.to/docs)
- [shipmail.to/docs/cli](https://shipmail.to/docs/cli)
- [shipmail.to/docs.md](https://shipmail.to/docs.md)
- [shipmail.to/openapi.json](https://shipmail.to/openapi.json)
- [shipmail.to/llms.txt](https://shipmail.to/llms.txt)
