API Reference

The API contract is published as four OpenAPI 3.0 documents, one per functional area, each at version 1.0.0:

Document Tags Operations
Email.yaml Email, OTP, Messages 7
Contacts.yaml Contacts, Lists & Segments, Suppressions, Custom Fields 32
Campaigns.yaml Templates, Assets, Campaigns, Automations 31
Account.yaml Domains, Senders, Webhooks, Billing, Reports, Auth 26
Total 96

Open any of the files in an OpenAPI viewer, such as Swagger UI or ReDoc, to browse request parameters, request bodies, response schemas and error responses for each operation.

All paths on this page are relative to the base URL:

https://emails.routeon.io/api/v1

Authentication

Every request carries an API key of your account as a bearer token:

Authorization: Bearer <API_KEY>

Create and revoke API keys in the customer portal at https://emails.routeon.io, under "API keys". The full key is shown only once, when you create it; store it in your secret manager. A key only ever sees and acts on the data of the account it belongs to.

curl https://emails.routeon.io/api/v1/me 
  -H "Authorization: Bearer <API_KEY>"

GET /me accepts any valid key and is a quick way to check that a key works.

A missing or malformed Authorization header, or an unknown or revoked key, answers 401 with meta.reason: UNAUTHORIZED and meta.details.code set to missing_authorization_header or api_key_unknown.

Scopes

Each key is created with one or more scopes. The key must hold every scope an operation requires; each operation lists its required scopes in the security section of its YAML document.

Scope Grants
domains:read Read sending domains, DNS records, warmup progress, sender profiles and sender routes
domains:write Register, verify, re-provision and remove domains; control warmup; create, change and delete sender profiles and routes
contacts:read Read contacts, contact exports, import jobs, lists, segments, suppressions and custom fields
contacts:write Create, change, erase and delete contacts; import contacts; record custom events; manage lists, segments, suppressions and custom fields
templates:read Read templates, template versions, per-link check results and stored assets
templates:write Create, edit and archive templates; preview, render and verify them; upload, confirm and delete assets
campaigns:read Read campaigns and their time series, automation enrollments and the reports overview
campaigns:write Create, edit, preview, pause, resume, cancel and delete campaigns; create recipient uploads; enroll contacts into automations
email:send Send transactional email (single and batch), cancel a scheduled transactional email, schedule and launch campaigns
otp:send Request one-time passcodes
otp:verify Verify one-time passcodes
messages:read Query messages and read a message’s status and events
webhooks:read List and read webhook subscriptions
webhooks:write Create, update and delete webhook subscriptions
billing:read Read the credit balance, invoices and credit activity; download billing statements

POST /templates/{id}/test-send sends a real email, so it requires both templates:write and email:send.

A request made with a key that lacks a required scope, or a request to a route that is not part of the API-key surface (for example, a customer-portal-only route), answers 403 with meta.reason: FORBIDDEN and no meta.details.code. A 403 that does carry a code (such as domain_not_verified or recipient_suppressed) is a business rule refusal, not a permissions problem.

Response envelope & errors

Every JSON response, success or error, uses the same envelope. http_code always equals the HTTP status of the response. A successful send:

{
  "status": "success",
  "http_code": 202,
  "payload": {
    "message_id": "b3b6b0f0-2f0e-4b8a-9c7e-6b6b1f0a0e2d",
    "send_at": null
  },
  "meta": {}
}

An error keeps the same shape. payload is always an empty array and the machine-readable reason is in meta. For example, POST /transactional/send with an invalid to address:

{
  "status": "error",
  "http_code": 422,
  "payload": [],
  "meta": {
    "reason": "VALIDATION_ERROR",
    "validation_error": [
      { "field": "to", "message": "Invalid email" }
    ]
  }
}

meta fields you will encounter:

Field Appears on Meaning
reason errors Machine-readable error class: VALIDATION_ERROR, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, CONFLICT, GONE, RATE_LIMITED, INSUFFICIENT_CREDITS, CREDIT_PROTECTION, PAYLOAD_TOO_LARGE, DAILY_LIMIT_EXCEEDED, INTERNAL_ERROR
validation_error[] 422 validation errors One { field, message } entry per failed rule. An empty field means the message applies to the request as a whole; show it as-is
details some errors A more specific machine-readable cause in details.code (for example credit_protection, otp_expired, invalid_state, transactional_daily_limit_exceeded), sometimes with numeric context such as a limit. Branch on details.code when it is present
per_page cursor-paginated lists The page size applied to this response
cursor cursor-paginated lists Opaque token for the next page. It is omitted on the last page, so its absence ends the walk. Pass it back as the cursor query parameter

Cursor-paginated lists (domains, contacts, messages, campaigns and others) always return per_page and return cursor only when a next page exists. Lists that are not paginated, such as suppressions, custom fields, templates, webhooks and GET /contacts/{id}/lists, return the whole list with an empty meta: {}. No list returns page numbers or total counts.

Two operations do not return a JSON envelope on success:

  • DELETE /webhooks/{id} answers 204 No Content with an empty body.
  • GET /billing/statement returns a text/csv file with a Content-Disposition: attachment header. Its errors, such as a period that is too long (statement_period_too_long) or too many rows (statement_too_large), are ordinary error envelopes.

Every authenticated request is subject to rate limiting. A throttled request answers 429 with meta.reason: RATE_LIMITED, meta.details.code: rate_limited, a Retry-After header, and X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers. The limits themselves are described in Limits, and the full list of error codes in Errors.

Endpoints by tag

Email.yaml (7 operations)

Email

