API reference
Early access. The VeraGen API is not open yet: this reference shows what it will do, and keys cannot be created until it opens. Request early access.

API reference

Automations

Automations are the other half of AI Automations: a trigger (a contact is created, a deal changes stage, a date comes round, a webhook arrives, or a person enrolls contacts by hand) followed by steps that run for one contact at a time: send an email or a text, wait, branch, tag, update the contact, notify the team. With automations:read a key lists them, across every project, and reads each one's runs. With automations:enroll it enrolls Thrive contacts in a published automation, exactly as Enroll contacts does in the hub, and enrolling sends real messages: the steps run within about a minute and email the contact, and text them where texting is set up. The API never creates, edits, publishes or pauses an automation, and returns a summary of its steps, never their settings. Nobody is messaged against their wishes. A contact who was erased from Thrive is not enrolled (erased). A contact who is on do-not-contact, has opted out of email or unsubscribed is enrolled, as in the hub, but every email step skips them and says why in the run; a contact on do-not-contact, one who replied STOP, or one who has not agreed to texts (when the project asks for consent, which it does unless its Settings say otherwise) gets no texts either. Other steps, such as adding a tag or notifying the team, still run. A run started through the API says so in Run history, with the key's name.

List automations

GET/v1/automationsautomations:read

Every automation in the account, across all its projects, most recently changed first: drafts, published and paused ones. Each carries its trigger, a summary of its steps (how many, which kinds, what it sends) and how many contacts it has run for. The steps' settings (addresses, message text, webhook URLs) are never included.

Parameters

NameTypeDescription
limit
query
integerHow many to return. 1 to 100. Default 25.
cursor
query
stringThe next_cursor from the previous page. Leave it out for the first page.
status
query
stringOnly automations with this status. One of draft, published, paused.
project_id
query
string (uuid)Only the automations in this project.

Response 200

