API Reference

The Spreeflo API is organized around REST. Our API has predictable resource-oriented URLs, accepts JSON request bodies, returns JSON responses, and uses standard HTTP response codes, authentication, and verbs.

Authentication

API requests are authenticated using Bearer token authentication where the token is an API key. You create API keys by:

1. Sign into your Spreeflo account

2. Navigate to Settings > API Keys

3. Click 'Add Key'

You must be subscribed to a plan to access the Spreeflo API.

Pass the API Key in the Authorization header as a Bearer token, e.g. Authorization: Bearer <API_KEY>. Below API references include code snippets for how to pass the API key in the Authorization header.

AI clients (MCP)

The same API key also connects your own AI client — Claude, Claude Code, Cursor, and others — to your workspace through Spreeflo's hosted MCP server, which exposes a curated set of tools instead of raw REST endpoints. Deletes, campaign sends, and form publishes are refused over MCP. See the MCP documentation and Connect your AI client.

Contact Emails

Some of the below APIs uses MD5 hashed email addresses to uniquely identify contacts in an audience. Email addresses are personally identifiable information. Passing them in clear text through APIs risks leaking this information to the public and raising compliance issues in many jurisdictions. Hashing provides a way to reference the same contact consistently without sharing the actual email.

Base URL: https://api.spreeflo.com/1.0

Authentication (bearerAuth): Bearer API key authentication

Ping

Ping API is a lightweight and simple API for you to check your API key is valid and able to return API responses.

GET /pingGet ping

Ping to say hi!

Responses

  • 200 — string
  • 401 — Unauthorized

Lists

Lists API for managing audiences, contacts, and tags.

An audience is a list of contacts. Everything else in Spreeflo — campaigns, journeys, segments, forms — belongs to one audience.

GET /listsList audiences

Get paginated list of all audiences

Parameters

  • page (query, required, number (double)) — The page number of the paginated list. Starts at 0.
  • pageSize (query, required, number (double)) — The number of items per page. Maximum is 50.
  • search (query, optional, string) — The search query to filter audiences by name.
  • sortBy (query, optional, updatedAt | createdAt | name)
  • sortDir (query, optional, ASC | DESC)

Responses

  • 200 — Paginated_ContactListResponseDto_
  • 401 — Unauthorized

POST /listsCreate an audience

Creates a new audience with the given name

Request body

  • name (string, required)

Responses

  • 200 — ContactListResponseDto
  • 401 — Unauthorized

DELETE /lists/{audienceId}Deletes an audience

Deletes an audience by its audience id

Parameters

  • audienceId (path, required, string) — The audience id to delete.

Responses

  • 200 — string
  • 401 — Unauthorized

GET /lists/{audienceId}/contactsList all contacts

Get paginated list of all contacts in an audience

Parameters

  • audienceId (path, required, string) — The audience id to get contacts from.
  • page (query, required, number (double)) — The page number of the paginated list. Starts at 0.
  • pageSize (query, required, number (double)) — The number of items per page. Maximum is 50.
  • search (query, optional, string) — The search query to filter contacts by email.
  • tag (query, optional, string) — The tag id to filter contacts by.
  • segment (query, optional, string) — The segment id to filter contacts by.
  • emailSub (query, optional, string) — The email subscription status to filter contacts by.
  • marketing (query, optional, string) — The marketing contact status to filter contacts by. Pass true for marketing contacts and false for non-marketing contacts.
  • identified (query, optional, string) — The identified contact status to filter contacts by. Pass true for identified contacts and false for anonymous contacts.
  • sortBy (query, optional, updatedAt | createdAt | name)
  • sortDir (query, optional, ASC | DESC)

Responses

  • 200 — Paginated_ContactDetailDto_
  • 401 — Unauthorized

PUT /lists/{audienceId}/contactsUpsert a contact

Creates a contact and adds it to an audience. If contact already exist, then updates contact with provided contact attributes

Parameters

  • audienceId (path, required, string) — The audience id to add a contact to.

Responses

  • 200 — ContactDetailDto
  • 401 — Unauthorized

GET /lists/{audienceId}/contacts/{md5HashedEmail}Retrieve a contact

Retrieves a contact by its email address in an audience

Parameters

  • audienceId (path, required, string) — The audience id to get a contact from.
  • md5HashedEmail (path, required, string) — The MD5 hashed email address of the contact to get.

Responses

  • 200 — ContactDetailDto
  • 401 — Unauthorized

PUT /lists/{audienceId}/contacts/{md5HashedEmail}Update a contact

Updates a contact by its email address in an audience

Parameters

  • audienceId (path, required, string) — The audience id to update a contact in.
  • md5HashedEmail (path, required, string) — The MD5 hashed email address of the contact to update.

Request body

  • first_name (string, optional) — The first name of the contact.
  • last_name (string, optional) — The last name of the contact.
  • email (string, optional) — The email address of the contact.
  • email_subscription_status (EmailSubscriptionStatus, optional) — The email subscription status of the contact.
  • is_marketing_contact (boolean, optional) — Whether the contact is a marketing contact.
  • lang (string, optional) — The language preference of the contact.
  • website (string, optional) — The website of the contact.

Responses

  • 200 — ContactDetailDto
  • 401 — Unauthorized

DELETE /lists/{audienceId}/contacts/{md5HashedEmail}Delete a contact

Deletes a contact and removes it from an audience by its email address

Parameters

  • audienceId (path, required, string) — The audience id to get tags from.
  • md5HashedEmail (path, required, string) — The MD5 hashed email address of the contact to get tags from.

Responses

  • 200 — string
  • 401 — Unauthorized

GET /lists/{audienceId}/tagsList all tags

Get paginated list of all tags in an audience

Parameters

  • audienceId (path, required, string) — The audience id to get tags from.
  • page (query, required, number (double)) — The page number of the paginated list. Starts at 0.
  • pageSize (query, required, number (double)) — The number of items per page. Maximum is 50.
  • search (query, optional, string) — The search query to filter tags by name.
  • sortBy (query, optional, updatedAt | createdAt | name)
  • sortDir (query, optional, ASC | DESC)

Responses

  • 200 — Paginated_ContactTagResponseDto_
  • 401 — Unauthorized

POST /lists/{audienceId}/tags/{tagId}Bulk contact tagging

Tags multiple contacts in an audience by their email addresses

Parameters

  • audienceId (path, required, string) — The audience id to bulk tag contacts in.
  • tagId (path, required, string) — The tag id to bulk tag contacts in.

Request body

  • emails (string[], required) — List of contact email addresses to tag or untag.
  • action (add | remove, required) — The action to perform: 'add' to tag contacts, 'remove' to untag contacts.

Responses

  • 200 — BulkContactTaggingDto
  • 401 — Unauthorized

GET /lists/{audienceId}/contacts/{md5HashedEmail}/tagsList all tags of a contact

Get paginated list of all tags of a contact in an audience

Parameters

  • audienceId (path, required, string) — The audience id to get tags from.
  • md5HashedEmail (path, required, string) — The MD5 hashed email address of the contact to get tags from.
  • page (query, required, number (double)) — The page number of the paginated list. Starts at 0.
  • pageSize (query, required, number (double)) — The number of items per page. Maximum is 50.
  • search (query, optional, string) — The search query to filter tags by name.
  • sortBy (query, optional, updatedAt | createdAt | name)
  • sortDir (query, optional, ASC | DESC)

