API/Trigger Workflow

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/trigger

Authentication (either header):

x-api-key: org_api_key
Authorization: Bearer org_api_key

Body:

FieldTypeRequiredDescription
workflowIdstringYesWorkflow id, shown in the workflow trigger settings.
emailstringYesContact email. Existing contacts are matched by organization and email.
firstNamestringNoContact first name.
lastNamestringNoContact last name.
tagsstring[]NoTags to add to the contact. Tags are additive: existing tags are never removed by this endpoint.
customFieldsobjectNoExtra 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:

StatusMeaning
400Missing workflowId or email, or the workflow trigger is not set to API request.
401Missing or invalid API key.
404Workflow not found in the API key's organization.
409Workflow is not active, or the contact is unsubscribed.

Behavior Details

  • The contact is created with source: "api" and status subscribed when new.
  • Unsubscribed, bounced, or complained contacts are not enrolled; the endpoint returns 409.
  • tags and customFields are both merged into the contact, exactly as Identify does — nothing another integration wrote is dropped. To remove tags, use Identify with removeTags, or the contacts UI.
  • The response carries skippedTags and skippedCustomFields, 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_request workflow 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.