# Public API Documentation

> Authentication, rate limits, paging, and key concepts for the Virtual Mail Public API.

Web page: https://reference.earthclassmail.com/v1/rest-api/

The Virtual Mail Public API provides direct HTTP access to virtual-mail
resources — inboxes, mail items (pieces), scans, media, shipments, and more.

If any difficulties arise, please email
[api@earthclassmail.com](mailto:api@earthclassmail.com) with your questions.

## OpenAPI Spec

The OpenAPI 3.1 specification is available as a YAML file for import into
tools like Postman, Swagger UI, or code generators:

| Format | Link |
|---|---|
| YAML | [`rest-api-v1.yaml`](https://reference.earthclassmail.com/rest-api-v1.yaml) |

Response schemas and examples describe the documented fields. Responses may
include additional fields; clients should ignore fields they don't recognize.

## Base URL

All endpoints are relative to:

```
https://api.earthclassmail.com/v1
```

For example, the reference page for `GET /pieces/{piece_id}` means
`GET https://api.earthclassmail.com/v1/pieces/{piece_id}`.

## Authentication

All requests require a **Static API Key**, passed in an
`x-api-key` header. You only need one key: the same
key works for the Public API and the MCP server.

Your API key is tied to your user account, so it can reach every mailbox you
have access to. Keep it private, treat it like a password, and don't commit it
to source control. Regenerating the key invalidates the existing one.

**Generate your API key in the Virtual Mail web application:**

1. Sign in and go to **Settings** → **Integrations**.
2. Find the **Custom API integration** section.
3. In the **API Key** card, click **Generate Key** (invalidates any previously
   generated key).
4. Your key appears — click **Copy**.

You can also generate your API key in the Virtual Mail mobile app.

To check that your key works, fetch the user it belongs to:

```sh
curl https://api.earthclassmail.com/v1/user \
  -H "x-api-key: $VIRTUAL_MAIL_API_KEY"
```

A valid key returns `200` with the user:

```json
{
  "id": 5001,
  "admin_account_ids": [100, 200],
  "created_at": "2024-01-10T08:00:00Z",
  "email": "jane.doe@example.com",
  "first_name": "Jane",
  "is_admin": true,
  "last_name": "Doe",
  "phone": "15551234567",
  "updated_at": "2024-06-01T12:00:00Z",
  "username": "jane.doe@example.com"
}
```

A missing or invalid key returns `401`. See
[Get Current User](https://reference.earthclassmail.com/_llms_txt/v1/rest-api/spec/operations/getcurrentuser.md) for the full
response schema.

## Errors

Failed requests return a `4xx` or `5xx` status code with a JSON body in this
shape:

```json
{
  "error": {
    "code": 422,
    "message": "label: must have a value",
    "type": "REST",
    "fields": {}
  }
}
```

- **code** — Usually the HTTP status code repeated in the body, but not always:
  a request to an unknown path returns HTTP `400` with `code` `500`. Use the
  HTTP status.
- **message** — A human-readable description. For validation errors it lists
  each problem as `field: message`. Don't parse it; the wording can change.
- **type** — The kind of error: `REST` or `Authorization`.
- **fields** — Only present on `422`. It may give more details about what
  failed validation, as lists of messages keyed by the name of the input.

| Status | Meaning | Retry? |
| --- | --- | --- |
| `400` | The request can't be processed as sent. | No — fix the request. |
| `401` | The `x-api-key` header is missing or invalid, or you don't have access to an ID in the request. | No — fix the key or the ID. |
| `403` | Actions on a piece in the request are blocked. The piece's `attributes` include `actions-blocked-no-reverification` or `actions-blocked-document-expired`. | No. |
| `404` | The resource exists and you can access it, but the thing you asked for doesn't — for example, a piece with no pending shred request. | No. |
| `409` | The request conflicts with the resource's current state, for example an operation already in progress. | Only after checking the resource's state. |
| `422` | The request failed validation; see `fields`. | No — fix the request. |
| `429` | Rate limit exceeded; see [Rate limiting](#rate-limiting). | Yes, after the 5-minute block ends. |
| `5xx` | Unexpected server error. | Yes for `GET`, with backoff. For other methods, check whether the action took effect first. |

An ID that doesn't exist and an ID that belongs to another account return the
same response, so a mistyped or stale ID returns this `401`:

```json
{
  "error": {
    "code": 401,
    "message": "User does not have access to requested resource.",
    "type": "Authorization"
  }
}
```

The API doesn't support idempotency keys. Before retrying a request that
changes something, such as creating a shipment or shredding, fetch the resource
and confirm the first attempt didn't succeed. Emailing a scan can't be checked
this way, so retrying it sends the email again.

## Rate limiting

To maintain a high quality of service, request rate limiting is enforced.

The maximum number of requests per minute from a single IP is **300**.

If you exceed this limit, the API will respond with `429 Too Many Requests`
status codes for **5 minutes** from the time the limit was exceeded. This rate
limit applies to **all endpoints combined**.

> **Note**
>
> It's good practice to code your application to handle rate limiting, even if you
> don't expect to hit the limit.

## Paging

Most "list" endpoints support paging through the following query string
parameters:

- **page** — The desired page from the result set. Integer. Defaults to `1`.
- **per_page** — The number of items per page. Integer. Defaults to `100`.
  Listing an inbox's pieces is the exception: it defaults to `95` and returns
  at most `95` items per page.

## Key Concepts

- **Inbox** — A mailbox that receives mail on behalf of a user or organization.
- **Piece** — An individual item of mail received at a facility and assigned to
  an Inbox.
- **Scan** — A request to digitize a Piece. Produces Media items once completed.
- **Media** — A digital image or PDF associated with a Piece (e.g., envelope
  photo, content scan). Includes a short-lived download URL.
- **Shipment** — A request to physically forward one or more Pieces to a
  Shipping Address.
- **Shipping Address** — A saved destination address that Pieces can be shipped
  to.
- **Shipping Rate** — A real-time carrier quote for a Shipment, including
  carrier, service, and price.
- **Check Deposit Request** — A request to deposit the checks found in a Piece
  to one of the Inbox's bank accounts. See
  [Depositing a Check](https://reference.earthclassmail.com/_llms_txt/v1/rest-api/playbooks.md#depositing-a-check).
