Automation

Behavioral events & workflows

Stream product and e-commerce events (cart_abandoned, order_completed, trial_started) via POST /v3/events to enrich contacts and trigger visual multi-step automation workflows.

Track a custom behavioral event

POST/v3/eventsPermission events:write
POST/v1/eventsAlias · Permission events:write

Records a custom event for a contact. Both Brevo's v3 event payload (identifiers.email_id, contact_properties, event_properties) and SnappyMails' flat payload (email, attributes, properties) are accepted.

  • If the contact does not exist yet, SnappyMails automatically creates the contact and merges any contact_properties / attributes.
  • If any ACTIVE automation workflow is listening for event_name, the contact is enrolled immediately and the response tells you how many workflows were triggered (workflows_triggered).
ParameterTypeDescription
event_namerequiredstringMachine-readable event name, e.g. cart_abandoned, order_completed, trial_started.
identifiersobjectBrevo-compatible identifier object { email_id?, contact_id? }. You can also pass top-level email directly.
emailstringShorthand for identifiers.email_id. Either email or identifiers is required.
contact_properties / attributesobjectContact attributes to upsert onto the contact record before evaluating workflow triggers.
event_properties / propertiesobjectArbitrary JSON payload stored with the event and passed to triggered workflow enrollments.
event_date / occurred_atstring (date-time)Optional RFC 3339 timestamp when the event occurred (defaults to server receive time).

Brevo v3 payload format

curl -X POST 'https://app.snappymails.com/v3/events' \
  -H "api-key: $SNAPPYMAILS_API_KEY" \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
    "event_name": "cart_abandoned",
    "identifiers": {
      "email_id": "jane@example.com"
    },
    "contact_properties": {
      "FIRSTNAME": "Jane",
      "PLAN": "Growth"
    },
    "event_properties": {
      "cart_id": "cart_9812",
      "total": 129,
      "currency": "USD",
      "checkout_url": "https://acme.com/checkout/cart_9812"
    }
  }'

Flat payload format

curl -X POST 'https://app.snappymails.com/v1/events' \
  -H "api-key: $SNAPPYMAILS_API_KEY" \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
    "event_name": "order_completed",
    "email": "jane@example.com",
    "attributes": {
      "LTV": 520
    },
    "properties": {
      "order_id": "ORD-1099",
      "total": 129
    }
  }'

201 Created

Response
{
  "id": "7f4e3d2c-1b0a-4f9e-8d7c-6b5a4e3f2d1c",
  "workflows_triggered": 1,
  "event": {
    "id": "7f4e3d2c-1b0a-4f9e-8d7c-6b5a4e3f2d1c",
    "contact_id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
    "email": "jane@example.com",
    "event_name": "cart_abandoned",
    "properties": {
      "cart_id": "cart_9812",
      "total": 129,
      "currency": "USD",
      "checkout_url": "https://acme.com/checkout/cart_9812"
    },
    "occurred_at": "2026-10-28T15:10:00Z",
    "created_at": "2026-10-28T15:10:00Z"
  }
}

Visual automation workflows

Build and monitor visual node-graph journeys in the portal under Workflows, or manage them via the portal REST API (/api/v1/workflows*, authenticated with a portal Bearer JWT).

Supported triggers

trigger_typeFired whentrigger_config fields
list_joinedA contact is added to a subscriber list (via API, form, import, double opt-in confirmation or portal).list_id (optional; empty matches any list), allow_reentry
form_submittedA subscriber submits a hosted or embedded sign-up form.form_id (optional; empty matches any form), allow_reentry
custom_eventA custom behavioral event arrives via POST /v3/events or POST /v1/events.event_name (e.g. cart_abandoned), allow_reentry
manualEnrolled explicitly via POST /api/v1/workflows/{id}/enroll or the portal's Test Enroll button.allow_reentry

Workflow node types

