API reference
Management
Add a sending domain, mint an app key and register a webhook over the API, with an ADMIN key.
The management endpoints provision a sending setup without the console: add a domain, read or publish its DNS records, register its sending identity, set its stream, mint the key for an app, and register the webhook that reports bounces. The CLI and the MCP server expose all of them.
The ADMIN key
Every endpoint on this page needs a key with the admin permission. Two rules make that tier safe to hand to a script.
An ADMIN key cannot mint another ADMIN key. (One exception exists: the partner API gives a new client organization its first ADMIN key. It is not available through POST /keys.) POST /keys answers 403 admin_key_not_mintable for
permission: "admin". POST /keys/{id}/rotate refuses an ADMIN key for the same reason: its replacement would carry
the same tier. Create and rotate ADMIN keys in the console, under API keys, as an organization admin.
The one exception is the partner API: it gives a new client organization its first key, which can be ADMIN. That key belongs to the new organization only, and the rule above still applies to it.
A domain-restricted key cannot widen its reach. It may grant only domains it already holds. It may not mint an unrestricted key, and it may not add a new domain at all.
The API also refuses a TEST key at ADMIN with 403 test_key_not_allowed. Management calls change live organization
state, so there is nothing to simulate.
Each management write lands in the audit log of the organization, naming the key that made it rather than a user.
Domains
POST /domains mints the DKIM pair, computes the expected DNS records and queues the first verification run. The
response already carries the records, so a manual-DNS setup never needs the console:
f5send domains create --name mail.example.com --dns-provider manual
A domain starts transactional. Under stream isolation a transactional domain refuses every bulk kind — digest.*,
broadcast.*, sequence.* and bulk.* — with 422 stream_mismatch. Send those from a separate bulk domain:
f5send domains create --name news.example.com --stream bulk
Then publish the records. On Cloudflare zones of an internal organization the platform can write them for you; anywhere else, read them and publish them with your own credentials:
f5send domains publish <id> # Cloudflare, internal organizations
f5send domains get <id> # records[] to publish yourself
f5send domains register-identity <id> # SES identity, DKIM selectors, MAIL FROM
f5send domains verify <id> # check now, rather than waiting for the cadence
GET /domains/{id} reports status and, per record, both value (what to publish) and observed (what the last run
resolved), which is what to read when a domain stays pending.
Delete a domain
DELETE /domains/{id} permanently deletes a domain that never sent mail, for example a misspelled one. It returns
204 with no body:
f5send domains delete <id>
The platform first removes the Cloudflare records it published and the SES identity in each region. Then it deletes the DKIM keys, records, suppressions and daily metrics of the domain. DMARC reports and inbound mail stay, with no domain.
A worker job removes the tracking alias about two minutes later.
The call refuses with 409 domain_in_use when the domain has messages, broadcasts or signup forms. It also refuses
when an active key or an automation uses the domain. The message names each blocking key and automation. A domain
that sent mail stays; disable it in the console instead.
Revoke the blocking keys first. The platform never edits the domain list of a key. An empty list means every domain, so a key restricted to this domain only would become unrestricted.
A domain-restricted key gets 403 forbidden_domain. If SES or Cloudflare refuses, the call returns
422 domain_cleanup_failed. The domain stays, disabled, and you can retry.
A send can start while the platform removes the identity and records. Then the call returns 409 domain_in_use. The
message lists what the platform already removed. The domain stays disabled until you register and publish it again.
DKIM rotation, MTA-STS, the DMARC policy ramp and transport selection are not on the API. They change how live mail authenticates and are reversible only by hand, so they stay in the console next to the current record state.
Keys
f5send keys create --name shop-prod --permission send --mode live --domain-id <domain-id>
f5send keys rotate <id> # replacement now, old token valid for 24 h
f5send keys revoke <id> # immediate
A key is test unless you pass --mode live. A Test key simulates sends and never delivers.
The token appears once, in the response. The database keeps only its prefix and a SHA-256 hash, so a lost token needs
a replacement rather than a lookup. Scope the key for each app to its own domain with --domain-id. A key without that
restriction may send from every domain the organization owns.
Webhooks
f5send webhooks create --url https://example.com/api/webhooks/f5send \
--event email.bounced --event email.complained
Pass no --event to receive every event. The signing secret is in the create response and never readable again; verify
deliveries with verifyWebhookSignature from the SDK. See webhook payloads for the event
bodies and the signature scheme.
Topics and segments
A topic is the subscribable unit that contacts and broadcasts reference by slug — one topic per thing a recipient can opt out of, rather than one list per audience:
f5send topics create --slug job-alerts --name "Job alerts" --default-opt-in false
f5send contacts subscribe jane@example.com --topic job-alerts
default_opt_in: true means a contact with no subscription row for that topic counts as subscribed. A segment is a saved
filter and is what POST /broadcasts takes as segment_id; the create response carries contact_count so you can check
the filter before pointing a broadcast at it:
f5send segments create --name "Active seekers" --filter '{"all":[{"status":"SUBSCRIBED"},{"topic":"job-alerts","state":"SUBSCRIBED"}]}'
Organization
f5send organization get
f5send organization update --postal-address "1 Main St, Lexington NC 27292"
Set the postal address before sending bulk mail. The CAN-SPAM and CASL footer carries the organization name and this address. A missing address drops the footer from the message entirely.
Provisioning an app end to end
export F5SEND_API_KEY=f5_live_… # an ADMIN key from the console
id=$(f5send domains create --name mail.example.com --compact | grep -o '"id":"[^"]*' | cut -d'"' -f4)
f5send domains get "$id" # publish records[] in your DNS
f5send domains register-identity "$id"
f5send domains verify "$id" # repeat until status is "verified"
f5send keys create --name myapp-prod --permission send --domain-id "$id"
f5send webhooks create --url https://example.com/api/webhooks/f5send \
--event email.bounced --event email.complained
Give the app the key from the second-to-last step, not the ADMIN key.
Partner API: client organizations
A platform that serves its own customers through Refresh can create one client organization for each customer. Only an
unrestricted, live ADMIN key of an internal organization can call these endpoints. All other keys get
403 forbidden. Client organizations cannot create organizations, so a customer cannot create more customers.
curl -X POST https://api.f5send.com/api/v1/organizations \
-H "Authorization: Bearer $F5SEND_PARTNER_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Acme Support", "slug": "acme-support", "alert_email": "ops@partner.example", "api_key": { "name": "helpdesk", "permission": "admin" } }'
| Field | Notes |
|---|---|
name |
Required. 1–120 chars. |
slug |
Required. 2–60 chars, lowercase letters, digits and single hyphens. Unique across the platform: a used slug answers 409 slug_taken. |
alert_email |
The address for the default alert rules (bounce, complaint, provider block, delivery silence). Default: the first admin of your organization. |
api_key.name |
Default Partner key. |
api_key.permission |
admin (default), full or send. The key is always live. Only an admin key can add domains and webhooks in the new organization. |
The response is 201 with { "organization": { … }, "api_key": { …, "token": "f5_live_…" } }. The token appears once.
Store it now. The platform keeps only its hash, so a lost token needs a console admin.
The new organization starts like a console sign-up:
- Kind
client, planfree, the Free daily cap, the default alert rules, no members. - No Trust & Safety approval, so the platform reviews its first messages like the mail of any new organization.
parent_organization_idnames your organization.- Your audit log records
org.create_child. The log of the new organization recordsorg.createandapi_key.create, with your key as the actor.
Next, use the new key to add the sending domain and the webhook of the customer, as in the section above.
GET /organizations lists the organizations that your organization created, newest first, with limit (1–100) and
cursor. An organization can create at most 25 organizations per UTC day; more answer 429 organization_quota_exceeded
with Retry-After. A parent on a Trust & Safety review hold gets 403 under_review.
Revoke or replace a client organization's keys
Nobody can sign in to a client organization that the partner API created, so you hold the control. POST /organizations/{id}/revoke-keys revokes every live key of an organization that your organization created. You do not
need the token. It still works during a review hold or a sending pause on your organization. For any other organization it answers 404.
curl -X POST https://api.f5send.com/api/v1/organizations/$CHILD_ID/revoke-keys \
-H "Authorization: Bearer $F5SEND_PARTNER_ADMIN_KEY" -H "Content-Type: application/json" \
-d '{ "replace_with": { "name": "helpdesk-2", "permission": "admin" } }'
The body is optional. With replace_with, the same call creates one new live key. Its token appears once in
api_key.token. Without it, api_key is null and the organization has no key until you replace one.
A paused organization (sending_paused_at set) cannot create organizations: POST /organizations answers
403 sending_paused.
Metadata and webhook fields
The new message_id and metadata fields on email.* webhooks are additive and can be null. An older worker omits
them, so treat a missing field like null. Webhooks go only to the endpoints of the organization that sent the email.
metadata counts as message content. When body retention is 0, the platform deletes it with the body, and API logs keep
only its size. Do not put secrets in it.