Skip to content

Contacts

Manage contacts in your graph8 workspace — your first-party CRM data.

Contacts get into your workspace through several paths: saving results from the Search endpoints, importing from a CSV or CRM, creating manually via the API, or automatically during enrichment. Once a contact is in your workspace, these endpoints give you full read/write access at no credit cost.


List Contacts

GET /contacts

Returns a paginated list of contacts.

Query Parameters

ParameterTypeDefaultDescription
pageinteger1Page number (1-indexed)
limitinteger50Items per page (1-200)
emailstringFilter by work email (exact match)
list_idintegerFilter by list membership
namestringFilter by first or last name (partial match, case-insensitive)
job_titlestringFilter by job title (partial match, case-insensitive)
seniority_levelstringFilter by seniority level (exact match)
company_namestringFilter by company name (partial match, case-insensitive)
company_idintegerFilter by company ID
countrystringFilter by country (exact match)
statestringFilter by state (exact match)
citystringFilter by city (partial match, case-insensitive)
job_departmentstringFilter by job department (partial match, case-insensitive)
industrystringFilter by company industry (partial match, case-insensitive)
include_custom_fieldsbooleanfalseWhen true, each contact gains a custom_fields object of its custom column values keyed by column slug (name from GET /fields). Default false keeps the legacy shape and adds no extra query.

All filters are optional and can be combined. When multiple filters are provided, results must match all of them (AND logic).

Example

Terminal window
# Basic pagination
curl "https://be.graph8.com/api/v1/contacts?limit=10&page=1" \
-H "Authorization: Bearer $API_KEY"
# Search by name and company
curl "https://be.graph8.com/api/v1/contacts?name=jane&company_name=acme" \
-H "Authorization: Bearer $API_KEY"
# Filter by seniority and country
curl "https://be.graph8.com/api/v1/contacts?seniority_level=VP&country=US" \
-H "Authorization: Bearer $API_KEY"
# Filter by department and industry
curl "https://be.graph8.com/api/v1/contacts?job_department=Engineering&industry=SaaS" \
-H "Authorization: Bearer $API_KEY"

Response

{
"data": [
{
"id": 101,
"first_name": "Jane",
"last_name": "Doe",
"work_email": "jane@acme.com",
"direct_phone": "+1...",
"mobile_phone": "+1...",
"job_title": "VP Sales",
"job_department": "Sales",
"seniority_level": "VP",
"linkedin_url": "https://...",
"city": "Berlin",
"state": "BE",
"country": "DE",
"company_id": 1,
"owner_id": "user_x",
"owner_name": "John",
"custom_fields": null
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 1,
"has_next": false
}
}

When called with ?include_custom_fields=true, custom_fields is an object like {"udo_lead_score_1712000000": "92", "udo_persona_1712000111": "Champion"} (only set values appear; keys are the column slug). Rows with no custom values return {}.


Get Contact

GET /contacts/{contact_id}

Returns detailed information about a single contact, including associated company data.

Path Parameters

ParameterTypeDescription
contact_idintegerContact ID

Query Parameters

ParameterTypeDefaultDescription
include_custom_fieldsbooleanfalseWhen true, the response gains a custom_fields object (same shape as the list endpoint). Default omits it (custom_fields: null).

Example

Terminal window
curl "https://be.graph8.com/api/v1/contacts/1" \
-H "Authorization: Bearer $API_KEY"

Response

{
"data": {
"id": 1,
"first_name": "Jane",
"last_name": "Smith",
"full_name": "Jane Smith",
"work_email": "jane@acme.com",
"personal_emails": null,
"direct_phone": "+1-555-0100",
"mobile_phone": null,
"job_title": "VP of Engineering",
"job_department": "Engineering",
"seniority_level": "VP",
"linkedin_url": "https://linkedin.com/in/janesmith",
"twitter_url": null,
"facebook_url": null,
"city": "San Francisco",
"state": "CA",
"country": "US",
"about": null,
"confidence_score": 0.95,
"company": {
"id": 42,
"name": "Acme Inc",
"domain": "acme.com",
"website": "https://acme.com",
"industry": "Technology",
"employee_count": "500",
"city": "San Francisco",
"state": "CA",
"country": "US",
"linkedin_url": "https://linkedin.com/company/acme",
"logo_url": "https://logo.clearbit.com/acme.com"
},
"meta_data": {},
"custom_fields": null,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-02-01T14:20:00Z"
}
}

Errors

StatusMeaning
404Contact not found

Create Contact

POST /contacts

Create a new contact and add it to a list.

Request Body

FieldTypeRequiredDescription
list_idintegerYesTarget list to add the contact to
first_namestringNoFirst name
last_namestringNoLast name
work_emailstringNoWork email address
personal_emailsstringNoPersonal email addresses
direct_phonestringNoDirect phone number
mobile_phonestringNoMobile phone number
job_titlestringNoJob title
job_departmentstringNoDepartment
seniority_levelstringNoSeniority level
linkedin_urlstringNoLinkedIn profile URL
citystringNoCity
statestringNoState or province
countrystringNoCountry
company_domainstringNoCompany domain for matching

Example

Terminal window
curl -X POST "https://be.graph8.com/api/v1/contacts" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"list_id": 5,
"first_name": "John",
"last_name": "Doe",
"work_email": "john@techcorp.io",
"job_title": "CTO",
"company_domain": "techcorp.io"
}'