Responses

  • 200 — Paginated_ContactTagResponseDto_
  • 401 — Unauthorized

POST /lists/{audienceId}/contacts/{md5HashedEmail}/tagsAdd or remove tags from a contact

Adds or removes tags from a contact in an audience by its email address

Parameters

  • audienceId (path, required, string) — The audience id to add or remove tags from.
  • md5HashedEmail (path, required, string) — The MD5 hashed email address of the contact to add or remove tags from.

Request body

  • tags (object[], required) — List of tag operations to perform.

Responses

  • 200 — UpdateContactTaggingForContactDto
  • 401 — Unauthorized

POST /lists/{audienceId}/contacts/importImport contacts from a CSV

Imports contacts into an audience from raw CSV content, up to 2MB per request.

The first row is the header row and must include an email column. Every other column is matched to a contact attribute by its header: reserved attributes such as first_name match by name, and any header that does not match an existing attribute is created as a new user defined contact attribute, with its type inferred from the values in that column.

Rows are processed in the background. The response returns as soon as the file is accepted, and tells you how many rows were found — not how many have been imported yet.

Parameters

  • audienceId (path, required, string) — The audience id to import contacts into.

Request body

  • csv (string, required) — The raw CSV content, up to 2MB. The first row is the header row and must include an email column.
  • filename (string, optional) — A name for the import, shown in the app alongside your other imports. Defaults to api-import.csv.
  • emailSubscriptionStatus (EmailSubscriptionStatus, optional) — The email subscription status to apply to every imported contact.
  • isMarketingContact (boolean, optional) — Whether the imported contacts should count as marketing contacts.
  • isOverrideExistingContactIdentities (boolean, optional) — Whether rows that match an existing contact by email update that contact. When false, those rows are skipped. Defaults to true.

Responses

  • 200 — ImportContactsCsvApiResponseDto
  • 401 — Unauthorized

POST /tagsCreate a tag

Creates a tag in an audience. Tag names are unique within an audience.

Parameters

  • audienceId (query, required, string) — The audience id to create the tag in.

Request body

  • name (string, required) — The name of the tag. Must be unique within the audience.

Responses

  • 200 — TagApiResponseDto
  • 401 — Unauthorized

PUT /tags/{tagId}Rename a tag

Renames a tag. Contacts keep the tag — only its name changes. The new name must not already be taken in the same audience.

Parameters

  • tagId (path, required, string) — The tag id to rename.

Request body

  • name (string, required) — The new name of the tag. Must be unique within the audience.

Responses

  • 200 — TagApiResponseDto
  • 401 — Unauthorized

DELETE /tags/{tagId}Delete a tag

Deletes a tag and removes it from every contact that has it.

Journeys that trigger on the tag being removed will run for those contacts, segment rules referencing the tag are cleaned up, and affected segments are recalculated.

Parameters

  • tagId (path, required, string) — The tag id to delete.

Responses

  • 200 — string
  • 401 — Unauthorized

Segments

Segments API for managing segments.

A segment is a saved set of criteria over your contacts. Membership is dynamic: contacts move in and out automatically as their attributes, tags, and activity change. Use segments to target campaigns and journeys, or to filter contacts when listing them.

POST /segmentsCreate a segment

Creates a segment in an audience from a set of criteria.

Membership is worked out as soon as the segment is created and kept up to date from then on. Retrieve the criteria format from GET /format-docs/segment-json-format, read together with segment-builder.

Parameters

  • audienceId (query, required, string) — The audience id to create the segment in.

Request body

  • name (string, required) — The name of the segment.
  • definition (SegmentDefinition, required) — The criteria that decide who is in the segment.

Responses

  • 200 — CreateSegmentApiResponseDto
  • 401 — Unauthorized

PUT /segments/{segmentId}Update a segment

Renames a segment and replaces its criteria. Both name and definition are required — the definition is replaced wholesale, not merged. Membership is recalculated from the new criteria.

Parameters

  • segmentId (path, required, string) — The segment id to update.

Request body

  • name (string, required) — The name of the segment.
  • definition (SegmentDefinition, required) — The criteria that decide who is in the segment. Replaces the existing criteria entirely.

Responses

  • 200 — UpdateSegmentApiResponseDto
  • 401 — Unauthorized

DELETE /segments/{segmentId}Delete a segment

Deletes a segment. The contacts in it are not deleted — only the segment and its membership are removed.

Parameters

  • segmentId (path, required, string) — The segment id to delete.

Responses

  • 200 — string
  • 401 — Unauthorized

Contact attributes

Contact attributes API for managing the fields stored on your contacts.

Alongside the reserved attributes Spreeflo defines (first_name, email, and so on), each audience can hold up to 100 user defined contact attributes. Create them up front here, or let them be created for you the first time you send an unrecognised property when upserting a contact.

POST /contact-attributesCreate a contact attribute

Defines a new user defined contact attribute for an audience.

The attribute key is derived from the name you provide — it is what you use as the property name when upserting contacts and as the variable name when personalising emails. An audience can hold a maximum of 100 user defined contact attributes.

Parameters

  • audienceId (query, required, string) — The audience id to create the contact attribute in.

Request body

  • name (string, required) — The display name of the attribute. Its key — the property name you use when upserting contacts and when personalising emails — is derived from this.
  • dataType (ContactAttributeDataType, optional) — The type of value the attribute holds. Defaults to TEXT.

Responses

  • 200 — ContactAttributeApiResponseDto
  • 401 — Unauthorized

DELETE /contact-attributes/{contactAttributeId}Delete a contact attribute

Deletes a contact attribute, every value your contacts hold for it, and any segment rules that reference it. This cannot be undone.

Reserved attributes (such as first_name or email) and attributes Spreeflo computes for you cannot be deleted.

Parameters

  • contactAttributeId (path, required, string) — The contact attribute id to delete.

Responses

  • 200 — string
  • 401 — Unauthorized

Objects

Objects API for the custom records your business runs on — orders, subscriptions, tickets, accounts.

An object type is the shape (order, with attributes total and status). An object is one record of that type, keyed by your own external id. A relationship links an object to a contact.

Objects earn their keep through that link: once a contact is related to an object, every email and web push sent to them can personalise from it with {{ <typeKey>.<attributeKey> }}, and segments can filter on it. Nothing needs declaring in the template — the attribute resolves by path at send time.

Attributes carry a scope. OBJECT attributes belong to the record itself (an order's total). RELATIONSHIP attributes belong to the pairing (a contact's role on an account) and are set when you relate the contact. Both share one namespace when personalising, so {{ account.role }} resolves either way.

Every endpoint here requires a Pro plan and an audienceId.

GET /objects/typesList object types

Get a paginated list of the object types defined in your workspace.

Requires a Pro plan.

Parameters

  • audienceId (query, required, string) — The audience the object types are scoped to.
  • page (query, optional, number (double)) — The page number of the paginated list. Starts at 0.
  • pageSize (query, optional, number (double)) — The number of items per page. Maximum is 50.

Responses

  • 200 — Paginated_ObjectTypeListItemResponseDto_
  • 401 — Unauthorized

