Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.tented.ai/llms.txt

Use this file to discover all available pages before exploring further.

Endpoints

POST /v1/contacts
POST /v1/contacts/update
POST /v1/contacts/delete
DELETE /v1/contacts
Use these endpoints to sync CRM contacts from external systems. Each request accepts an items array with up to 25 records.
Contact writes support partial success. Tented processes each item independently and returns accepted or rejected results in request order.

Create Contacts

POST /v1/contacts

Request Body

FieldTypeRequiredNotes
itemsarrayYesBetween 1 and 25 contacts

Create Item Fields

FieldTypeRequiredNotes
emailstringConditionallyEither email or phone is required
phonestringConditionallyEither email or phone is required
client_item_idstringNoYour own per-item identifier for reconciliation
display_namestringNoMaximum 255 characters
first_namestringNoMaximum 255 characters
last_namestringNoMaximum 255 characters
companystringNoMaximum 255 characters
job_titlestringNoMaximum 255 characters
addressstringNoMaximum 500 characters
address2stringNoMaximum 255 characters
citystringNoMaximum 255 characters
statestringNoMaximum 255 characters
countrystringNoMaximum 255 characters
zip_codestringNoMaximum 50 characters
lead_statusstringNoMaximum 100 characters
lifecycle_stagestringNoMaximum 100 characters
tented_scorenumber | nullNoOptional contact score
unsubscribedbooleanNoDefaults to false when omitted
marketing_email_subscribedboolean | nullNoEmail subscription state
marketing_sms_subscribedboolean | nullNoSMS subscription state

Create Request Example

curl --request POST \
  --url https://api.tented.ai/v1/contacts \
  --header "Authorization: Bearer $TENTED_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "items": [
      {
        "client_item_id": "crm-row-001",
        "email": "jane@example.com",
        "first_name": "Jane",
        "last_name": "Doe",
        "company": "Acme",
        "job_title": "CEO"
      },
      {
        "client_item_id": "crm-row-002",
        "phone": "+15551234567",
        "display_name": "Sam Rivera"
      }
    ]
  }'

Create Response Example

201 Created
{
  "status": "completed",
  "created_count": 2,
  "rejected_count": 0,
  "items": [
    {
      "index": 0,
      "client_item_id": "crm-row-001",
      "contact_id": "11111111-1111-4111-8111-111111111111",
      "status": "created",
      "contact": {
        "contact_id": "11111111-1111-4111-8111-111111111111",
        "display_name": "Jane Doe",
        "first_name": "Jane",
        "last_name": "Doe",
        "email": "jane@example.com",
        "phone": null,
        "company": "Acme",
        "job_title": "CEO",
        "original_source": "Public API",
        "created_at": "2026-04-30T12:00:00.000Z",
        "updated_at": "2026-04-30T12:00:00.000Z"
      }
    },
    {
      "index": 1,
      "client_item_id": "crm-row-002",
      "contact_id": "22222222-2222-4222-8222-222222222222",
      "status": "created",
      "contact": {
        "contact_id": "22222222-2222-4222-8222-222222222222",
        "display_name": "Sam Rivera",
        "email": null,
        "phone": "+15551234567",
        "original_source": "Public API",
        "created_at": "2026-04-30T12:00:00.000Z",
        "updated_at": "2026-04-30T12:00:00.000Z"
      }
    }
  ]
}

Duplicate Contacts

Tented checks normalized email and phone values before creating a contact. If an item matches an existing contact, that item is rejected with error_code: "conflict".
{
  "index": 0,
  "client_item_id": "crm-row-001",
  "status": "rejected",
  "error_code": "conflict",
  "message": "Contact already exists",
  "existing_contact": {
    "contact_id": "11111111-1111-4111-8111-111111111111",
    "display_name": "Jane Doe",
    "email": "jane@example.com",
    "phone": null
  }
}

Update Contacts

POST /v1/contacts/update

Request Body

FieldTypeRequiredNotes
itemsarrayYesBetween 1 and 25 update records

Update Item Fields

