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
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":
202:
POST /api/v1/messages/email) carries the
email-specific fields directly — subject plus html/text, reply_to,
cc, bcc, and attachments:
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 theList-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 aggregate0–1 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 anowner or admin role. A workspace member without one of these
roles receives a 403.
Request body:
data:
POST /api/v1/email/validate/bulk
Available to any authenticated workspace member — noowner/admin role
required.
Validate up to 1000 addresses in one call. Input is de-duplicated
case-insensitively before scoring. Request body:
data:
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:data:
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: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:data:
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
PairPOST /api/v1/email/validate/bulk with the unified send so an importer
never burns wallet credit or sender reputation on dead addresses:
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
- Messaging API → Send email
- Troubleshooting: message stuck in queued
- Channels → Email
- Compliance → Preference Center (for richer per-channel + per-topic preferences)