Method Path Summary
POST /transactional/send Send a transactional email
DELETE /transactional/{message_id} Cancel a scheduled transactional email
POST /transactional/send-batch Send a batch of transactional emails

OTP

Method Path Summary
POST /otp/request Request a one-time passcode
POST /otp/verify Verify a one-time passcode

Messages

Method Path Summary
GET /messages Query messages
GET /messages/{id} Get message delivery status and events

Contacts.yaml (32 operations)

Contacts

Method Path Summary
GET /contacts List/search contacts
POST /contacts Create a contact
GET /contacts/{id} Get a contact
PATCH /contacts/{id} Update a contact
DELETE /contacts/{id} Permanently delete a contact
POST /contacts/{id}/erase GDPR-erase a contact (anonymize and permanently suppress the address)
GET /contacts/{id}/lists List the static lists this contact belongs to
GET /contacts/{id}/export Export everything held about one contact (GDPR access request)
POST /contacts/import Bulk import contacts (async job)
GET /contacts/import/{job_id} Get import job status and summary
POST /events Record a custom event for a contact

Lists & Segments

Method Path Summary
GET /lists List static lists
POST /lists Create a static list
POST /lists/{id}/members Add/remove list members
GET /lists/{id} Get a single static list
DELETE /lists/{id} Soft-delete a static list
POST /lists/{id}/delete-with-contacts Delete a static list and hard-delete its exclusive members (async)
GET /lists/delete-jobs/{job_id} Get the status of a list-deletion job
GET /segments List segments
POST /segments Create a segment (filter)
GET /segments/{id}/preview Preview matching count + sample
GET /segments/{id} Get a single dynamic segment
PATCH /segments/{id} Update a dynamic segment (name and/or filter)
DELETE /segments/{id} Soft-delete a dynamic segment
POST /segments/preview Preview a filter without saving

Suppressions

Method Path Summary
GET /suppressions List suppression entries
POST /suppressions Add a suppression entry
DELETE /suppressions/{id} Remove a suppression entry

Custom Fields

Method Path Summary
GET /custom-fields List custom fields
POST /custom-fields Define a custom field
PATCH /custom-fields/{id} Update a custom field’s label
DELETE /custom-fields/{id} Delete a custom field

Campaigns.yaml (31 operations)

Templates

Method Path Summary
GET /templates List templates
POST /templates Create a template
GET /templates/{id} Get a template (latest version)
PATCH /templates/{id} Edit a template (creates a new version)
DELETE /templates/{id} Archive a template
POST /templates/{id}/test-send Send a test email
POST /templates/preview-source Preview unsaved template source
GET /templates/{id}/versions List template versions
GET /templates/{id}/versions/{version}/links Per-link anti-fraud results for one template version
POST /templates/{id}/render Render a template version with sample/contact data
POST /templates/{id}/verify Read the moderation verdict for one template version

Assets

Method Path Summary
GET /assets List this tenant’s stored email assets
POST /assets/presign Reserve an asset and get a direct-upload URL
POST /assets/{id}/confirm Validate an uploaded asset and publish its CDN URL
DELETE /assets/{id} Delete an unreferenced asset

Campaigns

Method Path Summary
GET /campaigns List campaigns
POST /campaigns Create a campaign
POST /campaigns/uploads Create an ephemeral recipient upload
GET /campaigns/{id} Get a campaign + stats
PATCH /campaigns/{id} Edit a draft campaign
DELETE /campaigns/{id} Permanently delete a draft campaign
GET /campaigns/{id}/series Time-series for a single campaign (per-day status + engagement counters)
POST /campaigns/{id}/preview Preview audience size and sample recipients
POST /campaigns/{id}/conflicts Detect recipients receiving >1 email on the campaign’s target send-day
POST /campaigns/{id}/schedule Schedule a blast
POST /campaigns/{id}/send Launch a campaign now
POST /campaigns/{id}/pause Pause a campaign
POST /campaigns/{id}/resume Resume a campaign
POST /campaigns/{id}/cancel Cancel a campaign

Automations

Method Path Summary
POST /automations/{id}/enroll Enroll a contact into an automation
GET /automations/{id}/enrollments List enrollments for an automation

Account.yaml (26 operations)

Domains

Method Path Summary
GET /domains List sending domains
POST /domains Register a sending domain
GET /domains/{id} Get domain detail + required DNS records
DELETE /domains/{id} Remove a domain (soft delete)
POST /domains/{id}/verify Trigger DNS verification
POST /domains/{id}/provider Change (or re-provision) the domain’s mail provider
GET /domains/{id}/warmup Get warmup schedule & progress
PATCH /domains/{id}/warmup Pause/resume/override warmup

Senders

Method Path Summary
GET /senders List sender profiles
POST /senders Create a sender profile
GET /senders/routes List source-type routes
PUT /senders/routes Replace the full route table
PATCH /senders/{id} Update a sender profile
DELETE /senders/{id} Delete a sender profile

Webhooks

Method Path Summary
GET /webhooks List webhook subscriptions
POST /webhooks Create a webhook subscription
GET /webhooks/{id} Get a webhook subscription
PATCH /webhooks/{id} Update a webhook subscription
DELETE /webhooks/{id} Delete a webhook subscription

Billing

Method Path Summary
GET /billing/balance Get credit balance
GET /billing/invoices List invoices
GET /billing/activity Credit activity feed
GET /billing/activity/messages Messages behind one activity group
GET /billing/statement Download a CSV billing statement

Reports

Method Path Summary
GET /reports/overview Get tenant-wide email reports overview

Auth

Method Path Summary
GET /me Current principal

Where to go next