GET /objects/types/{typeKey}Retrieve an object type

Retrieves one object type with its full attribute list — the attribute keys are what you use both when upserting objects and when personalising templates with {{ <typeKey>.<attributeKey> }}.

Requires a Pro plan.

Parameters

  • typeKey (path, required, string) — The key of the object type to retrieve.
  • audienceId (query, required, string) — The audience the object type is scoped to.

Responses

  • 200 — ObjectTypeResponseDto
  • 401 — Unauthorized

PUT /objects/types/{typeKey}Create or update an object type

Creates an object type, or updates the one already at this key.

The key becomes a template namespace: once a type keyed order has an attribute keyed total, any email or web push template can reference {{ order.total }} for the order related to the recipient. Keys must therefore be lowercase, start with a letter, and contain only letters, digits and underscores. contact, spree, ai and var are reserved because they would shadow a built-in namespace, and keys may not start with spree_.

attributes is additive — attributes you leave out are kept, not deleted. An attribute's data_type and scope are fixed once created; sending different ones for an existing key is rejected rather than migrating stored values. Its name can be changed freely.

Up to 50 object types per workspace, each with up to 100 attributes.

Requires a Pro plan.

Parameters

  • typeKey (path, required, string) — The key of the object type to create or update.

Request body

  • audienceId (string, required) — The audience this object type is scoped to.
  • name (string, optional) — A human-readable name shown in the app. Defaults to the type key.
  • description (string, optional) — A short description of what this type represents, shown in the app.
  • attributes (object[], optional) — Attributes to declare. Additive — attributes you leave out are kept. Re-sending an existing key with a different data_type or scope is rejected; its name can be changed.

Responses

  • 200 — ObjectTypeResponseDto
  • 401 — Unauthorized

DELETE /objects/types/{typeKey}Delete an object type

Deletes an object type, every object of that type, and every relationship those objects hold. Templates referencing {{ <typeKey>. }} will render those variables empty. This cannot be undone.

Requires a Pro plan.

Parameters

  • typeKey (path, required, string) — The key of the object type to delete.
  • audienceId (query, required, string) — The audience the object type is scoped to.

Responses

  • 204 — Deleted. No content is returned.
  • 401 — Unauthorized

GET /objects/{typeKey}List objects

Get a paginated list of the objects of one type. The list carries identifiers only — call the single-object endpoint for attribute values.

Requires a Pro plan.

Parameters

  • typeKey (path, required, string) — The key of the object type to list objects for.
  • audienceId (query, required, string) — The audience the objects belong to.
  • page (query, optional, number (double)) — The page number of the paginated list. Starts at 0.
  • pageSize (query, optional, number (double)) — The number of items per page. Maximum is 50.

Responses

  • 200 — Paginated_ObjectListItemResponseDto_
  • 401 — Unauthorized

GET /objects/{typeKey}/{externalId}Retrieve an object

Retrieves one object with its OBJECT-scoped attribute values.

Requires a Pro plan.

Parameters

  • typeKey (path, required, string) — The key of the object type.
  • externalId (path, required, string) — Your own identifier for the object.
  • audienceId (query, required, string) — The audience the object belongs to.

Responses

  • 200 — ObjectResponseDto
  • 401 — Unauthorized

PUT /objects/{typeKey}/{externalId}Create or update an object

Creates an object, or updates the one already at this external id. The external id is yours — an order number, a subscription id, whatever your system already calls this record — so you can upsert without storing a Spreeflo id.

attributes is a flat map keyed by attribute key. Only keys declared on the object type with scope: "OBJECT" are stored; values are capped at 255 characters.

Requires a Pro plan.

Parameters

  • typeKey (path, required, string) — The key of the object type.
  • externalId (path, required, string) — Your own identifier for this object.

Request body

  • audienceId (string, required) — The audience this object belongs to.
  • attributes (object, required) — Attribute values keyed by attribute key. Only keys declared on the object type are stored; each value is capped at 255 characters.

Responses

  • 200 — ObjectResponseDto
  • 401 — Unauthorized

DELETE /objects/{typeKey}/{externalId}Delete an object

Deletes an object and every relationship it holds with contacts. The contacts themselves are not affected.

Requires a Pro plan.

Parameters

  • typeKey (path, required, string) — The key of the object type.
  • externalId (path, required, string) — Your own identifier for the object.
  • audienceId (query, required, string) — The audience the object belongs to.

Responses

  • 204 — Deleted. No content is returned.
  • 401 — Unauthorized

GET /objects/{typeKey}/{externalId}/relationshipsList an object's contacts

Get a paginated list of the relationships an object holds with contacts.

Requires a Pro plan.

Parameters

  • typeKey (path, required, string) — The key of the object type.
  • externalId (path, required, string) — Your own identifier for the object.
  • audienceId (query, required, string) — The audience the object belongs to.
  • page (query, optional, number (double)) — The page number of the paginated list. Starts at 0.
  • pageSize (query, optional, number (double)) — The number of items per page. Maximum is 50.

Responses

  • 200 — Paginated_RelationshipResponseDto_
  • 401 — Unauthorized

PUT /objects/{typeKey}/{externalId}/relationshipsRelate a contact to an object

Links a contact to an object, creating the link or updating its attributes if it already exists.

This link is what makes {{ <typeKey>.<attributeKey> }} resolve for that contact when an email or web push is sent to them. Where a contact is linked to several objects of the same type, a send triggered by one of them uses that object; otherwise the most recently updated link wins.

attributes here sets values on the LINK rather than the object — attributes declared with scope: "RELATIONSHIP", for facts that belong to the pairing rather than either side, such as the contact's role on an account. They share the object's namespace in templates, so {{ account.role }} resolves whether role is object- or relationship-scoped.

Up to 1,000 relationships per contact per type, and 5,000 per contact overall.

Requires a Pro plan.

Parameters

  • typeKey (path, required, string) — The key of the object type.
  • externalId (path, required, string) — Your own identifier for the object.

Request body

  • audienceId (string, required) — The audience the contact and object belong to.
  • email (string, required) — The contact's email address, in clear text. The contact must already exist in the audience.
  • attributes (object, optional) — Values for attributes declared with scope: "RELATIONSHIP" — facts about this pairing rather than about either side.

Responses

  • 200 — RelationshipResponseDto
  • 401 — Unauthorized

DELETE /objects/{typeKey}/{externalId}/relationships/{md5HashedEmail}Unlink a contact from an object

Removes the link between a contact and an object. Neither the contact nor the object is deleted.

The contact is identified twice: by MD5 hash in the path, like the rest of this API, and by the raw address in the email query parameter, which resolving the link actually needs. Both are required and the hash must match the address.

Requires a Pro plan.

Parameters

  • typeKey (path, required, string) — The key of the object type.
  • externalId (path, required, string) — Your own identifier for the object.
  • md5HashedEmail (path, required, string) — The MD5 hashed email address of the contact to unlink.
  • audienceId (query, required, string) — The audience the object belongs to.
  • email (query, required, string) — The contact's email address in clear text. Must hash to the md5HashedEmail path segment.

Responses

  • 204 — Deleted. No content is returned.
  • 401 — Unauthorized

Campaigns

Campaigns API for managing campaigns.

