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

Contacts

The people in Thrive, the account's CRM: list, read, create and update them. A key reaches every contact in the account. Contacts created through the API are unassigned until someone in Thrive assigns them. The API never deletes a contact, never lifts a do-not-contact or an email opt-out, and never sends anything.

List contacts

GET/v1/contactscontacts:read

The account's contacts in Thrive, newest first. Deleted (archived) contacts are left out. Filters combine: q searches names, emails, titles and company names; email finds the one contact with that address; updated_since returns only contacts changed at or after a time, for keeping another system in step.

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.
q
query
stringContains this text, ignoring case (1 to 100 characters).
email
query
stringExactly this email address, ignoring case.
lifecycle
query
stringOnly contacts at this lifecycle stage. One of subscriber, lead, marketing_qualified, sales_qualified, opportunity, customer, evangelist, other.
updated_since
query
stringOnly contacts changed at or after this time: a date (YYYY-MM-DD, UTC) or an ISO-8601 date and time with a zone.

Response 200

FieldTypeDescription
dataarray of object
data[].idstring (uuid)
data[].full_namestring
data[].first_namestring or null
data[].last_namestring or null
data[].titlestring or null
data[].emailstring or null
data[].phonestring or null
data[].company_idstring or null (uuid)
data[].statusstringdo_not_contact: never contact them. Set in Thrive only; the API cannot change it.
data[].lifecycle_stagestring or null
data[].lead_sourcestring or null
data[].email_opt_outbooleantrue when they have opted out of email.
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 No endpoint at that path.
  • 429 Too many requests.
  • 500 Something went wrong on our side.

The codes are listed under Errors.

cURL
curl "https://api.veragen.ai/v1/contacts?limit=25" \
  -H "Authorization: Bearer $VERAGEN_API_KEY"
