All docs

Assert / Upsert

Idempotent create-or-update for contacts, companies, and deals

Assert endpoints provide idempotent create-or-update (upsert) semantics. If a matching record exists it gets updated; otherwise a new one is created. This makes them safe to call repeatedly without creating duplicates.


Assert Contact

PUT /contacts/assert

Create or update a single contact. Matches by work_email first, then falls back to linkedin_url.

Requires list_id and at least one match key (work_email or linkedin_url).

Request Body

FieldTypeRequiredDescription
list_idintegerYesTarget list ID
work_emailstringNo*Work email (primary match key)
linkedin_urlstringNo*LinkedIn URL (fallback match key)
first_namestringNoFirst name
last_namestringNoLast name
job_titlestringNoJob title
company_domainstringNoCompany domain
Any other contact field

*At least one of work_email or linkedin_url is required.

Example

cURL

curl -X PUT "https://api.graph8.com/api/v1/contacts/assert" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "list_id": 5,
    "work_email": "[email protected]",
    "first_name": "Jane",
    "last_name": "Doe",
    "job_title": "VP of Sales"
  }'

Python

response = requests.put(
    f"{BASE_URL}/contacts/assert",
    headers=HEADERS,
    json={
        "list_id": 5,
        "work_email": "[email protected]",
        "first_name": "Jane",
        "last_name": "Doe",
        "job_title": "VP of Sales"
    }
)

Response

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

The action field tells you what happened: "created" for a new record, "updated" for an existing match.


Batch Assert Contacts

PUT /contacts/assert/batch

Create or update up to 100 contacts at once. Each contact should include work_email or linkedin_url as a match key.

Request Body

FieldTypeRequiredDescription
list_idintegerYesTarget list ID
contactsobject[]YesUp to 100 contact objects

Example

cURL

curl -X PUT "https://api.graph8.com/api/v1/contacts/assert/batch" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "list_id": 5,
    "contacts": [
      {"work_email": "[email protected]", "first_name": "Jane"},
      {"work_email": "[email protected]", "first_name": "Bob"}
    ]
  }'

Python

response = requests.put(
    f"{BASE_URL}/contacts/assert/batch",
    headers=HEADERS,
    json={
        "list_id": 5,
        "contacts": [
            {"work_email": "[email protected]", "first_name": "Jane"},
            {"work_email": "[email protected]", "first_name": "Bob"}
        ]
    }
)

Response

{
  "data": {
    "total": 2,
    "created": 2,
    "updated": 0,
    "errors": []
  }
}

Assert Company

PUT /companies/assert

Create or update a company. Matches by domain first, then falls back to linkedin_url.

Request Body

FieldTypeRequiredDescription
list_idintegerYesTarget list ID
domainstringNo*Company domain (primary match key)
linkedin_urlstringNo*LinkedIn URL (fallback match key)
namestringNoCompany name
industrystringNoIndustry
employee_countintegerNoNumber of employees
Any other company field

*At least one of domain or linkedin_url is required.

Example

cURL

curl -X PUT "https://api.graph8.com/api/v1/companies/assert" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "list_id": 5,
    "domain": "acme.com",
    "name": "Acme Inc",
    "industry": "Technology"
  }'

Python

response = requests.put(
    f"{BASE_URL}/companies/assert",
    headers=HEADERS,
    json={
        "list_id": 5,
        "domain": "acme.com",
        "name": "Acme Inc",
        "industry": "Technology"
    }
)

Response

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

Batch Assert Companies

PUT /companies/assert/batch

Create or update up to 100 companies at once.

Request Body

FieldTypeRequiredDescription
list_idintegerYesTarget list ID
companiesobject[]YesUp to 100 company objects

Assert Deal

PUT /deals/assert

Create or update a deal. Matches by name + company_id.

Request Body

FieldTypeRequiredDescription
namestringYesDeal name (match key)
company_idstringNoCompany ID (match key, combined with name)
amountnumberNoMonetary value
currencystringNoCurrency code
stage_idstringNoPipeline stage ID
pipeline_idstringNoPipeline ID
descriptionstringNoDeal description
close_datestringNoExpected close date (ISO 8601)

Example

cURL

curl -X PUT "https://api.graph8.com/api/v1/deals/assert" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Enterprise Deal",
    "amount": 50000,
    "company_id": "comp-123"
  }'

Python

response = requests.put(
    f"{BASE_URL}/deals/assert",
    headers=HEADERS,
    json={
        "name": "Enterprise Deal",
        "amount": 50000,
        "company_id": "comp-123"
    }
)

Response

{
  "data": {
    "action": "created",
    "deal_id": "deal-new-abc"
  }
}

Assert Deal-Contact Association

PUT /contacts/{contact_id}/deals/assert

Link a contact to a deal. Idempotent — if the association already exists, returns "exists" without error.

Request Body

FieldTypeRequiredDescription
deal_idstringYesDeal ID to associate

Example

cURL

curl -X PUT "https://api.graph8.com/api/v1/contacts/12345/deals/assert" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"deal_id": "deal-abc"}'

Response

{
  "data": {
    "action": "created",
    "deal_id": "deal-abc",
    "contact_id": 12345
  }
}

The action field returns "created" for a new link, or "exists" if the association was already present.

Build with graph8