A campaign captures a fixed snapshot of contacts when it starts. It markets to only those contacts through to completion. Ideal when you want to market to everyone who currently matches specific conditions.

Build a campaign by posting a sequence document: the node graph that describes what it sends and in what order. Retrieve that format from GET /format-docs/sequence-json-format.

GET /campaignsList campaigns

Get paginated list of all campaigns

Parameters

  • page (query, required, number (double)) — The page number of the paginated list. Starts at 0.
  • pageSize (query, required, number (double)) — The number of items per page. Maximum is 50.
  • search (query, optional, string) — The search query to filter campaigns by campaign title.
  • sortBy (query, optional, updatedAt | createdAt | title)
  • sortDir (query, optional, ASC | DESC)
  • audienceId (query, optional, string) — The audience id to filter campaigns by audience.
  • status (query, optional, DRAFT | SCHEDULED | ACTIVE | PAUSED | TERMINATED | COMPLETED) — The campaign status to filter campaigns by.

Responses

  • 200 — Paginated_CampaignResponseDto_
  • 401 — Unauthorized

POST /campaigns/sequenceCreate a campaign or journey

Creates a campaign or a journey from a sequence document — the node graph describing what it sends, in what order, and under what conditions. Set type to campaign or journey.

The new campaign or journey is created as a draft so you can review it before it sends anything. Start it with POST /campaigns/{campaignId}/start or POST /journeys/{journeyId}/start.

Retrieve the sequence format from GET /format-docs/sequence-json-format, read together with automation-nodes.

Parameters

  • audienceId (query, required, string) — The audience id the campaign or journey will market to.

Request body

  • name (string, required) — The name of the campaign or journey.
  • type (campaign | journey, required) — Whether to build a campaign (a one-off send to a fixed set of contacts) or a journey (an always-on automation).
  • nodes (object[], required) — The nodes making up the sequence.

Responses

  • 200 — CreateCampaignFromSequenceResponseDto
  • 401 — Unauthorized

GET /campaigns/{campaignId}Get campaign details

Retrieves a campaign by its campaign id

Parameters

  • campaignId (path, required, string) — The campaign id to get details from.

Responses

  • 200 — CampaignResponseDto
  • 401 — Unauthorized

PUT /campaigns/{campaignId}Update a campaign

Updates a campaign’s title, target audience, settings, or UTM campaign name. Pass only the fields you want to change.

target can only be changed while the campaign is still a draft — once a campaign has started, its contacts are fixed.

Parameters

  • campaignId (path, required, string) — The campaign id to update.

Request body

  • title (string, optional) — The name of the campaign or journey.
  • target (object, optional) — Who the campaign markets to. Target either a segment, by contactSegmentId, or a set of criteria, by filter. Set filter to all to target everyone in the audience. Only applies to campaigns that are still drafts — once a campaign has started its contacts are fixed — and is ignored for journeys, where who enters is decided by the trigger nodes.
  • settings (object, optional) — Campaign settings, such as send scheduling and pacing. Replaces the existing settings entirely.
  • utm_campaign (optional) — The utm_campaign value added to links in this campaign, for your own analytics. Set to null to remove it.

Responses

  • 200 — string
  • 401 — Unauthorized

DELETE /campaigns/{campaignId}Delete a campaign

Deletes a campaign by its campaign id

Parameters

  • campaignId (path, required, string) — The campaign id to delete.

Responses

  • 200 — string
  • 401 — Unauthorized

PUT /campaigns/{campaignId}/sequenceReplace a sequence

Replaces the entire node graph of an existing campaign or journey with a new sequence document, in the same format as creating one.

This is a full replacement rather than a merge: any node you leave out is removed. The campaign or journey must be in DRAFT or PAUSED, and the document type must match what you are updating — use journey when the id belongs to a journey.

Parameters

  • campaignId (path, required, string) — The campaign or journey id whose sequence is being replaced.

Request body

  • name (string, required) — The name of the campaign or journey.
  • type (campaign | journey, required) — Whether to build a campaign (a one-off send to a fixed set of contacts) or a journey (an always-on automation).
  • nodes (object[], required) — The nodes making up the sequence.

Responses

  • 200 — CreateCampaignFromSequenceResponseDto
  • 401 — Unauthorized

POST /campaigns/{campaignId}/startStart a campaign

Starts a campaign. Spreeflo takes a snapshot of the contacts that currently match the campaign target and queues the sends for all of them.

This is the same action as pressing start in the app, including the plan checks — sending requires an active subscription.

Parameters

  • campaignId (path, required, string) — The campaign id to start.

Responses

  • 200 — string
  • 401 — Unauthorized

POST /campaigns/{campaignId}/pausePause a campaign

Pauses a campaign by its campaign id

Parameters

  • campaignId (path, required, string) — The campaign id to pause.

Responses

  • 200 — string
  • 401 — Unauthorized

POST /campaigns/{campaignId}/resumeResume a campaign

Resumes a campaign by its campaign id

Parameters

  • campaignId (path, required, string) — The campaign id to resume.

Responses

  • 200 — string
  • 401 — Unauthorized

POST /campaigns/{campaignId}/duplicateDuplicate a campaign

Creates a copy of a campaign, named "<original title> (Copy)", including its node graph and settings.

The copy is created as a draft with its statistics reset, so nothing is sent until you start it.

Parameters

  • campaignId (path, required, string) — The campaign id to duplicate.

Responses

  • 200 — DuplicateCampaignApiResponseDto
  • 401 — Unauthorized

Journeys

Journeys API for managing journeys.

A journey runs continuously. Any new contact that meets its trigger conditions will automatically enter for marketing automation. Ideal for responding to new behaviors or profile updates over time.

Journeys are built from the same sequence documents as campaigns, with type set to journey. Create one with POST /campaigns/sequence and replace its node graph with PUT /campaigns/{campaignId}/sequence.

GET /journeysList journeys

Get paginated list of all journeys

Parameters

  • page (query, required, number (double)) — The page number of the paginated list. Starts at 0.
  • pageSize (query, required, number (double)) — The number of items per page. Maximum is 50.
  • search (query, optional, string) — The search query to filter journeys by journey title.
  • sortBy (query, optional, updatedAt | createdAt | name)
  • sortDir (query, optional, ASC | DESC)
  • audienceId (query, optional, string) — The audience id to filter journeys by audience.
  • status (query, optional, DRAFT | SCHEDULED | ACTIVE | PAUSED | TERMINATED | COMPLETED) — The journey status to filter journeys by.

Responses

  • 200 — Paginated_JourneyResponseDto_
  • 401 — Unauthorized

GET /journeys/{journeyId}Get journey details

Retrieves a journey by its journey id

Parameters

  • journeyId (path, required, string) — The journey id to get details from.

Responses

  • 200 — JourneyResponseDto
  • 401 — Unauthorized

PUT /journeys/{journeyId}Update a journey

Updates a journey’s title, settings, or UTM campaign name. Pass only the fields you want to change.

target does not apply to journeys — who enters is decided by the trigger nodes in the journey itself — and is ignored if sent.

Parameters

  • journeyId (path, required, string) — The journey id to update.

