API reference
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.
/v1/contactscontacts:readThe 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.
| Name | Type | Description |
|---|---|---|
limitquery | integer | How many to return. 1 to 100. Default 25. |
cursorquery | string | The next_cursor from the previous page. Leave it out for the first page. |
qquery | string | Contains this text, ignoring case (1 to 100 characters). |
emailquery | string | Exactly this email address, ignoring case. |
lifecyclequery | string | Only contacts at this lifecycle stage. One of subscriber, lead, marketing_qualified, sales_qualified, opportunity, customer, evangelist, other. |
updated_sincequery | string | Only contacts changed at or after this time: a date (YYYY-MM-DD, UTC) or an ISO-8601 date and time with a zone. |
| Field | Type | Description |
|---|---|---|
data | array of object | |
data[].id | string (uuid) | |
data[].full_name | string | |
data[].first_name | string or null | |
data[].last_name | string or null | |
data[].title | string or null | |
data[].email | string or null | |
data[].phone | string or null | |
data[].company_id | string or null (uuid) | |
data[].status | string | do_not_contact: never contact them. Set in Thrive only; the API cannot change it. |
data[].lifecycle_stage | string or null | |
data[].lead_source | string or null | |
data[].email_opt_out | boolean | true when they have opted out of email. |
data[].created_at | string (date-time) | |
data[].updated_at | string (date-time) | |
has_more | boolean | |
next_cursor | string or null |
The codes are listed under Errors.
curl "https://api.veragen.ai/v1/contacts?limit=25" \
-H "Authorization: Bearer $VERAGEN_API_KEY"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();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(){
"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
}/v1/contactscontacts:writeAdds 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.
| Name | Type | Description |
|---|---|---|
Idempotency-Keyheader | string | Optional: 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. |
| Field | Type | Description |
|---|---|---|
id | string (uuid) | |
full_name | string | |
first_name | string or null | |
last_name | string or null | |
title | string or null | |
email | string or null | |
phone | string or null | |
company_id | string or null (uuid) | |
status | string | do_not_contact: never contact them. Set in Thrive only; the API cannot change it. |
lifecycle_stage | string or null | |
lead_source | string or null | |
email_opt_out | boolean | true when they have opted out of email. |
created_at | string (date-time) | |
updated_at | string (date-time) |
The codes are listed under Errors.
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"}'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();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(){
"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"
}/v1/contacts/{id}contacts:readOne contact. A deleted (archived) contact is not found.
| Name | Type | Description |
|---|---|---|
idpath, required | string (uuid) | The contact's id. |
| Field | Type | Description |
|---|---|---|
id | string (uuid) | |
full_name | string | |
first_name | string or null | |
last_name | string or null | |
title | string or null | |
email | string or null | |
phone | string or null | |
company_id | string or null (uuid) | |
status | string | do_not_contact: never contact them. Set in Thrive only; the API cannot change it. |
lifecycle_stage | string or null | |
lead_source | string or null | |
email_opt_out | boolean | true when they have opted out of email. |
created_at | string (date-time) | |
updated_at | string (date-time) |
The codes are listed under Errors.
curl "https://api.veragen.ai/v1/contacts/{id}" \
-H "Authorization: Bearer $VERAGEN_API_KEY"const res = await fetch("https://api.veragen.ai/v1/contacts/{id}", {
headers: { Authorization: `Bearer ${process.env.VERAGEN_API_KEY}` },
});
const data = await res.json();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(){
"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"
}/v1/contacts/{id}contacts:writeChanges 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.
| Name | Type | Description |
|---|---|---|
idpath, required | string (uuid) | The contact's id. |
| Field | Type | Description |
|---|---|---|
id | string (uuid) | |
full_name | string | |
first_name | string or null | |
last_name | string or null | |
title | string or null | |
email | string or null | |
phone | string or null | |
company_id | string or null (uuid) | |
status | string | do_not_contact: never contact them. Set in Thrive only; the API cannot change it. |
lifecycle_stage | string or null | |
lead_source | string or null | |
email_opt_out | boolean | true when they have opted out of email. |
created_at | string (date-time) | |
updated_at | string (date-time) |
The codes are listed under Errors.
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}'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();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(){
"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"
}