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:writePOST
/v1/eventsAlias · Permission events:writeRecords 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).
| Parameter | Type | Description |
|---|---|---|
event_namerequired | string | Machine-readable event name, e.g. cart_abandoned, order_completed, trial_started. |
identifiers | object | Brevo-compatible identifier object { email_id?, contact_id? }. You can also pass top-level email directly. |
email | string | Shorthand for identifiers.email_id. Either email or identifiers is required. |
contact_properties / attributes | object | Contact attributes to upsert onto the contact record before evaluating workflow triggers. |
event_properties / properties | object | Arbitrary JSON payload stored with the event and passed to triggered workflow enrollments. |
event_date / occurred_at | string (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_type | Fired when | trigger_config fields |
|---|---|---|
list_joined | A 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_submitted | A subscriber submits a hosted or embedded sign-up form. | form_id (optional; empty matches any form), allow_reentry |
custom_event | A custom behavioral event arrives via POST /v3/events or POST /v1/events. | event_name (e.g. cart_abandoned), allow_reentry |
manual | Enrolled explicitly via POST /api/v1/workflows/{id}/enroll or the portal's Test Enroll button. | allow_reentry |
Workflow node types
| Node type | Description | Key config fields |
|---|---|---|
send_email | Sends a personalised email using a saved template or inline HTML/subject. | template_id or subject + html_content, optional from_name, from_email |
delay | Pauses the contact's journey before advancing to next_id. | delay_minutes (duration value), delay_unit (minutes | hours | days) |
condition | Evaluates 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_contact | Updates 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 |
webhook | POSTs a JSON payload with the workflow, contact and trigger context to an external URL. | webhook_url |
exit | Ends 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
| Method | Path | Description |
|---|---|---|
GET | /api/v1/workflows/templates | List built-in workflow presets (Welcome Series, Abandoned Cart, Post-Purchase VIP). |
GET / POST | /api/v1/workflows | List 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}/activate | Set workflow status to ACTIVE so triggers begin enrolling contacts. |
POST | /api/v1/workflows/{id}/pause | Pause new enrollments and step execution. |
POST | /api/v1/workflows/{id}/enroll | Manually enroll a contact by {"email": "..."} or {"contact_id": "..."}. |
GET | /api/v1/workflows/{id}/enrollments | Paginated list of contacts enrolled in the workflow and their current node. |
GET | /api/v1/workflows/{id}/enrollments/{enrollmentId}/logs | Step-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.