Request body

  • title (string, optional) — The name of the campaign or journey.
  • target (object, optional) — Who the campaign markets to. Target either a segment, by contactSegmentId, or a set of criteria, by filter. Set filter to all to target everyone in the audience. Only applies to campaigns that are still drafts — once a campaign has started its contacts are fixed — and is ignored for journeys, where who enters is decided by the trigger nodes.
  • settings (object, optional) — Campaign settings, such as send scheduling and pacing. Replaces the existing settings entirely.
  • utm_campaign (optional) — The utm_campaign value added to links in this campaign, for your own analytics. Set to null to remove it.

Responses

  • 200 — string
  • 401 — Unauthorized

DELETE /journeys/{journeyId}Delete a journey

Deletes a journey by its journey id

Parameters

  • journeyId (path, required, string) — The journey id to delete.

Responses

  • 200 — string
  • 401 — Unauthorized

POST /journeys/{journeyId}/startStart a journey

Starts a journey. It becomes active and its triggers begin listening, so contacts enter it as and when they meet its conditions — unlike a campaign, no contacts are enrolled up front.

This is the same action as pressing start in the app, including the plan checks — sending requires an active subscription.

Parameters

  • journeyId (path, required, string) — The journey id to start.

Responses

  • 200 — string
  • 401 — Unauthorized

POST /journeys/{journeyId}/pausePause a journey

Pauses a journey by its journey id

Parameters

  • journeyId (path, required, string)

Responses

  • 200 — string
  • 401 — Unauthorized

POST /journeys/{journeyId}/resumeResume a journey

Resumes a journey by its journey id

Parameters

  • journeyId (path, required, string) — The journey id to pause.

Responses

  • 200 — string
  • 401 — Unauthorized

POST /journeys/{journeyId}/duplicateDuplicate a journey

Creates a copy of a journey, named "<original title> (Copy)", including its node graph and settings.

The copy is created as a draft with its statistics reset and its triggers switched off, so nothing runs until you start it.

Parameters

  • journeyId (path, required, string) — The journey id to duplicate.

Responses

  • 200 — DuplicateCampaignApiResponseDto
  • 401 — Unauthorized

Email templates

Email templates API for managing reusable email designs.

Templates hold the subject, preview text, body, and personalisation variables of an email. Marketing templates are sent by the email nodes inside campaigns and journeys, transactional templates are sent through the Transactional API, and opt-in templates are used to confirm double opt-in sign-ups.

Personalisation

Write variables as namespaced paths — {{ namespace.name }} — anywhere in the subject, preview text, or body.

| Namespace | Resolves to | Declare it? |

| --- | --- | --- |

| contact. | a contact attribute, by its key | no |

| <objectType>. | an attribute of a related custom object | no |

| spree. | unsubscribe and opt-in links, sender identity, date parts | no |

| ai. | copy written per recipient at send time | yes — in aiVariables |

| var. | values passed by a transactional send | no (TRANSACTION templates only) |

Only ai. is declared. Everything else resolves against the live schema when the email is sent, so adding a contact attribute makes it usable in every template immediately — there is nothing to map, bind, or keep in sync.

Give any variable a fallback with the Liquid default filter: {{ contact.first_name | default: "there" }}. An unknown path renders empty rather than failing the send.

POST /node-email-templatesCreate an email template

Creates an email template in an audience.

templateFormat is required here: unlike an update there is no stored format to infer the body from, and guessing would risk storing MJML as plain HTML. Send emailBody as an object for the json builder format, or as a string for html and mjml — or leave it out to create an empty template and fill it in later.

Personalise the copy with namespaced variables (see the section notes above). Declare only the AI ones, in aiVariables; every {{ ai.<key> }} the content references must have a declaration, and every declaration must be referenced by the content.

Only marketing, transactional, and opt-in templates can be created here. Retrieve the builder body format from GET /format-docs/email-json-format.

Parameters

  • audienceId (query, required, string) — The audience id to create the template in.

Request body

  • type (EmailTemplateType, required) — What the template is used for. This cannot be changed after the template is created.
  • templateName (string, required) — The name of the template, used to identify it in the app.
  • templateFormat (TemplateFormat, required) — The format emailBody is in. Required, so the body is stored and rendered correctly.
  • emailSubject (string, optional) — The subject line. Personalise it with namespaced variables, e.g. {{ contact.first_name | default: "there" }}.
  • emailPreview (string, optional) — The preview text shown after the subject line in the inbox.
  • emailBody (string | object, optional) — The email body, matching templateFormat: an object for json, a string for html and mjml. Leave it out to create an empty template. See GET /format-docs/email-json-format.
  • aiVariables (AiVariables, optional) — The AI variables this template declares. Only needed if the content references {{ ai. }} — data variables are never declared.
  • utmContent (string, optional) — The utm_content value added to links in this email, for your own analytics. Generated from the template name if you leave it out. Opt-in confirmation emails never carry one.

Responses

  • 200 — UpdateNodeEmailTemplateApiResponseDto
  • 401 — Unauthorized

PUT /node-email-templates/{templateId}Update an email template

Updates an email template. Pass only the fields you want to change; anything you leave out is kept as it is — with one exception: aiVariables replaces the declaration set entirely, so send the full set rather than a patch.

Changing which contact attributes or object attributes the copy references needs nothing beyond the new body — those resolve by path and are never declared.

Only marketing, transactional, and opt-in templates can be updated here. Retrieve the builder body format from GET /format-docs/email-json-format.

Parameters

  • templateId (path, required, string) — The email template id to update.

Request body

  • templateName (string, optional) — The name of the template, used to identify it in the app.
  • emailSubject (string, optional) — The subject line. Personalise it with namespaced variables, e.g. {{ contact.first_name | default: "there" }}.
  • emailPreview (string, optional) — The preview text shown after the subject line in the inbox.
  • emailBody (string | object, optional) — The email body. Send an object for the json builder format, or a string for html and mjml. See GET /format-docs/email-json-format.
  • templateFormat (TemplateFormat, optional) — The format emailBody is in. Defaults to the template’s current format.
  • aiVariables (AiVariables, optional) — The AI variables this template declares. Replaces the existing declarations entirely — send the full set, not a patch. Data variables are never declared, so changing {{ contact. }} usage in the body needs nothing here.

Responses

  • 200 — UpdateNodeEmailTemplateApiResponseDto
  • 401 — Unauthorized

DELETE /node-email-templates/{templateId}Delete an email template

Deletes an email template. Any campaign or journey node that was sending it is detached, so those nodes need a new template before the campaign can run again.

Only marketing, transactional, and opt-in templates can be deleted here.

Parameters

  • templateId (path, required, string) — The email template id to delete.

Responses

  • 200 — string
  • 401 — Unauthorized

POST /node-email-templates/{templateId}/duplicateDuplicate an email template

Creates a copy of an email template. The copy is not attached to any campaign, so you can edit it freely before using it.

Only marketing, transactional, and opt-in templates can be duplicated here.

Parameters

  • templateId (path, required, string) — The email template id to duplicate.

Request body

  • templateName (string, optional) — The name for the copy. Defaults to the original name followed by "(Copy)".

Responses

  • 200 — UpdateNodeEmailTemplateApiResponseDto
  • 401 — Unauthorized

Web push templates

Web push templates API for managing browser notification designs.

A web push template holds the title, body, click-through URL, and imagery of a browser notification, and is sent by the web push nodes inside campaigns and journeys.