Response 201 Created

{
"data": {
"status": "ok",
"count": 1,
"validation_errors": []
}
}

Update Contact

PATCH /contacts/{contact_id}

Update one or more fields on a contact. Only include the fields you want to change.

Path Parameters

ParameterTypeDescription
contact_idintegerContact ID

Request Body

All fields are optional. Include only the fields to update.

FieldTypeDescription
first_namestringFirst name
last_namestringLast name
work_emailstringWork email address
direct_phonestringDirect phone number
mobile_phonestringMobile phone number
job_titlestringJob title
job_departmentstringDepartment
seniority_levelstringSeniority level
linkedin_urlstringLinkedIn profile URL
citystringCity
statestringState or province
countrystringCountry

Example

Terminal window
curl -X PATCH "https://be.graph8.com/api/v1/contacts/1" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_title": "CTO", "city": "New York"}'

Response

{
"data": {
"updated": 1
}
}

Errors

StatusMeaning
400No fields provided to update
404Contact not found

Delete Contact

DELETE /contacts/{contact_id}

Soft-delete a contact. The contact is marked as deleted but not permanently removed.

Path Parameters

ParameterTypeDescription
contact_idintegerContact ID

Example

Terminal window
curl -X DELETE "https://be.graph8.com/api/v1/contacts/1" \
-H "Authorization: Bearer $API_KEY"

Response

{
"data": {
"deleted": true
}
}

Errors

StatusMeaning
404Contact not found

Upsert Contact

PUT /contacts/assert

Single contact upsert. Matches an existing contact by work_email first, then by linkedin_url. Creates a new contact if no match is found.

Request Body

FieldTypeRequiredDescription
list_idintegerYesTarget list
work_emailstringConditionalWork email — at least one of work_email or linkedin_url is required
linkedin_urlstringConditionalLinkedIn profile URL — at least one of work_email or linkedin_url is required
first_namestringNoFirst name
last_namestringNoLast name
personal_emailsstringNoPersonal email addresses
direct_phonestringNoDirect phone number
mobile_phonestringNoMobile phone number
job_titlestringNoJob title
job_departmentstringNoDepartment
seniority_levelstringNoSeniority level
citystringNoCity
statestringNoState or province
countrystringNoCountry
company_domainstringNoCompany domain for matching

Example

Terminal window
curl -X PUT "https://be.graph8.com/api/v1/contacts/assert" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"list_id": 12345,
"work_email": "jane@acme.com",
"first_name": "Jane",
"last_name": "Doe",
"job_title": "VP Sales",
"company_domain": "acme.com"
}'

Response

{ "data": { "action": "created", "count": 1 } }

The action field is either "created" or "updated".


Bulk Upsert Contacts

PUT /contacts/assert/batch

Bulk upsert of up to 100 contacts. Uses the same match keys as the single upsert (work_email then linkedin_url).

Request Body

FieldTypeRequiredDescription
list_idintegerYesTarget list for all contacts in this batch
contactsarrayYesList of contact objects (1–100 items). Each item follows the same field set as PUT /contacts/assert, with at least one of work_email or linkedin_url per row.