FieldTypeDescription
dataarray of object
data[].idstring (uuid)
data[].project_idstring (uuid)The project it belongs to.
data[].namestring
data[].statusstringdraft (never published), published (running) or paused. Only a published automation takes enrollments.
data[].triggerobjectWhat starts it, as the hub's list shows it under Starts when.
data[].trigger.typestring or nullThe trigger, such as contact.created, deal.stage_changed, schedule or manual; null before one is chosen.
data[].trigger.labelstringIts name in the hub, such as Contact created.
data[].published_versionintegerThe version that runs; 0 for a draft that was never published.
data[].has_unpublished_changesbooleantrue when the draft in the builder differs from the published version.
data[].step_countintegerHow many steps the version that runs has (the draft's, before it is published), branches included.
data[].step_typesarray of stringThe kinds of step in it, each once, such as email.send, sms.send, wait or ai.write. More kinds may be added.
data[].sendsarray of stringWhat it sends: email and sms to the contact, team to people in the account.
data[].enrollmentsobjectIts runs, test runs left out.
data[].enrollments.totalintegerEvery run, except a delivery its Run only if filter skipped.
data[].enrollments.activeintegerRunning or waiting now.
data[].enrollments.completedinteger
data[].last_run_atstring or null (date-time)When its latest run started; null if it has never run.
data[].created_atstring (date-time)
data[].updated_atstring (date-time)
has_moreboolean
next_cursorstring or null

Errors

  • 400 The request is not valid.
  • 401 No valid API key.
  • 403 The key may not do this.
  • 404 Not found in this account.
  • 429 Too many requests.
  • 500 Something went wrong on our side.

The codes are listed under Errors.

cURL
curl "https://api.veragen.ai/v1/automations?limit=25" \
  -H "Authorization: Bearer $VERAGEN_API_KEY"
JavaScript
const res = await fetch("https://api.veragen.ai/v1/automations?limit=25", {
  headers: { Authorization: `Bearer ${process.env.VERAGEN_API_KEY}` },
});
const data = await res.json();
Python
import os, requests

res = requests.get(
    "https://api.veragen.ai/v1/automations",
    params={"limit": 25},
    headers={"Authorization": f"Bearer {os.environ['VERAGEN_API_KEY']}"},
)
data = res.json()
Response 200
{
  "data": [
    {
      "id": "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
      "project_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
      "name": "Speed to lead",
      "status": "published",
      "trigger": {
        "type": "contact.created",
        "label": "Contact created"
      },
      "published_version": 3,
      "has_unpublished_changes": false,
      "step_count": 5,
      "step_types": [
        "email.send",
        "notify.team",
        "wait",
        "task.create",
        "end"
      ],
      "sends": [
        "email",
        "team"
      ],
      "enrollments": {
        "total": 214,
        "active": 9,
        "completed": 198
      },
      "last_run_at": "2026-10-07T16:42:10.000Z",
      "created_at": "2026-09-02T10:00:00.000Z",
      "updated_at": "2026-10-01T08:15:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Get an automation

GET/v1/automations/{id}automations:read

One automation: its status, trigger, step summary and run counts.

Parameters

NameTypeDescription
id
path, required
string (uuid)The automation's id.

Response 200

FieldTypeDescription
idstring (uuid)
project_idstring (uuid)The project it belongs to.
namestring
statusstringdraft (never published), published (running) or paused. Only a published automation takes enrollments.
triggerobjectWhat starts it, as the hub's list shows it under Starts when.
trigger.typestring or nullThe trigger, such as contact.created, deal.stage_changed, schedule or manual; null before one is chosen.
trigger.labelstringIts name in the hub, such as Contact created.
published_versionintegerThe version that runs; 0 for a draft that was never published.
has_unpublished_changesbooleantrue when the draft in the builder differs from the published version.
step_countintegerHow many steps the version that runs has (the draft's, before it is published), branches included.
step_typesarray of stringThe kinds of step in it, each once, such as email.send, sms.send, wait or ai.write. More kinds may be added.
sendsarray of stringWhat it sends: email and sms to the contact, team to people in the account.
enrollmentsobjectIts runs, test runs left out.
enrollments.totalintegerEvery run, except a delivery its Run only if filter skipped.
enrollments.activeintegerRunning or waiting now.
enrollments.completedinteger
last_run_atstring or null (date-time)When its latest run started; null if it has never run.
created_atstring (date-time)
updated_atstring (date-time)

Errors

  • 400 The request is not valid.
  • 401 No valid API key.
  • 403 The key may not do this.
  • 404 Not found in this account.
  • 429 Too many requests.
  • 500 Something went wrong on our side.

The codes are listed under Errors.

cURL
curl "https://api.veragen.ai/v1/automations/{id}" \
  -H "Authorization: Bearer $VERAGEN_API_KEY"
JavaScript
const res = await fetch("https://api.veragen.ai/v1/automations/{id}", {
  headers: { Authorization: `Bearer ${process.env.VERAGEN_API_KEY}` },
});
const data = await res.json();
Python
import os, requests

res = requests.get(
    "https://api.veragen.ai/v1/automations/{id}",
    headers={"Authorization": f"Bearer {os.environ['VERAGEN_API_KEY']}"},
)
data = res.json()
Response 200
{
  "id": "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
  "project_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
  "name": "Speed to lead",
  "status": "published",
  "trigger": {
    "type": "contact.created",
    "label": "Contact created"
  },
  "published_version": 3,
  "has_unpublished_changes": false,
  "step_count": 5,
  "step_types": [
    "email.send",
    "notify.team",
    "wait",
    "task.create",
    "end"
  ],
  "sends": [
    "email",
    "team"
  ],
  "enrollments": {
    "total": 214,
    "active": 9,
    "completed": 198
  },
  "last_run_at": "2026-10-07T16:42:10.000Z",
  "created_at": "2026-09-02T10:00:00.000Z",
  "updated_at": "2026-10-01T08:15:00.000Z"
}

Enroll contacts

POST/v1/automations/{id}/enrollmentsautomations:enroll

Adds Thrive contacts to a published automation, exactly as Enroll contacts does in the hub, and answers at once with who was enrolled and who was skipped, and why. Each enrolled contact gets a run; its steps begin within about a minute and send real messages: the emails the automation sends, and its texts where texting is set up. Follow them with List an automation's runs. Steps that use AI are billed to the account like any other AI use.

A contact is skipped, not refused, when it is not in this account or is archived (not_found), was erased from Thrive at their request (erased), is already in this automation (already_enrolled), has been through it before and the automation runs once per contact (enrolled_before), or is already in as many automations at once as the project allows (too_many_automations). Contacts on do-not-contact or opted out are enrolled, and the steps that would message them are skipped (see the Automations section).

The automation must be published: a draft or a paused one answers 409 automation_not_published and enrolls nobody. The trigger does not matter, and its Run only if filter is not applied: enrolling by hand is an instruction, as in the hub.

Idempotency-Key is required. Send a new unique value for each request and the same value again if you retry: a retry with the same key and the same body returns the first answer, with Idempotent-Replay: true, and enrolls nobody twice. If the first request did not finish, the retry answers 409 idempotency_outcome_unknown and nothing is done again; list the automation's runs to see whether it did. Keys are remembered for 24 hours. Run history shows each run as started through the API, with the key's name.

Parameters

NameTypeDescription
id
path, required
string (uuid)The automation's id.
Idempotency-Key
header, required
stringA unique value for this request, 8 to 64 printable characters (a UUID is ideal). Send the same value again to retry without enrolling anyone twice.

Response 200

FieldTypeDescription
automation_idstring (uuid)
enrolledintegerHow many contacts were enrolled.
runsarray of objectThe run each enrolled contact got.
runs[].idstring (uuid)The run, for Get an automation run.
runs[].contact_idstring (uuid)
skippedarray of objectThe contacts not enrolled, each with why.
skipped[].contact_idstring (uuid)
skipped[].reasonstring

Errors

  • 400 The request is not valid.
  • 401 No valid API key.
  • 403 The key may not do this.
  • 404 Not found in this account.
  • 409 The automation is not published, or the Idempotency-Key is busy or its first request did not finish.
  • 422 The body or the Idempotency-Key cannot be used.
  • 429 Too many requests.
  • 500 Something went wrong on our side.

The codes are listed under Errors.

cURL
curl -X POST "https://api.veragen.ai/v1/automations/{id}/enrollments" \
  -H "Authorization: Bearer $VERAGEN_API_KEY" \
  -H "Idempotency-Key: 6f1d2c3b-enroll-2026-10-07-1642" \
  -H "Content-Type: application/json" \
  -d '{"contact_ids": ["1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f", "2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"]}'
JavaScript
const res = await fetch("https://api.veragen.ai/v1/automations/{id}/enrollments", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.VERAGEN_API_KEY}`, "Idempotency-Key": "6f1d2c3b-enroll-2026-10-07-1642", "Content-Type": "application/json" },
  body: JSON.stringify({
    "contact_ids": [
      "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
      "2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"
    ]
  }),
});
const data = await res.json();
Python
import os, requests

res = requests.post(
    "https://api.veragen.ai/v1/automations/{id}/enrollments",
    headers={"Authorization": f"Bearer {os.environ['VERAGEN_API_KEY']}", "Idempotency-Key": "6f1d2c3b-enroll-2026-10-07-1642"},
    json={
        "contact_ids": [
            "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
            "2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"
        ]
    },
)
data = res.json()
Response 200
{
  "automation_id": "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
  "enrolled": 1,
  "runs": [
    {
      "id": "7b2c3d4e-5f60-4b7c-9d8e-0f1a2b3c4d5e",
      "contact_id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"
    }
  ],
  "skipped": [
    {
      "contact_id": "2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a",
      "reason": "already_enrolled"
    }
  ]
}

List an automation's runs

GET/v1/automations/{id}/runsautomations:read

The automation's runs, one per contact each time they went through it, newest first, however they started: by its trigger, enrolled in the hub, or through the API. These are the rows Run history shows. Test runs from the builder are included, with test: true.

Parameters

NameTypeDescription
id
path, required
string (uuid)The automation's id.
limit
query
integerHow many to return. 1 to 100. Default 25.
cursor
query
stringThe next_cursor from the previous page. Leave it out for the first page.
status
query
stringOnly runs with this status. One of running, waiting, succeeded, failed, stopped, skipped.

Response 200

FieldTypeDescription
dataarray of object
data[].idstring (uuid)
data[].automation_idstring (uuid)
data[].contactobject or nullWho it runs for; null for a scheduled run with no contact.
data[].contact.idstring (uuid)
data[].contact.namestring
data[].statusstringAs Run history shows it: running, waiting (at a wait, until next_at), succeeded, failed, stopped, or skipped (its Run only if filter did not match).
data[].status_labelstringThe same, in the words Run history uses.
data[].started_bystringWhat started it, as Run history's Started by column says: the trigger, the person who enrolled the contact, or the API key.
data[].testbooleantrue for a test run from the builder, which sends only to the person testing.
data[].versionintegerThe published version it runs.
data[].started_atstring (date-time)
data[].finished_atstring or null (date-time)null until it ends.
data[].next_atstring or null (date-time)For a waiting run, when it carries on; null otherwise.
data[].errorstring or nullWhy it failed or was stopped.
has_moreboolean
next_cursorstring or null

Errors

  • 400 The request is not valid.
  • 401 No valid API key.
  • 403 The key may not do this.
  • 404 Not found in this account.
  • 429 Too many requests.
  • 500 Something went wrong on our side.

The codes are listed under Errors.

cURL
curl "https://api.veragen.ai/v1/automations/{id}/runs?limit=25" \
  -H "Authorization: Bearer $VERAGEN_API_KEY"
JavaScript
const res = await fetch("https://api.veragen.ai/v1/automations/{id}/runs?limit=25", {
  headers: { Authorization: `Bearer ${process.env.VERAGEN_API_KEY}` },
});
const data = await res.json();
Python
import os, requests

res = requests.get(
    "https://api.veragen.ai/v1/automations/{id}/runs",
    params={"limit": 25},
    headers={"Authorization": f"Bearer {os.environ['VERAGEN_API_KEY']}"},
)
data = res.json()
Response 200
{
  "data": [
    {
      "id": "7b2c3d4e-5f60-4b7c-9d8e-0f1a2b3c4d5e",
      "automation_id": "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
      "contact": {
        "id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
        "name": "Maya Lindqvist"
      },
      "status": "waiting",
      "status_label": "Waiting until 2026-10-09 16:42 UTC",
      "started_by": "Enrolled through the API by key “Website sign-ups” (vg_live_3fa9…)",
      "test": false,
      "version": 3,
      "started_at": "2026-10-07T16:42:10.000Z",
      "finished_at": null,
      "next_at": "2026-10-09T16:42:10.000Z",
      "error": null
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Get an automation run

GET/v1/automation-runs/{id}automations:read

One run of an automation, with each step it has run: its name, kind, status, how long it took and any error. A step that was skipped (an email to someone who unsubscribed, a text to someone who has not agreed to texts) is skipped. What a step sent (addresses, subject, text) is not included.

Parameters

NameTypeDescription
id
path, required
string (uuid)The run's id, from List an automation's runs or Enroll contacts.

Response 200

FieldTypeDescription
idstring (uuid)
automation_idstring (uuid)
contactobject or nullWho it runs for; null for a scheduled run with no contact.
contact.idstring (uuid)
contact.namestring
statusstringAs Run history shows it: running, waiting (at a wait, until next_at), succeeded, failed, stopped, or skipped (its Run only if filter did not match).
status_labelstringThe same, in the words Run history uses.
started_bystringWhat started it, as Run history's Started by column says: the trigger, the person who enrolled the contact, or the API key.
testbooleantrue for a test run from the builder, which sends only to the person testing.
versionintegerThe published version it runs.
started_atstring (date-time)
finished_atstring or null (date-time)null until it ends.
next_atstring or null (date-time)For a waiting run, when it carries on; null otherwise.
errorstring or nullWhy it failed or was stopped.
failed_step_idstring or nullFor a failed run, the step it stopped at.
stepsarray of objectThe steps it has run, in order. What a step sent (addresses, text) is not included.
steps[].step_idstringThe step's id in the automation; t is the trigger.
steps[].namestringThe step's name in the builder.
steps[].typestringWhat kind of step it is, such as email.send, sms.send, wait or trigger.
steps[].statusstring
steps[].started_atstring or null (date-time)
steps[].msintegerHow long the step took.
steps[].errorstring or nullWhy the step failed, or null.

Errors

  • 400 The request is not valid.
  • 401 No valid API key.
  • 403 The key may not do this.
  • 404 Not found in this account.
  • 429 Too many requests.
  • 500 Something went wrong on our side.

The codes are listed under Errors.

cURL
curl "https://api.veragen.ai/v1/automation-runs/{id}" \
  -H "Authorization: Bearer $VERAGEN_API_KEY"
JavaScript
const res = await fetch("https://api.veragen.ai/v1/automation-runs/{id}", {
  headers: { Authorization: `Bearer ${process.env.VERAGEN_API_KEY}` },
});
const data = await res.json();
Python
import os, requests

res = requests.get(
    "https://api.veragen.ai/v1/automation-runs/{id}",
    headers={"Authorization": f"Bearer {os.environ['VERAGEN_API_KEY']}"},
)
data = res.json()
Response 200
{
  "id": "7b2c3d4e-5f60-4b7c-9d8e-0f1a2b3c4d5e",
  "automation_id": "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
  "contact": {
    "id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
    "name": "Maya Lindqvist"
  },
  "status": "waiting",
  "status_label": "Waiting until 2026-10-09 16:42 UTC",
  "started_by": "Enrolled through the API by key “Website sign-ups” (vg_live_3fa9…)",
  "test": false,
  "version": 3,
  "started_at": "2026-10-07T16:42:10.000Z",
  "finished_at": null,
  "next_at": "2026-10-09T16:42:10.000Z",
  "error": null,
  "failed_step_id": null,
  "steps": [
    {
      "step_id": "t",
      "name": "Contact created",
      "type": "trigger",
      "status": "ok",
      "started_at": "2026-10-07T16:42:10.000Z",
      "ms": 0,
      "error": null
    },
    {
      "step_id": "s1",
      "name": "Email: thanks for getting in touch",
      "type": "email.send",
      "status": "ok",
      "started_at": "2026-10-07T16:42:31.000Z",
      "ms": 412,
      "error": null
    },
    {
      "step_id": "s2",
      "name": "Tell the owner",
      "type": "notify.team",
      "status": "ok",
      "started_at": "2026-10-07T16:42:32.000Z",
      "ms": 268,
      "error": null
    },
    {
      "step_id": "s3",
      "name": "Wait 2 days",
      "type": "wait",
      "status": "waiting",
      "started_at": "2026-10-07T16:42:32.000Z",
      "ms": 0,
      "error": null
    }
  ]
}