POST /node-webpush-templatesCreate a web push template

Creates a web push template in an audience. Use it from the web push nodes inside your campaigns and journeys.

Parameters

  • audienceId (query, required, string) — The audience id to create the template in.

Request body

  • templateName (string, required) — The name of the template, used to identify it in the app.
  • content (WebPushNotificationContent, required) — The notification content.
  • aiVariables (AiVariables, optional) — The AI variables this template declares. Only needed if the title or body references {{ ai. }} — data variables are never declared.
  • utmContent (string, optional) — The utm_content value added to links in this notification. Generated from the template name if you leave it out.

Responses

  • 200 — WebpushTemplateApiResponseDto
  • 401 — Unauthorized

PUT /node-webpush-templates/{templateId}Update a web push template

Updates a web push template. Pass only the fields you want to change; anything you leave out is kept as it is. Note that content is replaced wholesale, so include every content field you want to keep.

Parameters

  • templateId (path, required, string) — The web push template id to update.

Request body

  • templateName (string, optional) — The name of the template, used to identify it in the app.
  • content (WebPushNotificationContent, optional) — The notification content. Replaces the existing content entirely.
  • aiVariables (AiVariables, optional) — The AI variables this template declares. Replaces the existing declarations entirely — send the full set, not a patch. Data variables are never declared.
  • utmContent (optional) — The utm_content value added to links in this notification. Set to null to regenerate it from the template name.

Responses

  • 200 — WebpushTemplateApiResponseDto
  • 401 — Unauthorized

DELETE /node-webpush-templates/{templateId}Delete a web push template

Deletes a web push template. Any campaign or journey node that was sending it is detached, so those nodes need a new template before the campaign can run again.

Parameters

  • templateId (path, required, string) — The web push template id to delete.

Responses

  • 200 — string
  • 401 — Unauthorized

POST /node-webpush-templates/{templateId}/duplicateDuplicate a web push template

Creates a copy of a web push template. The copy is not attached to any campaign, so you can edit it freely before using it.

Parameters

  • templateId (path, required, string) — The web push template id to duplicate.

Request body

  • templateName (string, optional) — The name for the copy. Defaults to the original name followed by "(Copy)".

Responses

  • 200 — WebpushTemplateApiResponseDto
  • 401 — Unauthorized

Forms

Forms API for building and measuring forms.

A form collects answers from visitors and can map them onto contacts in an audience. Saving a form only updates its draft — publish it to make your changes live. Embeds control where a published form appears: inline on a page, or as a popup, slider, side tab, or popover.

POST /formCreate a form

Creates a form in an audience.

The form is saved as a draft and is not reachable by visitors until you publish it. Retrieve the questions and settings format from GET /format-docs/form-json-format.

Parameters

  • audienceId (query, required, string) — The audience id the form maps its responses into.

Request body

  • title (string, required) — The title of the form.
  • questions (FormQuestion[], optional) — The questions in the form.
  • settings (FormSettings, optional) — The form settings.
  • description (string, optional) — An internal description of the form, shown in the app.

Responses

  • 200 — CreateFormApiResponseDto
  • 401 — Unauthorized

PUT /form/{formId}Update a form

Updates a form. Pass only the fields you want to change; anything you leave out is kept as it is.

questions and settings are each replaced wholesale rather than merged, so send the complete array or object — a partial settings drops every key you omit.

Changes are saved to the draft. Publish the form to make them live.

Parameters

  • formId (path, required, string) — The form id.

Request body

  • title (string, optional) — The title of the form.
  • questions (FormQuestion[], optional) — The questions in the form. Replaces the existing questions entirely.
  • settings (FormSettings, optional) — The form settings. Replaces the existing settings entirely.
  • description (string, optional) — An internal description of the form, shown in the app.
  • metaTitle (string, optional) — The title used when the form is shared as a link.
  • metaDescription (string, optional) — The description used when the form is shared as a link.
  • metaImage (string, optional) — The URL of the image used when the form is shared as a link.

Responses

  • 200 — UpdateFormApiResponseDto
  • 401 — Unauthorized

DELETE /form/{formId}Delete a form

Deletes a form along with its embeds. Any campaign or journey node that referenced one of its popups is cleared.

Parameters

  • formId (path, required, string) — The form id.

Responses

  • 200 — string
  • 401 — Unauthorized

POST /form/{formId}/publishPublish a form

Publishes the current draft. Spreeflo takes a snapshot of the questions, settings, and social preview, and that snapshot is what visitors see from now on — later edits stay in the draft until you publish again.

Each publish increments the form version number.

Parameters

  • formId (path, required, string) — The form id.

Responses

  • 200 — PublishFormApiResponseDto
  • 401 — Unauthorized

GET /form/{formId}/analyticsGet form analytics

Returns the views, starts, and submissions funnel for a form, optionally narrowed to a date range or a device type.

Parameters

  • formId (path, required, string) — The form id.
  • from (query, optional, string) — Only count activity at or after this ISO 8601 timestamp.
  • to (query, optional, string) — Only count activity at or before this ISO 8601 timestamp.
  • device (query, optional, string) — Only count activity from this device type.

Responses

  • 200 — FormAnalyticsApiResponseDto
  • 401 — Unauthorized

GET /form/{formId}/summaryGet a summary of form responses

Returns aggregated answers for every question in a form: option counts for choice questions, rating distributions and averages for scales, yes/no splits, matrix and ranking breakdowns, and a sample of free-text answers.

Parameters

  • formId (path, required, string) — The form id.
  • from (query, optional, string) — Only include responses submitted at or after this ISO 8601 timestamp.
  • to (query, optional, string) — Only include responses submitted at or before this ISO 8601 timestamp.

Responses

  • 200 — FormSummaryDto
  • 401 — Unauthorized

GET /form/{formId}/responses/csvExport form responses as CSV

Returns the form responses as CSV text, newest first, with the same columns as the export in the app.

A single request returns at most 10,000 rows. When truncated is true there were more responses in the range than were returned — narrow the date window and request again to page through the rest.

Parameters

  • formId (path, required, string) — The form id.
  • from (query, optional, string) — Only include responses submitted at or after this ISO 8601 timestamp.
  • to (query, optional, string) — Only include responses submitted at or before this ISO 8601 timestamp.
  • maxRows (query, optional, number (double)) — The maximum number of rows to return. Maximum is 10000, which is also the default.

Responses

  • 200 — FormResponsesCsvApiResponseDto
  • 401 — Unauthorized

POST /form/{formId}/embedsCreate an embed

Creates an embed for a form. An embed is one placement of the form on your site — inline in the page, taking over the full page, or as a popup, slider, side tab, or popover.

Parameters

  • formId (path, required, string) — The form id.

Request body

  • name (string, required) — A name for the embed, used to tell your placements apart in the app.
  • type (FormEmbedType, required) — How the form appears.
  • settings (object, optional) — Placement settings, such as when the embed appears and how it is styled.

Responses

  • 200 — EmbedApiResponseDto
  • 401 — Unauthorized

PUT /form/{formId}/embeds/{embedId}Update an embed

Replaces an embed’s name, type, and settings. All three are replaced wholesale, so send the complete object.