Node typeDescriptionKey config fields
send_emailSends a personalised email using a saved template or inline HTML/subject.template_id or subject + html_content, optional from_name, from_email
delayPauses the contact's journey before advancing to next_id.delay_minutes (duration value), delay_unit (minutes | hours | days)
conditionEvaluates a rule and branches to true_id (Yes) or false_id (No).condition_type: attribute (attribute_key, operator, value), email_opened, email_clicked, custom_event (event_name, within_days), or in_list (list_id)
update_contactUpdates a contact attribute, adds/removes a tag, or subscribes/unsubscribes from a list.contact_action: set_attribute | add_tag | remove_tag | add_to_list | remove_from_list
webhookPOSTs a JSON payload with the workflow, contact and trigger context to an external URL.webhook_url
exitEnds the contact's enrollment immediately.—

Example workflow graph (JSON)

Workflow definition
{
  "name": "Abandoned Cart Recovery",
  "trigger_type": "custom_event",
  "trigger_config": {
    "event_name": "cart_abandoned",
    "allow_reentry": true
  },
  "entry_node_id": "node_wait_1h",
  "nodes": [
    {
      "id": "node_wait_1h",
      "type": "delay",
      "label": "Wait 1 Hour",
      "config": {
        "delay_minutes": 1,
        "delay_unit": "hours"
      },
      "next_id": "node_reminder_email"
    },
    {
      "id": "node_reminder_email",
      "type": "send_email",
      "label": "Send Cart Reminder",
      "config": {
        "subject": "You left something behind, {{ contact.FIRSTNAME | default:\"there\" }}!",
        "html_content": "<p>Resume your checkout anytime.</p><p><a href=\"{{ unsubscribe }}\">Unsubscribe</a></p>"
      },
      "next_id": "node_wait_24h"
    },
    {
      "id": "node_wait_24h",
      "type": "delay",
      "label": "Wait 24 Hours",
      "config": {
        "delay_minutes": 24,
        "delay_unit": "hours"
      },
      "next_id": "node_check_order"
    },
    {
      "id": "node_check_order",
      "type": "condition",
      "label": "Completed order in last 2 days?",
      "config": {
        "condition_type": "custom_event",
        "event_name": "order_completed",
        "within_days": 2
      },
      "true_id": "node_tag_recovered",
      "false_id": "node_discount_email"
    },
    {
      "id": "node_tag_recovered",
      "type": "update_contact",
      "label": "Set CART_STATUS = recovered",
      "config": {
        "contact_action": "set_attribute",
        "attribute_key": "CART_STATUS",
        "attribute_value": "recovered"
      }
    },
    {
      "id": "node_discount_email",
      "type": "send_email",
      "label": "Send 10% Recovery Offer",
      "config": {
        "subject": "Take 10% off your cart (Code: COMEBACK10)",
        "html_content": "<p>Use code <strong>COMEBACK10</strong> at checkout.</p>"
      }
    }
  ]
}

Portal workflow endpoints

MethodPathDescription
GET/api/v1/workflows/templatesList built-in workflow presets (Welcome Series, Abandoned Cart, Post-Purchase VIP).
GET / POST/api/v1/workflowsList all workflows or create a new workflow.
GET / PUT / DELETE/api/v1/workflows/{id}Get workflow (includes per-node funnel stats), update graph, or delete.
POST/api/v1/workflows/{id}/activateSet workflow status to ACTIVE so triggers begin enrolling contacts.
POST/api/v1/workflows/{id}/pausePause new enrollments and step execution.
POST/api/v1/workflows/{id}/enrollManually enroll a contact by {"email": "..."} or {"contact_id": "..."}.
GET/api/v1/workflows/{id}/enrollmentsPaginated list of contacts enrolled in the workflow and their current node.
GET/api/v1/workflows/{id}/enrollments/{enrollmentId}/logsStep-by-step execution audit log for a single enrollment.

Test events in the portal

You can inspect the live event stream and fire test events without writing code from Events & Brevo Import in the portal.