JavaScript
const res = await fetch("https://api.veragen.ai/v1/contacts?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/contacts",
    params={"limit": 25},
    headers={"Authorization": f"Bearer {os.environ['VERAGEN_API_KEY']}"},
)
data = res.json()
Response 200
{
  "data": [
    {
      "id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
      "full_name": "Maya Lindqvist",
      "first_name": "Maya",
      "last_name": "Lindqvist",
      "title": "Head of Operations",
      "email": "maya@northwind.example",
      "phone": "+1 415 555 0134",
      "company_id": "6e5d4c3b-2a1f-4e0d-9c8b-7a6f5e4d3c2b",
      "status": "active",
      "lifecycle_stage": "sales_qualified",
      "lead_source": "Website form",
      "email_opt_out": false,
      "created_at": "2026-09-28T16:42:10.000Z",
      "updated_at": "2026-10-03T09:15:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Create a contact

POST/v1/contactscontacts:write

Adds a contact to Thrive and answers 201 with it. It needs a name: full_name, or first_name and last_name. It is unassigned (no owner) until someone in Thrive assigns it, and its history says it was created through the API.

One contact per email address. If a contact in the account already has the address, nothing is created or changed: the answer is 409 contact_exists with that contact's id in resource_id, so you can update it with PATCH. An address that belongs to someone erased from Thrive at their request is refused with 409 contact_do_not_contact.

Idempotency-Key is optional and recommended: a retry with the same key and the same request returns the first answer, with Idempotent-Replay: true, and never makes a second contact.

Parameters

NameTypeDescription
Idempotency-Key
header
stringOptional: a unique value for this request, 8 to 64 printable characters (a UUID is ideal). Send the same value again to retry without creating a second one.

Response 201

FieldTypeDescription
idstring (uuid)
full_namestring
first_namestring or null
last_namestring or null
titlestring or null
emailstring or null
phonestring or null
company_idstring or null (uuid)
statusstringdo_not_contact: never contact them. Set in Thrive only; the API cannot change it.
lifecycle_stagestring or null
lead_sourcestring or null
email_opt_outbooleantrue when they have opted out of email.
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.
  • 409 A contact already has that email address, or the Idempotency-Key is busy.
  • 422 A body field 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/contacts" \
  -H "Authorization: Bearer $VERAGEN_API_KEY" \
  -H "Idempotency-Key: 6f1d2c3b-page-2026-10-03-1420" \
  -H "Content-Type: application/json" \
  -d '{"first_name": "Maya", "last_name": "Lindqvist", "email": "maya@northwind.example", "title": "Head of Operations", "lead_source": "Website form"}'
JavaScript
const res = await fetch("https://api.veragen.ai/v1/contacts", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.VERAGEN_API_KEY}`, "Idempotency-Key": "6f1d2c3b-page-2026-10-03-1420", "Content-Type": "application/json" },
  body: JSON.stringify({
    "first_name": "Maya",
    "last_name": "Lindqvist",
    "email": "maya@northwind.example",
    "title": "Head of Operations",
    "lead_source": "Website form"
  }),
});
const data = await res.json();
Python
import os, requests

res = requests.post(
    "https://api.veragen.ai/v1/contacts",
    headers={"Authorization": f"Bearer {os.environ['VERAGEN_API_KEY']}", "Idempotency-Key": "6f1d2c3b-page-2026-10-03-1420"},
    json={
        "first_name": "Maya",
        "last_name": "Lindqvist",
        "email": "maya@northwind.example",
        "title": "Head of Operations",
        "lead_source": "Website form"
    },
)
data = res.json()
Response 201
{
  "id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
  "full_name": "Maya Lindqvist",
  "first_name": "Maya",
  "last_name": "Lindqvist",
  "title": "Head of Operations",
  "email": "maya@northwind.example",
  "phone": "+1 415 555 0134",
  "company_id": "6e5d4c3b-2a1f-4e0d-9c8b-7a6f5e4d3c2b",
  "status": "active",
  "lifecycle_stage": "sales_qualified",
  "lead_source": "Website form",
  "email_opt_out": false,
  "created_at": "2026-09-28T16:42:10.000Z",
  "updated_at": "2026-10-03T09:15:00.000Z"
}

Get a contact

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

One contact. A deleted (archived) contact is not found.

Parameters

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

Response 200

FieldTypeDescription
idstring (uuid)
full_namestring
first_namestring or null
last_namestring or null
titlestring or null
emailstring or null
phonestring or null
company_idstring or null (uuid)
statusstringdo_not_contact: never contact them. Set in Thrive only; the API cannot change it.
lifecycle_stagestring or null
lead_sourcestring or null
email_opt_outbooleantrue when they have opted out of email.
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/contacts/{id}" \
  -H "Authorization: Bearer $VERAGEN_API_KEY"
JavaScript
const res = await fetch("https://api.veragen.ai/v1/contacts/{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/contacts/{id}",
    headers={"Authorization": f"Bearer {os.environ['VERAGEN_API_KEY']}"},
)
data = res.json()
Response 200
{
  "id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
  "full_name": "Maya Lindqvist",
  "first_name": "Maya",
  "last_name": "Lindqvist",
  "title": "Head of Operations",
  "email": "maya@northwind.example",
  "phone": "+1 415 555 0134",
  "company_id": "6e5d4c3b-2a1f-4e0d-9c8b-7a6f5e4d3c2b",
  "status": "active",
  "lifecycle_stage": "sales_qualified",
  "lead_source": "Website form",
  "email_opt_out": false,
  "created_at": "2026-09-28T16:42:10.000Z",
  "updated_at": "2026-10-03T09:15:00.000Z"
}

Update a contact

PATCH/v1/contacts/{id}contacts:write

Changes only the fields you send; every other field is left as it is. Send null to clear a field (not full_name, which a contact always has). Changing first_name or last_name does not change full_name: send it too if it should change. An empty body changes nothing and returns the contact.

The API cannot lift an email opt-out (email_opt_out: false on a contact that has opted out is 409 opt_out_locked), and cannot change someone erased from Thrive at their request (409 contact_do_not_contact). Moving a contact to an email address another contact has is 409 contact_exists.

Parameters

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

Response 200

FieldTypeDescription
idstring (uuid)
full_namestring
first_namestring or null
last_namestring or null
titlestring or null
emailstring or null
phonestring or null
company_idstring or null (uuid)
statusstringdo_not_contact: never contact them. Set in Thrive only; the API cannot change it.
lifecycle_stagestring or null
lead_sourcestring or null
email_opt_outbooleantrue when they have opted out of email.
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.
  • 409 The change is not allowed for this contact.
  • 422 A body field cannot be used.
  • 429 Too many requests.
  • 500 Something went wrong on our side.

The codes are listed under Errors.

cURL
curl -X PATCH "https://api.veragen.ai/v1/contacts/{id}" \
  -H "Authorization: Bearer $VERAGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"lifecycle_stage": "customer", "title": "COO", "phone": null}'
JavaScript
const res = await fetch("https://api.veragen.ai/v1/contacts/{id}", {
  method: "PATCH",
  headers: { Authorization: `Bearer ${process.env.VERAGEN_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "lifecycle_stage": "customer",
    "title": "COO",
    "phone": null
  }),
});
const data = await res.json();
Python
import os, requests

res = requests.patch(
    "https://api.veragen.ai/v1/contacts/{id}",
    headers={"Authorization": f"Bearer {os.environ['VERAGEN_API_KEY']}"},
    json={
        "lifecycle_stage": "customer",
        "title": "COO",
        "phone": None
    },
)
data = res.json()
Response 200
{
  "id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
  "full_name": "Maya Lindqvist",
  "first_name": "Maya",
  "last_name": "Lindqvist",
  "title": "COO",
  "email": "maya@northwind.example",
  "phone": null,
  "company_id": "6e5d4c3b-2a1f-4e0d-9c8b-7a6f5e4d3c2b",
  "status": "active",
  "lifecycle_stage": "customer",
  "lead_source": "Website form",
  "email_opt_out": false,
  "created_at": "2026-09-28T16:42:10.000Z",
  "updated_at": "2026-10-03T09:15:00.000Z"
}