RefreshDocsConsole →

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

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.

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

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.