Skip to content

Contacts ​

A contact is a person identified by an email address, a personal LinkedIn profile URL, or both. Each identity is unique within a workspace.

The contact object ​

json
{
  "id": 42,
  "email": "alice@example.com",
  "first_name": "Alice",
  "last_name": "Martin",
  "full_name": "Alice Martin",
  "company_id": 3,
  "company_name": "Acme Corp",
  "company": { "id": 3, "name": "Acme Corp" },
  "phone": "+33123456789",
  "country": "France",
  "city": "Paris",
  "linkedin_url": "https://linkedin.com/in/alice",
  "job_title": "CTO",
  "status": "active",
  "lifecycle_stage": "lead",
  "custom_fields": { "account_tier": "enterprise" },
  "subscribed_at": "2026-02-23T10:00:00.000000Z",
  "unsubscribed_at": null,
  "created_at": "2026-02-23T10:00:00.000000Z",
  "updated_at": "2026-02-23T10:00:00.000000Z",
  "lists": [{ "id": 1, "name": "Newsletter", "color": "#6366f1", "contacts_count": 120 }],
  "tags": [{ "id": 5, "name": "VIP", "color": "#f59e0b" }]
}
FieldTypeDescription
full_namestringFirst and last name. Falls back to the email or LinkedIn identity when both are empty.
emailstring | nullEmail identity. Required when linkedin_url is omitted.
linkedin_urlstring | nullCanonical personal LinkedIn profile URL. Required when email is omitted.
company_idinteger | nullLinked CRM company in the same workspace.
company_namestring | nullDisplay name of the linked company.
companyobject | nullLinked company summary with id and name.
statusenumactive, unsubscribed, bounced, complained
lifecycle_stageenum | nullsubscriber, lead, marketing_qualified_lead, sales_qualified_lead, opportunity, customer, evangelist, or other
custom_fieldsobjectValues for workspace-defined CRM contact fields. Only writable text fields can be set.
lists, tagsarrayIncluded on list, get, create and update responses

Endpoints ​

MethodPathPermission
GET/contactscontacts:read
POST/contactscontacts:write
GET/contacts/{id}contacts:read
PUT/contacts/{id}contacts:write
DELETE/contacts/{id}contacts:write
GET/contacts/{id}/timelinecontacts:read
POST/contacts/{id}/unsubscribecontacts:write
POST/contacts/bulk-createcontacts:write
PUT/contacts/bulk-updatecontacts:write
DELETE/contacts/bulk-deletecontacts:write
POST/contacts/importcontacts:import
GET/contacts/exportcontacts:export

List contacts ​

GET /contacts

Returns contacts from the token workspace. Results are ordered by created_at desc, then id desc, unless another sort is requested.

Pass filters in the URL query string. Invalid values and relationship IDs outside the token workspace return 422.

QueryTypeDescription
pageintegerPage number, minimum 1, default 1.
per_pageintegerContacts per page, from 1 to 100, default 15.
searchstringPartial search over email, LinkedIn URL, first name, last name, job title and linked company. Every term must match; at most six terms are used.
statusenumactive, unsubscribed, bounced, or complained.
lifecycle_stageenumsubscriber, lead, marketing_qualified_lead, sales_qualified_lead, opportunity, customer, evangelist, or other.
company_idintegerOnly contacts linked to this workspace company.
list_idintegerOnly contacts in this workspace list.
tag_idintegerOnly contacts with this workspace tag.
has_emailboolean1 keeps contacts with an email; 0 keeps contacts without one.
has_linkedinboolean1 keeps contacts with a personal LinkedIn URL; 0 keeps contacts without one.
has_companyboolean1 keeps contacts linked to a company; 0 keeps unlinked contacts.
sortenumcreated_at, updated_at, first_name, last_name, email, linkedin_url, job_title, status, or lifecycle_stage.
directionenumasc or desc. Default desc.
bash
curl --get https://app.beeving.com/api/v1/contacts \
  -H "Authorization: Bearer TOKEN" \
  -H "Accept: application/json" \
  --data-urlencode "page=2" \
  --data-urlencode "per_page=15" \
  --data-urlencode "search=alice acme" \
  --data-urlencode "lifecycle_stage=lead" \
  --data-urlencode "has_email=1" \
  --data-urlencode "sort=first_name" \
  --data-urlencode "direction=asc"

The response contains data, navigation URLs in links, and meta with current_page, last_page, per_page, total, and count for the current page.

Create a contact ​

