API/Trigger Event

Trigger Event

Post what happened and let Growth pick the workflow that answers it.

Enroll a contact by naming what happened instead of which workflow should answer. Your app posts an event — freebie, customer, subscription-cancelled — and the organization's routing table decides which workflow it enters.

Compared with Trigger Workflow, which pins the workflow id in your code:

  • A new campaign sequence is a row in Email marketing > Workflows > Event routing, not an environment variable and a deploy on your side.
  • Routing reads the contact's merged profile, not the request. A payment webhook carries no cookie, but the utm_campaign written on the contact at signup three weeks earlier is still there — so the payment event can still be routed by the campaign that produced the lead. Your app cannot do that for itself.
  • Exactly one workflow is entered. Two sequences selling the same offer to the same contact is the failure this endpoint exists to prevent.

How A Route Is Chosen

Routes belong to an event. Each is either a rule (a contact field that must equal a value) or the event's default (no condition).

  1. Paused routes are skipped.
  2. Rules are tried first, highest priority first, ties going to the older rule.
  3. The default catches everything no rule matched. A default never shadows a rule, whatever its priority.

A rule's field is read from the contact's custom fields, and from its tags as a fallback: utm_source matches both the custom field utm_source and the tag utm-source:instagram. Comparison ignores case and accents.

Request

POST /api/v1/email/events/trigger

Authentication (either header):

x-api-key: org_api_key
Authorization: Bearer org_api_key

Body:

FieldTypeRequiredDescription
eventstringYesEvent name, normalized to lowercase [a-z0-9._-].
emailstringYesContact email. Existing contacts are matched by organization and email.
firstNamestringNoContact first name.
lastNamestringNoContact last name.
tagsstring[]NoTags to add. Additive: nothing is removed.
customFieldsobjectNoMerged into the contact and used for routing. Numbers and booleans are coerced to strings.

Example:

curl -X POST https://your-growth-app.convex.site/api/v1/email/events/trigger \
  -H "x-api-key: org_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "freebie",
    "email": "[email protected]",
    "firstName": "Lead",
    "tags": ["freebie", "utm-campaign:ete-2026"],
    "customFields": { "utm_campaign": "ete-2026", "ref": "gregoire" }
  }'

Response

200, with the enrolment decision alongside the upserted contact:

{
  "contact": { "id": "m57...", "email": "[email protected]", "tags": ["freebie"] },
  "created": true,
  "enrolled": true,
  "reason": "enrolled",
  "event": "freebie",
  "workflowId": "n97...",
  "workflowName": "Freebie nurture — summer",
  "matchedBy": "rule"
}

A routing problem is not an error. The contact was merged, and that is a real effect the caller must not have to undo, so an unroutable event still returns 200 with enrolled: false and a reason to log:

reasonMeaning
enrolledThe contact entered workflowId.
no_routeNothing is routed for this event yet.
no_matching_routeRules exist for the event, none matched, and there is no default.
workflow_not_activeThe routed workflow is a draft or paused.
workflow_missingThe routed workflow was deleted.
excluded_by_tagsThe contact carries one of the workflow's exclude tags.

Genuine errors:

StatusMeaning
400Missing event or email.
401Missing or invalid API key.
409The contact is unsubscribed, bounced or complained.
422The contact's custom field cap was reached.

Log enrolled and reason. no_route on an event you thought was configured is the one that costs you a nurture sequence, and nothing else will tell you.

Managing Routes From The API

Routes are editable in the UI and over the API.

# List (optionally filtered by event)
curl "https://your-growth-app.convex.site/api/v1/email/event-routes?event=freebie" \
  -H "x-api-key: org_api_key"

# Create or update (pass routeId to update)
curl -X POST https://your-growth-app.convex.site/api/v1/email/event-routes \
  -H "x-api-key: org_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "freebie",
    "matchKey": "utm_campaign",
    "matchValue": "ete-2026",
    "workflowId": "n97...",
    "priority": 10
  }'

# Delete
curl -X DELETE https://your-growth-app.convex.site/api/v1/email/event-routes/<route-id> \
  -H "x-api-key: org_api_key"

Omit matchKey and matchValue to set the event's default. Both must be present together.

The routed workflow must use the API request trigger; saving a route to a workflow on any other trigger is refused at save time rather than failing silently at send time.

Partner-Owned Routes

A route can belong to an affiliate partner who built their own workflow from their portal. Those routes are pinned to the partner's own ref, sit above the organization's rules, and follow their workflow's status. The organization can pause them or change their priority but not repoint them — see Affiliate tracking setup.