Public API Documentation
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 with your questions.
OpenAPI Spec
Section titled “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 |
Response schemas and examples describe the documented fields. Responses may include additional fields; clients should ignore fields they don’t recognize.
Base URL
Section titled “Base URL”All endpoints are relative to:
https://api.earthclassmail.com/v1For example, the reference page for GET /pieces/{piece_id} means
GET https://api.earthclassmail.com/v1/pieces/{piece_id}.
Authentication
Section titled “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:
- Sign in and go to Settings → Integrations.
- Find the Custom API integration section.
- In the API Key card, click Generate Key (invalidates any previously generated key).
- 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:
curl https://api.earthclassmail.com/v1/user \ -H "x-api-key: $VIRTUAL_MAIL_API_KEY"A valid key returns 200 with the user:
{ "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 for the full
response schema.
Errors
Section titled “Errors”Failed requests return a 4xx or 5xx status code with a JSON body in this
shape:
{ "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
400withcode500. 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:
RESTorAuthorization. - 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. | 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:
{ "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
Section titled “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.
Paging
Section titled “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 to95and returns at most95items per page.
Key Concepts
Section titled “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.