Trigger Workflow
Enroll a contact in an email workflow from your application.
Enroll a contact in a workflow from your own backend. Typical use cases: deliver a freebie or lead magnet after an opt-in form, start an onboarding sequence after signup, or kick off any lifecycle automation from an external event.
The endpoint upserts the contact (creates it if the email is unknown, updates it otherwise) and enrolls it in the target workflow. Enrollment is idempotent: calling it again with the same email never enrolls the contact twice in the same workflow.
Requirements
- The workflow must exist in the same organization as the API key.
- The workflow trigger must be set to API request (Workflows > edit > trigger node).
- The workflow status must be Active.
The workflow settings panel shows the exact workflowId and a ready-to-copy curl example when the trigger is set to API request.
Request
POST /api/v1/email/workflows/triggerAuthentication (either header):
x-api-key: org_api_key
Authorization: Bearer org_api_keyBody:
| Field | Type | Required | Description |
|---|---|---|---|
workflowId | string | Yes | Workflow id, shown in the workflow trigger settings. |
email | string | Yes | Contact email. Existing contacts are matched by organization and email. |
firstName | string | No | Contact first name. |
lastName | string | No | Contact last name. |
tags | string[] | No | Tags to add to the contact. Tags are additive: existing tags are never removed by this endpoint. |
customFields | object | No | Extra values used for segmentation and personalization. Numbers and booleans are coerced to strings. |
Example:
curl -X POST https://your-growth-app.convex.site/api/v1/email/workflows/trigger \
-H "x-api-key: org_api_key" \
-H "Content-Type: application/json" \
-d '{"workflowId":"<workflow-id>","email":"[email protected]","firstName":"Lead","tags":["freebie"]}'Response
200 with the upserted contact:
{
"contact": {
"id": "m57...",
"email": "[email protected]",
"firstName": "Lead",
"status": "subscribed",
"source": "api",
"tags": ["freebie"],
"createdAt": 1783528574709,
"updatedAt": 1783528574709
},
"created": true
}Error responses:
| Status | Meaning |
|---|---|
400 | Missing workflowId or email, or the workflow trigger is not set to API request. |
401 | Missing or invalid API key. |
404 | Workflow not found in the API key's organization. |
409 | Workflow is not active, or the contact is unsubscribed. |
Behavior Details
- The contact is created with
source: "api"and statussubscribedwhen new. - Unsubscribed, bounced, or complained contacts are not enrolled; the endpoint returns
409. tagsandcustomFieldsare both merged into the contact, exactly as Identify does — nothing another integration wrote is dropped. To remove tags, use Identify withremoveTags, or the contacts UI.- The response carries
skippedTagsandskippedCustomFields, non-empty only when the 50-tag or 20-field cap was reached. - If you only want to tag a contact and let a Tag added workflow react, call Identify instead. It upserts, merges, and fires the same triggers — without an
api_requestworkflow to carry the payload. - If the contact was already enrolled in this workflow (even after completing it), the call succeeds without re-enrolling.
- Workflow steps run on the standard scheduler; the first email is sent after the first step's configured delay.
Delivering A File Behind A Button
To serve a downloadable resource (PDF, zip, audio) from the workflow email, open the email step in the workflow editor, add a Button block, and use Link a file to upload the resource. The file is hosted on the organization's storage and the button links to it automatically. You can also paste any external URL in the button's URL field instead.