Example

Terminal window
curl -X PUT "https://be.graph8.com/api/v1/contacts/assert/batch" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"list_id": 12345,
"contacts": [
{
"work_email": "alice@acme.com",
"first_name": "Alice",
"last_name": "Smith",
"job_title": "CEO",
"company_domain": "acme.com"
},
{
"linkedin_url": "https://linkedin.com/in/bobdoe",
"first_name": "Bob",
"last_name": "Doe"
}
]
}'

Response

{ "data": { "total": 100, "created": 87, "updated": 13, "errors": [] } }

List Contact Columns

GET /contacts/columns

List custom contact columns defined in your workspace.

Query Parameters

ParameterTypeDefaultDescription
list_idintegerFilter columns by list. Omit to return all (including global) columns.

Example

Terminal window
curl "https://be.graph8.com/api/v1/contacts/columns" \
-H "Authorization: Bearer $API_KEY"
# Filter by list
curl "https://be.graph8.com/api/v1/contacts/columns?list_id=12345" \
-H "Authorization: Bearer $API_KEY"

Response

{
"data": [
{
"id": 1,
"title": "Job Level",
"name": "job_level",
"data_type": "text",
"is_global": true
}
]
}

Create Contact Column

POST /contacts/columns/create

Create a custom contact column. Optionally provision an enrichment waterfall pipeline in the same request so the column is immediately enrichable.

Request Body

FieldTypeRequiredDescription
titlestringYesDisplay name for the column
data_typestringYesColumn data type — currently only "text" is supported
list_idintegerNoScope the column to a specific list. Omit (or null) for a global column. Required when enrichment is provided (waterfall pipelines are list-scoped).
created_bystringNoEmail of the user creating the column
enrichmentobjectNoEnrichment waterfall config — strongly recommended for enrichable columns. When supplied, a companion waterfall pipeline is provisioned in the same request so the UI Enrich button and POST /enrichment/waterfall/enrich work out-of-the-box. Without it the column exists but is not enrichable until you call POST /enrichment/waterfall/configs.

enrichment object

FieldTypeDescription
typestringEnrichment type — use "waterfall"
providersarrayOrdered list of provider slugs. Mutually exclusive with steps. Allowed values: graph8, hunter, prospeo, dropcontact, icypeas, leadmagic, emaillistverify, apollo, lusha, rocketreach.
email_verificationobjectOptional email verification config (see below)

enrichment.email_verification object

FieldTypeDescription
providerstringVerification provider (e.g. "zerobounce")
enabledbooleanWhether verification is active
use_system_credentialsbooleanUse the platform’s credentials instead of your own
accept_catchallbooleanWhether to accept catch-all addresses
valid_statusesarrayList of statuses to accept (e.g. ["valid"])

Example

Terminal window
# Basic column (no enrichment)
curl -X POST "https://be.graph8.com/api/v1/contacts/columns/create" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Job Level",
"data_type": "text",
"created_by": "user@graph8.com"
}'
# Column with enrichment waterfall
curl -X POST "https://be.graph8.com/api/v1/contacts/columns/create" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Job Level",
"data_type": "text",
"list_id": 12345,
"created_by": "user@graph8.com",
"enrichment": {
"type": "waterfall",
"providers": ["hunter", "prospeo"],
"email_verification": {
"provider": "zerobounce",
"enabled": true,
"use_system_credentials": true,
"accept_catchall": false,
"valid_statuses": ["valid"]
}
}
}'

Response 201 Created

Without enrichment:

{
"data": {
"id": 1,
"title": "Job Level",
"data_type": "text",
"name": "job_level",
"object_id": "12345",
"is_global": false,
"enrichment": null
}
}

With enrichment:

{
"data": {
"id": 1,
"title": "Job Level",
"data_type": "text",
"name": "job_level",
"object_id": "12345",
"is_global": false,
"enrichment": {
"column_id": "job_level",
"config_id": "uuid",
"name": "Job Level Enrichment",
"type": "waterfall",
"providers": ["hunter", "prospeo"],
"email_verification": { "enabled": true, "provider": "zerobounce" }
}
}
}