Skip to content

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.

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

FormatLink
YAMLrest-api-v1.yaml

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

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}.

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:

Terminal window
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.

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 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.
StatusMeaningRetry?
400The request can’t be processed as sent.No — fix the request.
401The 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.
403Actions on a piece in the request are blocked. The piece’s attributes include actions-blocked-no-reverification or actions-blocked-document-expired.No.
404The 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.
409The request conflicts with the resource’s current state, for example an operation already in progress.Only after checking the resource’s state.
422The request failed validation; see fields.No — fix the request.
429Rate limit exceeded; see Rate limiting.Yes, after the 5-minute block ends.
5xxUnexpected 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.

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.

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.
  • 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.