Parameters

  • formId (path, required, string) — The form id.
  • embedId (path, required, string) — The embed id.

Request body

  • name (string, required) — A name for the embed, used to tell your placements apart in the app.
  • type (FormEmbedType, required) — How the form appears.
  • settings (object, optional) — Placement settings, such as when the embed appears and how it is styled.

Responses

  • 200 — EmbedApiResponseDto
  • 401 — Unauthorized

DELETE /form/{formId}/embeds/{embedId}Delete an embed

Deletes an embed. The form itself and its responses are unaffected.

Parameters

  • formId (path, required, string) — The form id.
  • embedId (path, required, string) — The embed id.

Responses

  • 200 — string
  • 401 — Unauthorized

POST /form/{formId}/popup-embedsCreate a popup embed

Creates a standalone popup embed for a form — a popup, slider, side tab, or popover that can be shown on its own, or triggered by a popup node inside a campaign or journey.

Parameters

  • formId (path, required, string) — The form id.

Request body

  • name (string, required) — A name for the popup, used to tell your placements apart in the app.
  • type (PopupEmbedType, required) — How the popup appears.
  • settings (object, optional) — Placement settings, such as when the popup appears and how it is styled.

Responses

  • 200 — EmbedApiResponseDto
  • 401 — Unauthorized

PUT /form/{formId}/popup-embeds/{embedId}Update a popup embed

Replaces a popup embed’s name, type, and settings. All three are replaced wholesale, so send the complete object.

Parameters

  • formId (path, required, string) — The form id.
  • embedId (path, required, string) — The embed id.

Request body

  • name (string, required) — A name for the popup, used to tell your placements apart in the app.
  • type (PopupEmbedType, required) — How the popup appears.
  • settings (object, optional) — Placement settings, such as when the popup appears and how it is styled.

Responses

  • 200 — EmbedApiResponseDto
  • 401 — Unauthorized

DELETE /form/{formId}/popup-embeds/{embedId}Delete a popup embed

Deletes a popup embed. Any campaign or journey node that was showing it is cleared.

Parameters

  • formId (path, required, string) — The form id.
  • embedId (path, required, string) — The embed id.

Responses

  • 200 — string
  • 401 — Unauthorized

Senders

Senders API for managing the domains and email addresses you send from.

Register a domain you own, add the DNS records Spreeflo returns to verify it, then add sender addresses on that domain. These endpoints require an API key with admin access to the workspace.

POST /identities/domainsRegister a sending domain

Registers a domain you own so you can send from addresses on it.

The response contains the DNS records to add at your domain host. Once those records have propagated, Spreeflo verifies the domain and you can add sender addresses on it. Requires an API key with admin access to the workspace.

Request body

  • domain (string, required) — The domain you own, without a subdomain or protocol, for example example.com.

Responses

  • 200 — RegisterDomainIdentityApiResponseDto
  • 401 — Unauthorized

DELETE /identities/domains/{domainIdentityId}Delete a sending domain

Deletes a domain you registered, along with every sender address on it. Campaigns still sending from those addresses are paused.

Only domains you registered yourself can be deleted here — a domain purchased through Spreeflo is tied to its subscription and must be cancelled in the app. Requires an API key with admin access to the workspace.

Parameters

  • domainIdentityId (path, required, string) — The sending domain id to delete.

Responses

  • 200 — string
  • 401 — Unauthorized

POST /identities/emailsAdd a sender address

Adds a sender email address on one of your registered sending domains. The name you provide is the friendly from-name recipients see. Requires an API key with admin access to the workspace.

Request body

  • emailAddress (string, required) — The address to send from. Its domain must already be registered and verified.
  • firstName (string, required) — The first name recipients see in the from-name.
  • lastName (string, required) — The last name recipients see in the from-name.

Responses

  • 200 — EmailIdentityApiResponseDto
  • 401 — Unauthorized

PUT /identities/emails/{emailIdentityId}Update a sender address

Updates the friendly from-name on a sender address. The address itself cannot be changed. Requires an API key with admin access to the workspace.

Parameters

  • emailIdentityId (path, required, string) — The sender address id to update.

Request body

  • firstName (string, required) — The first name recipients see in the from-name.
  • lastName (string, required) — The last name recipients see in the from-name.

Responses

  • 200 — EmailIdentityApiResponseDto
  • 401 — Unauthorized

DELETE /identities/emails/{emailIdentityId}Delete a sender address

Deletes a sender address. Campaigns still sending from it are paused first, so they do not fail midway. Requires an API key with admin access to the workspace.

Parameters

  • emailIdentityId (path, required, string) — The sender address id to delete.

Responses

  • 200 — string
  • 401 — Unauthorized

Tracking

Tracking API for recording what your contacts do.

Send events from your own backend to trigger journeys, build segments, and personalise emails. Use identify to attach a known email address to a website visitor Spreeflo has been tracking anonymously.

POST /track/eventTrack an event

Records a custom event against a contact — an order shipped, a plan upgraded, a feature used.

Events can start journeys, be used as segment criteria, and have their properties injected into emails. The contact must already exist in the audience; create it first with the upsert contact endpoint.

Events are processed asynchronously: a 204 means the event was accepted, not that it has been applied yet.

Request body

  • audienceId (string, required) — The audience the contact belongs to.
  • email (string, required) — The email address of the contact the event belongs to. The contact must already exist in the audience.
  • name (string, required) — The name of the event, for example order_shipped.
  • properties (object, optional) — Any data to record with the event. Nested objects and arrays are supported. These values can be compared in segments and injected into emails.
  • timestamp (string, optional) — The ISO 8601 time the event happened. Defaults to now.

Responses

  • 204 — The event was accepted.
  • 401 — Unauthorized

POST /track/identifyIdentify a visitor

Binds a website visitor Spreeflo has been tracking anonymously to an email address, so their browsing history joins up with their contact record.

Pass the visitor id your site captured in the spreeflo_vid cookie. If no contact exists for the email, one is created; if one does, the visitor is merged into it. Any attributes you send are applied to the contact.

Identifications are processed asynchronously: a 204 means the request was accepted, not that it has been applied yet.

Request body

  • audienceId (string, required) — The audience to bind the visitor into.
  • visitorId (string, required) — The visitor id your site captured, from the spreeflo_vid cookie.
  • email (string, required) — The email address to bind the visitor to. A contact is created if none exists, and merged into if one does.
  • attributes (object, optional) — Contact attributes to set at the same time, keyed by attribute key.

Responses

  • 204 — The event was accepted.
  • 401 — Unauthorized

Revenue

Revenue API for recording payments.

Send each payment from your backend — typically from your payment provider webhook — and Spreeflo attributes the revenue back to the campaign, journey, or channel that brought the customer in.

POST /revenueRecord revenue

Records a payment so Spreeflo can attribute the revenue to the campaign, journey, or channel that brought the customer in. Send this from your backend, typically from your payment provider webhook.

transactionId makes the call idempotent — sending the same transaction twice records it once, so webhook retries are safe.

Amounts are always positive. To record a refund, send the same transactionId again with refunded set to true.