POST /contacts
FieldTypeRules
emailstring | nullRequired when linkedin_url is omitted. Valid email, unique in the workspace.
linkedin_urlstring | nullRequired when email is omitted. Personal LinkedIn profile URL, unique in the workspace, max 500 characters.
first_name, last_name, job_titlestring | nullMax 255 characters.
company_idinteger | nullID of a company in the same workspace.
phonestring | nullMax 50 characters.
country, citystring | nullMax 100 characters.
lifecycle_stagestring | nullsubscriber, lead, marketing_qualified_lead, sales_qualified_lead, opportunity, customer, evangelist, or other.
custom_fieldsobject | nullUp to 50 text values keyed by workspace-defined writable CRM contact fields. Unknown or read-only keys are rejected; values are at most 5000 characters.
custom_properties—Prohibited. Use custom_fields.
tagsinteger[]Tag IDs
listsinteger[]List IDs

A new contact always starts with status: active.

bash
curl -X POST https://app.beeving.com/api/v1/contacts \
  -H "Authorization: Bearer TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alice@example.com",
    "first_name": "Alice",
    "last_name": "Martin",
    "lists": [1],
    "custom_fields": { "plan": "enterprise" }
  }'

201 Created: { "data": Contact }

Get a contact ​

GET /contacts/{id}

200: { "data": Contact }. Returns 404 if the contact isn't in your workspace.

Update a contact ​

PUT /contacts/{id}

This is a partial update: send only the fields you want to change.

  • It accepts the same fields as create, plus status.
  • A field sent as null is ignored. Null does not clear the value.
  • When tags or lists is sent, it replaces the contact's current tags or lists.

200: { "data": Contact }

Delete a contact ​

DELETE /contacts/{id}

204 No Content

Timeline ​

GET /contacts/{id}/timeline

Returns the contact's 50 most recent email events. This endpoint is not paginated.

json
{
  "data": [
    {
      "type": "email_sent",
      "subject": "Quick question",
      "campaign_id": 12,
      "status": "sent",
      "sent_at": "2026-03-01T09:00:00.000000Z",
      "opened_at": "2026-03-01T10:12:00.000000Z",
      "clicked_at": null,
      "created_at": "2026-03-01T09:00:00.000000Z"
    }
  ]
}

Unsubscribe ​

POST /contacts/{id}/unsubscribe

Sets status to unsubscribed and records unsubscribed_at.

200: { "data": Contact, "message": "Contact unsubscribed successfully" }

Bulk create ​

POST /contacts/bulk-create
json
{
  "contacts": [
    { "email": "a@example.com", "first_name": "A" },
    { "linkedin_url": "https://www.linkedin.com/in/example", "custom_fields": { "source": "webinar" } }
  ]
}
  • Up to 1000 contacts per call.
  • Each item requires email or linkedin_url and accepts first_name, last_name, company_id, phone, lifecycle_stage, and relational custom_fields.
  • Existing email or LinkedIn identities are reported as failures. They are not updated.

201

json
{
  "data": [ /* created contacts */ ],
  "message": "Bulk create completed",
  "created": 1,
  "failed": 1,
  "errors": [{ "index": 0, "email": "a@example.com", "error": "Contact already exists" }]
}

Bulk update ​

PUT /contacts/bulk-update
json
{
  "contact_ids": [1, 2, 3],
  "updates": { "status": "unsubscribed" }
}

updates accepts these keys:

  • status
  • custom_fields: replaces the whole object

200: { "data": { "message": "Bulk update completed", "updated": 3 } }

Bulk delete ​

DELETE /contacts/bulk-delete
json
{ "contact_ids": [1, 2, 3] }

200: { "data": { "message": "Bulk delete completed", "deleted": 3 } }

Import a CSV ​

POST /contacts/import

Send multipart/form-data:

FieldDescription
fileRequired. A .csv or .txt file, max 10 MB
mapping[email], mapping[linkedin_url]Map at least one identity column. Both may be mapped.
mapping[first_name], mapping[last_name], mapping[company_id], mapping[phone], mapping[lifecycle_stage]Optional column names. company_id accepts a workspace company ID or unambiguous company name.
mapping[custom_fields.KEY]Optional column for an existing writable CRM contact field.
bash
curl -X POST https://app.beeving.com/api/v1/contacts/import \
  -H "Authorization: Bearer TOKEN" \
  -H "Accept: application/json" \
  -F "file=@contacts.csv" \
  -F "mapping[email]=Email" \
  -F "mapping[first_name]=First name"

Rows without a valid email or personal LinkedIn profile URL are skipped.

200

json
{
  "data": {
    "message": "Import completed",
    "created": 120,
    "skipped": 4,
    "errors": 0,
    "error_details": []
  }
}

error_details lists the first 10 errors as { "row": 12, "error": "..." }.

Export as CSV ​

GET /contacts/export?status=active&search=acme

Returns a text/csv file named contacts-YYYY-MM-DD.csv. The export uses import-compatible columns for Email, LinkedIn URL, names, job title, phone, location, lifecycle stage, CRM company ID, and each writable relational custom field. LinkedIn-only contacts are included and can be imported again.

API v1.0