Skip to main content

Email API

Endpoints under the /api/v1/email base path. This page covers the public one-click unsubscribe handler, the authenticated recipient validation (list-hygiene) endpoints, and the IP warm-up ramp endpoints. Sending email goes through the unified Messaging API. Base path: /api/v1/email

Using the SDKs

Python (same call via the SDK’s escape hatch):
The Python SDK is core-scope — it wraps the 8 core resources (messaging, voice, contacts, campaigns, verify, numbers) and reaches everything else through the generic client.request() escape hatch above. See the Python SDK. Returns the typed ApiResponse envelope. See the SDK index at SDK quickstart.

Sending email

Send runs through the Messaging API, not the /api/v1/email base path — this page is where email API users land, so here is the round-trip in full. Two shapes, same pipeline: the unified send with an explicit channel, and the email-native route. Unified send (POST /api/v1/messages) with channel: "email":
Response 202:
The email-native route (POST /api/v1/messages/email) carries the email-specific fields directly — subject plus html/text, reply_to, cc, bcc, and attachments:
Either shape returns the same accepted-for-delivery envelope shown above (id, status, channel). Delivery status then moves through the sent → delivered lifecycle; any volume above a warm-up cap waits as queued. On the unified send the email subject travels in subject; on the bulk send (POST /api/v1/messages/bulk) it travels in each row’s metadata.subject. Full field reference for every messaging route: Messaging API.

Unsubscribe

The public one-click unsubscribe surface that the List-Unsubscribe and List-Unsubscribe-Post headers in outbound email reference. RFC 8058 compliant. Authentication: HMAC-token (embedded in URL). Not Clerk-authenticated — these URLs are designed to be opened by the recipient’s mail client without any session. You don’t typically call these endpoints from your own application — Orbit injects the URLs into outbound email automatically.

Recipient validation

Real-time recipient list-hygiene checks (SendGrid Email Validation API parity). Score an address before you send to it: syntax, disposable-domain and role-account detection, MX presence, a likely-typo correction, and an aggregate 01 risk score with a coarse low/medium/high band. Pure DNS + heuristics — no SMTP mailbox probing and no third-party feed. Addresses are not persisted (transient validation only). Authentication: standard workspace auth (X-API-Key or dashboard session). Both endpoints require an authenticated workspace, but the auth level differs: single-address POST /email/validate additionally requires an owner or admin role, while POST /email/validate/bulk is open to any authenticated workspace member. A non-admin caller hitting POST /email/validate gets a 403.

POST /api/v1/email/validate

Requires an owner or admin role. A workspace member without one of these roles receives a 403. Request body:
Response data:

POST /api/v1/email/validate/bulk

Available to any authenticated workspace member — no owner/admin role required. Validate up to 1000 addresses in one call. Input is de-duplicated case-insensitively before scoring. Request body:
Response data:
Each row carries a single machine-readable reason code — one of deliverable, invalid_syntax, no_mx_record, disposable_domain, role_account, possible_typo, or mx_unknown — so importers can branch deterministically. The summary.risky count is the number of deliverable but flagged rows (disposable / role / typo / non-low band) a sender should review before importing.

IP warm-up

Endpoints that plan, report, and gate an IP-warm-up ramp. When you send from a fresh IP or sender reputation, Orbit caps your daily outbound volume along a ramp that grows from a conservative day-1 seed (default 50/day) toward your target steady-state volume, so a high-volume sender ramps deliberately instead of spiking a cold IP. Volume above today’s cap is queued server-side until the cap resets — it is never authorised above the ceiling. All three endpoints are read-only, tenant-isolated, and require an operator role (owner / admin / developer). All three accept the same query parameters: targetDailyVolume (required), plus optional startingVolume (day-1 seed, default 50/day) and growthFactor (per-day multiplier between 1.05 and 4, default 1.5). warmup-enforcement takes one additional required parameter: requested (the batch size you want to send).

GET /api/v1/email/warmup-plan

Compose the ramp before you commit to a send cadence. To reach a steady 5,000 emails/day from a 50/day seed at the default 1.5x growth:
Response data:
Each day’s maxVolume is that day’s send ceiling; cumulative is the running total across the ramp. truncated is false when the ramp reaches its target within 60 days — a true value means the inputs (high target, shallow growth) need a growth factor closer to 4, not a longer schedule.

GET /api/v1/email/warmup-status

Track the live position on that same ramp:
Response data:
rampState is the verdict to branch on: ramping (reputation healthy or not yet scored — today’s cap matches the plan), holding (reputation poor — cap halved), or throttled (reputation critical — cap quartered). When the state is holding or throttled, todayCap is below baseCap and feedbackReason explains why. Response data field reference:

GET /api/v1/email/warmup-enforcement

Pre-flight a batch against today’s headroom before dispatching it. If today is on the plan above with 40 already sent, a 2,000-email batch gets split:
Response data:
A whole-batch-fit answers decision: "allow" with deferred: 0 and retryAfter: null; a spent cap answers "block" with accepted: 0 and retryAfter pointing at the next UTC day boundary. Response data field reference: A partial or block verdict is the same gate that holds email rows at queued in the Delivery Log once the day’s cap is spent — see Troubleshooting: message stuck in queued.

Integration recipe: validate, then send only the clean rows

Pair POST /api/v1/email/validate/bulk with the unified send so an importer never burns wallet credit or sender reputation on dead addresses:
Rows the bulk verdict flags (disposable_domain, role_account, possible_typo, non-low risk) land in summary.risky — review those per your sending policy before importing; rows with valid: false (invalid_syntax, no_mx_record) are dropped outright. During an active warm-up ramp, gate step 3 on warmup-enforcement so the batch does not outpace today’s cap.

See also