Contacts and roles
A contact is someone the user deals with by email, a person or a business. A role is what that contact is to the user: a client, a vendor, their accountant. One contact can hold several roles. This guide covers how bind.co builds both from the mailbox, how duplicates are handled, how a person stays in control, and which webhook tells you something changed.
Reading needs contacts:read; changing needs contacts:write, which includes contacts:read.
A contact
{
"contact_id": "con_3SrGoE2zltj",
"contact_status": "active",
"contact_name": {"value": "Dana Ruiz", "bind_inference_confidence": 0.95},
"contact_primary_email": {"value": "dana@acme.com", "bind_inference_confidence": 0.99},
"contact_title": {"value": "Controller", "bind_inference_confidence": 0.9},
"contact_summary": {"value": "Acme's controller; handles the monthly close.", "bind_inference_confidence": 0.84},
"contact_relationship_strength": {"value": 7, "bind_inference_confidence": 0.7},
"contact_tone": {"value": 4, "bind_inference_confidence": 0.66},
"first_contact": "2024-02-12T15:03:00Z",
"last_contact": "2026-10-02T18:20:00Z",
"user_direct_first_contact": "2024-02-12T15:03:00Z",
"user_direct_last_contact": "2026-09-30T11:20:00Z",
"contact_direct_first_contact": "2024-02-13T08:41:00Z",
"contact_direct_last_contact": "2026-10-02T18:20:00Z",
"user_indirect_first_contact": null,
"user_indirect_last_contact": null,
"other_indirect_first_contact": null,
"other_indirect_last_contact": null,
"truth_updated_at": null,
"suggestion_updated_at": null
}
email_addresseslists every address known for the contact, the primary first, each with the company it belongs to.contact_summarysays who the contact is to the user, in a sentence.contact_relationship_strengthruns from 1 (weak or recent) to 10 (close and long-standing);contact_tonefrom 1 (direct, strictly business) to 10 (open and friendly).first_contactandlast_contactare facts read from the email headers, not suggestions: the dates of the first and the latest email between the user and the contact. Mail the user sent to them, mail they sent to the user, and mail with them copied all count; bulk mail does not. They carry no confidence and cannot be edited.- The same facts come split by who wrote:
user_direct_*(the user wrote to them),contact_direct_*(they wrote to the user),user_indirect_*(the user wrote with them copied) andother_indirect_*(someone else wrote to the user with them copied), each as a_first_contactand a_last_contact,nullwhen it never happened.
Every learned detail has a bind_inference_confidence; a detail a person set has 1. How to use the
score: Reading confidence.
bind.co sets newsletters and mailing lists aside on its own: they become dismissed and leave the
lists, and come back if the user starts corresponding with them.
To add someone the mailbox has not shown yet, POST /v1/contacts with a name and email address. The
contact is saved as set by a person. An address another contact already holds, or a user whose
mailbox has not been read yet, answers 409.
Roles
{
"category_contact_role_id": "rol_1S9dCsGYrSI",
"contact_id": "con_3SrGoE2zltj",
"contact_role_status": "active",
"entity_type": {"value": "ACCOUNTANT", "bind_inference_confidence": 0.92},
"entity_stage": {"value": "ACTIVE", "bind_inference_confidence": 0.81},
"home_currency": {"value": "USD", "bind_inference_confidence": 0.77},
"entity_type_inferred": {"value": null, "bind_inference_confidence": null},
"entity_type_inferred_streak": 0
}
entity_typeis the kind of role, such asCLIENT,VENDOR,ACCOUNTANT,BOOKKEEPER,LAWYER,BANKER,INSURANCE_BROKER,INVESTOR,TEAM_MEMBERorFRIENDS_AND_FAMILY.entity_stageis where the relationship stands in that role. ACLIENTis aPROSPECT,ACTIVE,DORMANT,FORMERorUNKNOWN.home_currencyis the currency the contact deals in for that role.
A contact holds each type at most once. An accountant who is also a friend has two roles, each with
its own id, stage and history. GET /v1/roles lists roles with their contact's name, email and
contact dates, so a "my clients" screen is one call:
GET /v1/roles?entity_type=CLIENT&entity_stage=ACTIVE.
Roles are kept up to date from the mail. When the mail points to a different type than the one a
role has, entity_type_inferred shows it and entity_type_inferred_streak how many updates in a
row have pointed to it. GET /v1/roles/families shows how
types group into families, such as accountants under vendors.
Drafts are written per role; see Drafts and goals. Operations name roles too, for example the counterpart of an expense; see Operations.
Who has the last word
Contacts and roles follow the same steps as the profile:
- Nobody has saved it: bind.co keeps it up to date from the mail.
- A person saves it with
PATCH(the fields sent, with confidence 1) orconfirm(as it is). New mail no longer changes the saved values. - When the mail later reads differently, bind.co flags a suggestion:
suggestion_updated_atis set and your endpoint receivescontacts.suggestion_availableorcontact_roles.suggestion_available. GET /v1/contacts/{contact_id}/suggestionsorGET /v1/roles/{role_id}/suggestionsshows what differs, current and suggested (example below).PATCHthe suggested values to accept, orconfirmto keep what is saved.resethands it back to bind.co.
A role suggestion:
{
"suggestion_updated_at": "2026-10-03T07:41:12Z",
"entity_stage": {
"current": {"value": "PROSPECT", "bind_inference_confidence": 1.0},
"suggested": {"value": "ACTIVE", "bind_inference_confidence": 0.84}
}
}
To find everything waiting for review: GET /v1/contacts?has_suggestion=true and
GET /v1/roles?has_suggestion=true.
Set aside and bring back
POST /v1/contacts/{contact_id}/rejectdismisses a contact: it and its roles leave every list.POST /v1/roles/{role_id}/rejectdismisses one role. Nothing is deleted;status=dismissedon the lists still finds them.restorebrings back what a person dismissed, active and as set by a person. A contact bind.co set aside as a newsletter answers409.- A dismissed contact or role cannot be edited or confirmed (
409): restore it first.
One person, one contact
bind.co checks for duplicates and merges them on its own. A person can merge too:
curl https://api.bind.co/v1/contacts/con_3SrGoE2zltj/merge \
-H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"loser_contact_ids": ["con_8LmQe4XyTb1"]}'
The addresses, roles and history of each listed contact move to this one, and they become
merged. Every listed contact must be active and the user's; if one is not, nothing is merged.
POST /v1/contacts/{contact_id}/unmerge undoes merges into this contact, whoever made them: each
contact comes back active with its own id, addresses, roles and history. Leave the list out to undo
them all. Once two contacts are separated, bind.co never merges them again on its own. A contact
changed since the merge, or a later merge touching them, answers 409 and nothing is undone.
If your application stores contact ids, handle merged: when a stored contact answers 404,
re-read the contact list to find where it went.
Webhooks
contacts.first_completed: the user's first contacts and roles are ready. Sent once per user.contacts.completed: contacts and roles were updated from new mail;createdcounts new roles andupdatedre-scored ones.contacts.suggestion_available(contact_ids) andcontact_roles.suggestion_available(category_contact_role_ids): saved records with a newer suggestion.
The webhooks guide has every payload.