Documentation
Everything Fromly does through its interface, it does through an API: reading contacts and events, drafting emails, and sending. The same key works for the HTTP API, the CLI and the MCP server.
Authentication
Every request carries an API key as a bearer token. Keys are created in Settings → API and shown once.
curl https://api.fromly.io/v1/automations \
-H "Authorization: Bearer fk_live_…"A key that is absent, malformed, unknown, revoked or expired gets the same 401 with the same body. That is deliberate: distinguishing them would tell someone probing which of their guesses was closest.
Permission levels
A key is given one of three levels when it is created. Each one contains everything the level below it can do.
| Level | What it can do |
|---|---|
| Read only | Can read everything. Cannot change or send anything. |
| Read and draft | Can also create and edit contacts, emails, campaigns and flows, and record events. Cannot send. |
| Full access | Can also send campaigns and transactional emails, activate flows, tag contacts and change their lists, send test emails and delete emails. |
| Transactional only | Can send transactional emails and read templates, sent emails and domains. Cannot send campaigns or change anything. |
Four things put mail in someone’s inbox, and only the highest level can do any of them: sending a campaign, activating a flow, sending a test email, and tagging a contact. That last one is the least obvious — writing a tag records a tag.added event, which can enrol the contact into a flow that is already live.
Permissions in full
Each level is stored as the list of permissions it grants, so a key keeps working unchanged when a new permission appears — it simply does not have it.
| Permission | Grants | Read only | Read and draft | Full access | Transactional only |
|---|---|---|---|---|---|
contacts:read | Read contacts and their attributes. | ✓ | ✓ | ✓ | — |
contacts:write | Create and update contacts. | — | ✓ | ✓ | — |
contacts:tag | Add or remove tags and list memberships, which can enrol a contact into a live flow. | — | — | ✓ | — |
events:read | Read the event stream. | ✓ | ✓ | ✓ | — |
events:write | Record product events. These can trigger live automations. | — | ✓ | ✓ | — |
emails:read | Read the email library. | ✓ | ✓ | ✓ | ✓ |
emails:write | Create and edit emails. | — | ✓ | ✓ | — |
emails:delete | Delete an email from the library. | — | — | ✓ | — |
emails:test | Send a test email to an arbitrary address. | — | — | ✓ | — |
broadcasts:read | Read campaigns and their results. | ✓ | ✓ | ✓ | — |
messages:read | Read sent emails and what became of them: delivered, opened, bounced. | ✓ | ✓ | ✓ | ✓ |
domains:read | Read the sending domains and their senders. | ✓ | ✓ | ✓ | ✓ |
workspace:read | Read the workspace this key or connection reaches. | ✓ | ✓ | ✓ | ✓ |
broadcasts:write | Create and edit campaign drafts. | — | ✓ | ✓ | — |
broadcasts:send | Send, schedule or resume a campaign. | — | — | ✓ | — |
transactional:send | Send a transactional email to one address. | — | — | ✓ | ✓ |
automations:read | Read flows, their figures and who is in them. | ✓ | ✓ | ✓ | — |
automations:write | Create and edit flows, including live ones. | — | ✓ | ✓ | — |
automations:activate | Turn a flow on, after which it sends on its own. | — | — | ✓ | — |
A request missing a permission gets 403, and the reply names the one it needed — unlike the 401 above, the caller is already known by then, so saying so costs nothing and saves a support ticket.
Rate limits
Counted per key rather than per workspace, so two keys in one workspace never spend each other’s budget.
| Requests | Limit |
|---|---|
| Ordinary requests | 600 per minute |
| Sending a campaign, activating a flow | 10 per minute |
Over the limit is 429 with a Retry-After header in seconds.
Paging
Lists take limit (1–100, default 25) and return a nextCursor. Pass it back as cursor to continue; a nextCursor of null means the last page.
{ "data": [ … ], "nextCursor": "clx…" }Errors
| Status | Meaning |
|---|---|
401 | The key is missing, unusable, revoked or expired. |
403 | The key lacks a permission. The reply names it. |
404 | No such record in this workspace. |
409 | Understood, but the current state refuses it. |
429 | Too many requests. See Retry-After. |
Where to go next
- API reference — every endpoint and the permission it needs.
- MCP — connect an AI assistant to a workspace.