Every call requires Authorization: Bearer <key>. Rotate from workspace settings — old keys are revoked instantly. Server-side only; never ship a live key to the browser.
Base URLhttps://flaxxa.com/api/v1/campaigns
Lists & fields
Lists & fields API
A list holds subscribers and its own fields. Each field has a merge tag (`FIRST_NAME`, …) — the key you use in a subscriber's `fields` and as `{FIRST_NAME}` in an email. Scopes: `campaigns.audience.view` to read, `campaigns.audience.manage` to change.
GET/lists
List lists
Every list with its subscriber counts and open / click rates.
POST/lists
Create list
{ name, description?, from_name?, from_email?, reply_to?, double_opt_in?, company? }. Blank sender fields take the workspace defaults.
GET/lists/{id}
Retrieve list
The list and its settings. /lists/{id}/overview adds its growth and engagement.
PUT/lists/{id}
Update list
Same body as create.
POST/lists/{id}/copy
Copy list
A new list with the same fields and settings.
DELETE/lists/{id}
Delete list
Deletes the list.
GET/lists/{id}/fields
List fields
Each field's label, tag, type and whether it's required.
POST/lists/{id}/fields
Add field
Add a custom field; PUT / DELETE /lists/{id}/fields/{field} to change or remove one, POST …/fields/reorder to reorder.
Subscribers
Subscribers API
People on a list. Status is one of `subscribed`, `unconfirmed` (waiting on double opt-in), `unsubscribed`, `bounced` or `complained`. An address on the workspace blacklist is refused.
{ list_id, email, status?, fields?, tags? }. Without status, a double opt-in list makes them unconfirmed, otherwise subscribed. 422 if the address is already on the list.
GET/subscribers/{id}
Retrieve subscriber
The subscriber, their list's fields and their activity timeline.
PUT/subscribers/{id}
Update subscriber
{ email?, status?, fields?, tags? }.
DELETE/subscribers/{id}
Delete subscriber
Removes them from the list.
POST/subscribers/bulk
Bulk action
{ action: subscribe|unsubscribe|delete|add_tag|remove_tag|copy|move, ids[] (≤ 1000) or all: true with the list filters }.
GET/subscribers/export
Export CSV
The filtered subscribers as a CSV file.
GET/subscribers/tags
Tags in use
Each tag with how many subscribers carry it (?list_id= for one list).
CSV imports
CSV imports API
Bring a list in from a CSV in two calls: upload it, then map its columns and start. The import runs in the background — poll it for progress.
POST/lists/{id}/imports
Upload CSV
multipart file (csv, up to 50 MB). Returns the import with its column headers and sample rows.
POST/imports/{id}/start
Map and start
{ mapping: { "0": "email", "1": "field:FIRST_NAME", "2": "tags" }, update_existing? }. Column index → email, tags or field:<TAG>.
GET/imports/{id}
Import progress
Status and counts: imported, updated, skipped.
Segments
Segments API
A saved filter on one list (by field, tag, status, activity). Send a campaign to a segment instead of the whole list.
GET/segments
List segments
Every segment with its list and size.
GET/lists/{id}/segment-options
Condition options
The fields and operators a segment on that list can use.
POST/segments/preview
Preview
{ list_id, match: all|any, conditions[] } → how many match, without saving.
Its conditions and size. PUT to change, DELETE to remove.
Signup forms
Signup forms API
Embedded, popup and hosted forms that add people to a list. Scopes as for lists.
GET/forms
List forms
Every form with its list, type and status.
POST/forms
Create form
{ name, list_id, type, design? } — design is blank or one of GET /forms/designs.
GET/forms/{id}/stats
Form stats
Views, signups and conversion.
GET/forms/{id}/embed
Embed code
The snippet / link to put the form on your site.
POST/forms/{id}/publish
Publish
Make it live; /unpublish takes it down, /duplicate copies it.
Email campaigns
Email campaigns API
A campaign starts as a `draft`. Give it a subject, a from address, content (`html` or `plain`) and one or more audiences (`{ list_id, segment_id? }`), then send or schedule it. Statuses: draft, scheduled, sending, paused, sent, failed, cancelled. Scopes: `campaigns.campaign.view` / `.manage`, and `campaigns.campaign.send` to send.
GET/email/campaigns
List campaigns
Filter: status, q; per_page ≤ 100.
POST/email/campaigns
Create campaign
{ name?, type?: regular|plain }. Starts from the workspace's sender and tracking settings.
GET/email/campaigns/{id}
Retrieve campaign
Everything about it, with its audiences.
PUT/email/campaigns/{id}
Update campaign
name, subject, preheader, from_name, from_email, reply_to, html, plain, template_id, track_opens, track_clicks, audiences[]. Only while draft, scheduled or paused; audiences are fixed once sending starts.
POST/email/recipient-count
Count recipients
{ audiences[] } → the deduplicated number of subscribed people.
Starts sending. 422 with the failing check if preflight isn't ready.
POST/email/campaigns/{id}/schedule
Schedule
{ scheduled_at } — a time in the future (ISO 8601).
POST/email/campaigns/{id}/pause
Pause / resume / cancel
Pause a sending campaign; …/resume carries on; …/cancel unschedules a scheduled one or stops a sending one for good.
POST/email/campaigns/{id}/duplicate
Duplicate
A new draft with the same content and audiences.
DELETE/email/campaigns/{id}
Delete campaign
Pause or cancel a sending campaign first.
Reports
Reports API
Read how a campaign did, and who it went to.
GET/email/campaigns/{id}/report
Campaign report
Sent, delivered, opened, clicked, bounced, complained and unsubscribed, with rates.
GET/email/campaigns/{id}/recipients
Recipients
Who it went to and what each did; /recipients/export as CSV.
Automations
Automations API
Emails that go out on their own when something happens on a list — someone subscribes, a date comes round, and so on. Build the flow, then activate it. Scopes: `campaigns.automation.view` / `.manage`.
GET/automations
List automations
Each with its list, trigger, status and counts.
GET/automations/options
Builder options
The triggers and steps available.
POST/automations
Create automation
{ name, list_id, trigger, flow? }.
PUT/automations/{id}/flow
Save flow
{ flow: { steps[] } } — the emails, waits and conditions.
POST/automations/{id}/activate
Activate / pause
Turn it on; …/pause stops new runs.
GET/automations/{id}/stats
Stats and runs
Per-step numbers; /runs lists who went through it.
Your sending and tracking domains, and the addresses and domains never to email. Scopes: `campaigns.sending.view` / `.manage`.
GET/sending/domains
Sending domains
Each domain with its DNS records and verification state.
POST/sending/domains
Add domain
Returns the DNS records to publish; POST …/domains/{id}/verify once they're live.
GET/sending/tracking-domains
Tracking domains
Branded open / click links; POST to add, …/verify, …/default.
GET/sending/blacklist
Blacklist
Blocked addresses (q, per_page).
POST/sending/blacklist
Block addresses
Add addresses; POST /sending/blacklist/delete to unblock.
Webhooks
Webhooks API
POST /api/v1/webhooks { url, subscribed_events[] } — `app.campaigns.*` subscribes to every Campaigns event. Each delivery carries X-Flaxxa-Event and X-Flaxxa-Signature (HMAC-SHA256 of the raw body with your endpoint secret). The payload: tenant_id, message_id, campaign_id or automation_id, subscriber_id, list_id, contact_id, email, occurred_at.
POST/../webhooks
Subscribe
Scope workspace.webhooks.manage. The secret is returned once.
DELETE/../webhooks/{id}
Unsubscribe
Stops delivery.
Webhook events
Which webhook events can I subscribe to?
Subscribe via POST /webhooks. Verify X-Flaxxa-Signature on every payload.
app.campaigns.email.delivered
An email from a campaign or automation was handed to the mail server.
app.campaigns.email.opened
A recipient opened it (once per email).
app.campaigns.email.clicked
A recipient clicked a link (once per email); the payload carries the url.
app.campaigns.email.bounced
It bounced; bounce_type is hard or soft. A hard bounce blacklists the address.
app.campaigns.email.complained
The recipient marked it as spam.
app.campaigns.email.unsubscribed
The recipient unsubscribed from its link.
Errors
What errors can the API return?
All errors return a JSON envelope: { error: { code, message, details } }. Retry idempotent calls on 5xx with exponential backoff.
Code
Name
When it fires
401
unauthenticated
API key missing, expired or revoked.
403
insufficient_scope
The key lacks the scope — e.g. campaigns.audience.manage to add subscribers, campaigns.campaign.send to send.
404
not_found
No list, subscriber or campaign with that id in this workspace.
422
validation_failed
{ message, errors } — e.g. the address is already on the list, it's blacklisted, or a campaign isn't ready to send.
429
rate_limited
120 requests a minute per key.
Conventions
Which rules apply to every request?
Every Flaxxa app's API follows the same authentication, rate limits, idempotency and webhook signing.
Bearer auth
Every API call uses a Bearer token. Keys belong to a workspace and are scoped per app. Create and revoke them from workspace API settings.
Rate limits
Each key can make 120 requests a minute unless the app sets its own limit. Going over returns 429 with a Retry-After header.
Idempotency
Send an Idempotency-Key header on a POST and a retry with the same key within 24 hours returns the first response instead of running again.
Signed webhooks
Every webhook payload carries an X-Flaxxa-Signature HMAC-SHA256 header made with the secret you get when you subscribe. Verify it before processing.
Yes. A REST API at https://flaxxa.com/api/v1/campaigns, called with a workspace API key scoped to Flaxxa Campaigns.
Each key can make 120 requests a minute unless the app sets its own limit. Going over returns 429 with a Retry-After header.
Every webhook carries an X-Flaxxa-Signature header: an HMAC-SHA256 of the payload made with the secret you get when you subscribe. Recompute it and compare before processing.
Send an Idempotency-Key header on the POST. A retry with the same key within 24 hours returns the first response instead of running again.
Get started in minutes
Ship faster with Flaxxa Campaigns.
Create a workspace, make an API key in its settings, and send your first request.