FieldTypeRequiredNotes
contact_iduuidYesContact to update
client_item_idstringNoYour own per-item identifier for reconciliation
emailstring | nullNoSet to null to clear, as long as phone remains present
phonestring | nullNoSet to null to clear, as long as email remains present
display_namestring | nullNoMaximum 255 characters
first_namestringNoMaximum 255 characters
last_namestringNoMaximum 255 characters
email_domainstring | nullNoUsually derived automatically from email
companystringNoMaximum 255 characters
job_titlestringNoMaximum 255 characters
addressstringNoMaximum 500 characters
address2stringNoMaximum 255 characters
citystringNoMaximum 255 characters
statestringNoMaximum 255 characters
countrystringNoMaximum 255 characters
zip_codestringNoMaximum 50 characters
original_sourcestring | nullNoMaximum 255 characters
original_source_detailstring | nullNoMaximum 255 characters
lead_statusstringNoMaximum 100 characters
lifecycle_stagestringNoMaximum 100 characters
tented_scorenumber | nullNoOptional contact score
unsubscribedbooleanNoUnsubscribe flag
marketing_email_subscribedboolean | nullNoEmail subscription state
marketing_sms_subscribedboolean | nullNoSMS subscription state
marketing_email_invalidbooleanNoEmail validity flag
marketing_sms_invalidbooleanNoSMS validity flag
custom_fieldsobjectNoEditable custom fields by API name
A contact must always have at least one valid email or phone. An update that clears both identity fields is rejected for that item.

Update Request Example

curl --request POST \
  --url https://api.tented.ai/v1/contacts/update \
  --header "Authorization: Bearer $TENTED_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "items": [
      {
        "client_item_id": "crm-row-001",
        "contact_id": "11111111-1111-4111-8111-111111111111",
        "company": "Acme Enterprise",
        "lead_status": "qualified",
        "custom_fields": {
          "favorite_color": "Green"
        }
      }
    ]
  }'

Update Response Example

200 OK
{
  "status": "completed",
  "updated_count": 1,
  "rejected_count": 0,
  "items": [
    {
      "index": 0,
      "client_item_id": "crm-row-001",
      "contact_id": "11111111-1111-4111-8111-111111111111",
      "status": "updated",
      "contact": {
        "contact_id": "11111111-1111-4111-8111-111111111111",
        "email": "jane@example.com",
        "company": "Acme Enterprise",
        "lead_status": "qualified",
        "updated_at": "2026-04-30T12:05:00.000Z"
      }
    }
  ]
}

Delete Contacts

POST /v1/contacts/delete
DELETE /v1/contacts
Use POST /v1/contacts/delete if your HTTP client does not support request bodies on DELETE.

Request Body

FieldTypeRequiredNotes
itemsarrayYesBetween 1 and 25 delete records

Delete Item Fields

FieldTypeRequiredNotes
contact_iduuidYesContact to delete
client_item_idstringNoYour own per-item identifier for reconciliation

Delete Request Example

curl --request POST \
  --url https://api.tented.ai/v1/contacts/delete \
  --header "Authorization: Bearer $TENTED_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "items": [
      {
        "client_item_id": "crm-row-001",
        "contact_id": "11111111-1111-4111-8111-111111111111"
      }
    ]
  }'

Delete Response Example

200 OK
{
  "status": "completed",
  "deleted_count": 1,
  "rejected_count": 0,
  "items": [
    {
      "index": 0,
      "client_item_id": "crm-row-001",
      "contact_id": "11111111-1111-4111-8111-111111111111",
      "status": "deleted"
    }
  ]
}

Batch Response Format

Every contacts write endpoint returns:
FieldTypeNotes
statusstringAlways completed after the request has been processed
created_countnumberPresent on create responses
updated_countnumberPresent on update responses
deleted_countnumberPresent on delete responses
rejected_countnumberNumber of rejected items
itemsarrayPer-item results in request order
Rejected items include:
FieldTypeNotes
indexnumberZero-based item index from the request
client_item_idstringReturned when supplied
contact_iduuidReturned when supplied
statusstringrejected
error_codestringbad_request, not_found, conflict, or error
messagestringHuman-readable failure reason
existing_contactobjectReturned for duplicate conflicts

Common Item-Level Rejections

Error CodeCause
bad_requestNo fields to update
bad_requestUpdate would remove both email and phone
bad_requestCustom field is missing, inactive, non-editable, or has an invalid value
not_foundContact does not exist in the workspace
conflictEmail or phone matches another existing contact

Common Request Errors

StatusCause
400 Bad RequestInvalid JSON body
400 Bad RequestRequest body is missing
400 Bad Requestitems is missing, empty, or contains more than 25 records
400 Bad RequestA create item has neither email nor phone
400 Bad RequestInvalid email, phone, UUID, or field format
401 UnauthorizedMissing or invalid bearer token

Back to API Overview

Review the full Vibe Coding API endpoint map.