Request body

  • audienceId (string, required) — The audience to credit the revenue to.
  • eventName (string, required) — A name for the payment event, for example subscription_payment or purchase.
  • revenue (object, required) — The money involved.
  • transactionId (string, required) — Your own identifier for the payment. Sending the same identifier twice records it once, so webhook retries are safe.
  • visitorId (string, optional) — The visitor id from the spreeflo_vid cookie, if you have it. Without it the payment is still recorded, but cannot be attributed to a specific visit.
  • sessionId (string, optional) — The session the payment belongs to, if you have it.
  • email (string, optional) — The customer’s email address, used to match the payment to a contact.
  • name (string, optional) — The customer’s name.
  • customerId (string, optional) — Your own identifier for the customer.
  • renewal (boolean, optional) — Whether this payment is a renewal of an existing subscription rather than a first purchase.
  • isFreeTrial (boolean, optional) — Whether this payment starts a free trial.
  • refunded (boolean, optional) — Whether this call records a refund. Send the original transactionId again with this set to true. With a refundId, amount is the amount of that one refund; without one, amount is the total refunded so far.
  • refundId (string, optional) — Your own identifier for this refund, so partial refunds can be recorded separately and retries deduplicated.
  • timestamp (string, optional) — The ISO 8601 time the payment happened. Defaults to now. Backdate it when your webhook arrives late, so the revenue lands on the right day.

Responses

  • 200 — RevenueApiResponseDto
  • 401 — Unauthorized

Transactional

Transactional API for sending transactional emails.

Transactional emails are emails programmatically sent by you, triggered by user actions or system events (e.g. password resets, order confirmations). Multiple recipients are supported. Unlike campaigns, recipients do not need to be marketing contacts nor even exist in your audience list. Each recipient counts as a send towards your email sends limit.

Personalisation

Every variable is a namespaced path — {{ namespace.name }} — and the namespace says where the value comes from. This is the same syntax across all three send endpoints, whether the body is inline HTML, a raw MIME message, or a saved template.

| Namespace | Comes from |

| --- | --- |

| var. | the vars array in your request |

| contact. | the contact matched by recipient address |

| spree. | system values (unsubscribe link, sender, date) |

| <objectType>. | a custom object related to that contact |

| ai. | AI variables declared on a saved template |

The rule for a caller is one line: everything you put in vars is {{ var.<key> }} — whether you sent a literal value or a prompt for AI to write. Everything else is resolved for you and needs nothing in the request.

Give any variable a fallback with the Liquid default filter, e.g. {{ contact.first_name | default: "there" }}. A variable nobody supplies renders as empty; it is not an error and the surrounding copy is untouched.

GET /transactional/uploads/presigned-urlGet a presigned upload URL

Returns a presigned URL for uploading a file to use as an email attachment or inline image. Upload the file to the returned URL via a PUT request with the file's Content-Type header, then pass the returned key when sending an email.

Example upload using curl:

Replace <pathToFile> with the path to the file you want to upload, and <uploadUrl> with the uploadUrl value from the response. Set the Content-Type header to match the contentType query parameter used when requesting the presigned URL.

Parameters

  • fileName (query, required, string) — The name of the file to upload.
  • contentType (query, required, string) — The MIME type of the file to upload.

Responses

  • 200 — TransactionalPresignedUploadResponseDto
  • 401 — Unauthorized

GET /transactional/emails/info/{emailId}Get email info

Retrieves metadata about a sent transactional email, including delivery status, open and click tracking.

Parameters

  • emailId (path, required, string) — The id of the transactional email to retrieve info for.

Responses

  • 200 — TransactionalEmailInfoResponseDto
  • 401 — Unauthorized

GET /transactional/emails/content/{emailId}Get email content

Retrieves the full content of a sent transactional email. The response includes one of html, text, or raw depending on the type of email sent, along with any attachments and inline images.

Parameters

  • emailId (path, required, string) — The id of the transactional email to retrieve content for.

Responses

  • 200 — TransactionalEmailContentResponseDto
  • 401 — Unauthorized

POST /transactional/emails/send-rawSend a raw email

Sends a transactional email using a pre-uploaded raw MIME message in RFC 5322 format. Upload the raw message first via the presigned upload URL endpoint, then reference it by key.

Variables work in the headers (including Subject:) and in the body, using the same namespaces as every other send endpoint. The From: and To: headers in your uploaded message are placeholders — they are overwritten from fromEmail/fromName and to.

The whole message is rendered as a Liquid template. If it legitimately contains {% ... %}, wrap that part in {% raw %}...{% endraw %} so it is not read as a tag.

Request body

  • message (SendRawEmailMessageDto, required)
  • audienceId (string, optional) — The audience id to pull contact attributes from when isPullAttributesIfMatchContact is enabled. If not provided, defaults to your last selected audience.

Responses

  • 200 — TransactionalEmailSendResponseDto
  • 401 — Unauthorized

POST /transactional/emails/send-templateSend a template email

Sends a transactional email using a pre-designed email template. Reference the template by its id and supply recipient and variable data.

What you pass in vars fills the {{ var. }} tokens the template references — so the template must reference {{ var.<key> }} for your value to appear anywhere. Everything else the template can use ({{ contact. }}, {{ spree. }}, related object namespaces) resolves without anything from you.

The template's own AI variables ({{ ai. }}) belong to whoever authored it: their prompts live in the template and cannot be changed, overridden, or suppressed from this request. {{ var.headline }} and {{ ai.headline }} are independent slots even when they share a name — so a slot you need to set per send must be written {{ var.headline }} in the template.

Request body

  • message (SendTemplateEmailMessageDto, required)

Responses

  • 200 — TransactionalEmailSendResponseDto
  • 401 — Unauthorized

POST /transactional/emails/sendSend an email

Sends a transactional email with inline HTML and/or text content. Provide the full email body directly in the request. At least one of html or text must be provided. Note: plain text emails do not support open or click tracking.

The body and subject are rendered with the same namespaced variables as a template send, so {{ contact. }}, {{ spree. }} and related object namespaces all work in inline copy — not just the values you pass in vars.

Request body

  • message (SendEmailMessageDto, required)
  • audienceId (string, optional) — The audience id to pull contact attributes from when isPullAttributesIfMatchContact is enabled. If not provided, defaults to your last selected audience.

Responses

  • 200 — TransactionalEmailSendResponseDto
  • 401 — Unauthorized

Formats

Formats API for retrieving the document specifications this API accepts.

Several endpoints take a whole document rather than a flat set of fields — a sequence node graph, a segment definition, a form questions array, an email body. These endpoints return the full, current specification for each of those documents, so you (or your AI agent) can author them correctly instead of by trial and error.

GET /format-docsList format specifications

Lists the available document specifications: the slug, title, and a summary of what each one covers, plus which specifications only make sense read together.

Start with capability-summaries for an overview of what the platform can express.

Responses

  • 200 — FormatDocsListDto
  • 401 — Unauthorized

GET /format-docs/{slug}Get a format specification

Returns one specification in full, as markdown. Specifications are never truncated — a clipped spec produces documents that fail validation for reasons you cannot see.

An unrecognised slug returns 410.

Parameters

  • slug (path, required, capability-summaries | sequence-json-format | automation-nodes | segment-json-format | segment-builder | form-json-format | email-json-format) — The slug of the specification to retrieve, as returned by the list endpoint.

Responses

  • 200 — FormatDocDto
  • 401 — Unauthorized