identities): a
phone number, an email address or an account on a channel. Conversations, notes and consent attach to the contact.
All routes are under https://api.impellabs.tech/api/v1. Send Authorization: Bearer imp_... on each request. See
Authentication and Conventions. The API reference lists every field.
Who can see a contact
A token acts as the person who made it. A contact answers404 with There is no contact like that. when it does not
exist or when the person may not see it. A contact has a visibility: workspace (everyone) or owner (its owner
and their managers).
A removed contact is archived, not deleted. Its handles become free for a new contact.
List contacts
GET /contacts returns a page: { results, count, page, page_size, pages }. page_size is 20 unless you send
page_size (1 to 100).
Create a contact
POST /contacts. Send a name, a phone, an email, or any of them. You must send at least one. Write a phone
number with its country code.
201:
Read, update and remove
GET /contacts/{contact_id}returns the contact withconsent,timeline,suggestions(the number of suggested merges),crm_leadandapp_records.PATCH /contacts/{contact_id}changes only the fields you send. The editable fields arename,owner_id,visibility,language,time_zone,tags,custom_fields,do_not_contact. Set a custom field tonullto remove its value.DELETE /contacts/{contact_id}answers204.
GET /contacts/{contact_id}/timeline returns the contact’s history. Use limit (1 to 100), before (the time from
next) and kind (one kind, or kinds separated by commas; note returns the notes).
Handles
POST /contacts/{contact_id}/identitieswith{ "kind", "value" }adds a handle, for examplephone,whatsapp,emailorinstagram. A typed handle is not verified.201returns{ id, kind, value, verified }. If another contact holds the value, the reply is409and ImpelLabs adds a suggested merge.DELETE /contacts/{contact_id}/identities/{identity_id}removes a handle.404if the contact has no such handle.
Notes
GET /contacts/{contact_id}/notesreturns the notes you may see, newest first.POST /contacts/{contact_id}/noteswith{ "text", "visibility"?, "department_id"? }.texthas 5000 characters at most.visibilityiseveryone(the default),departmentorprivate. Adepartmentnote needsdepartment_id, one of your departments.PATCHandDELETE /contacts/{contact_id}/notes/{note_id}work only for the person who wrote the note. Others get404.
Consent
POST /contacts/{contact_id}/consent records a decision of the customer.
The reply is
201 with { "consent": [ { "channel", "purpose", "status", "source", "at" } ] }: the newest decision
for each channel and purpose. The source is staff.
Merge
Two contacts can be one person. ImpelLabs suggests merges when handles or names match.GET /contacts/matcheslists suggestions.stateisreview(the default) ormerged.contactlimits the list to one contact.POST /contacts/matches/{match_id}/acceptmerges the two contacts.rejectdismisses the suggestion.undosplits a merge that was made.POST /contacts/{contact_id}/mergewith{ "other_id" }merges two contacts you name. The reply is201with the match. A merge that is not allowed answers400withdetail.
Contact fields
A contact field is a custom field of the workspace, such as a customer number.GET /contacts/fieldslists the fields.POST /contacts/fieldscreates one:key(small letters, numbers and_, starting with a letter),label,kind(defaulttext), and optionallyrequired,choices,value_map,help_textandorder. Aselectormultiselectfield needschoices.PATCHandDELETE /contacts/fields/{field_id}change and archive a field.
Import contacts from a sheet
An import loads a CSV or Excel file. You send the file first, then check the column mapping, then start the job.POST /fileswith a multipartfile. The reply has the fileid.POST /imports/groupswith{ "file_id", "group", "mapping"? }. Themappingholds column names:email, and optionallyname,companyandconsent. ImpelLabs suggests columns from the headers. Each person in the file gets the taggroup:<name>. The reply is201with the import, the fileheaders, sample rows and the suggested columns.POST /imports/{import_id}/checkwith{ "mapping": { "group": "<name>", "email": "<column>", "name": "<column>" } }saves the mapping and runs a dry run. Nothing is written. The mapping always names thegroupand theemailcolumn.name,companyandconsentare optional columns.POST /imports/{import_id}/startstarts the job. It answers202. A large import can wait for an approval first; the importstatusshowswaiting.GET /imports/{import_id}showsstatusand therowscounts.GET /imports/{import_id}/errorspages the rows that had a problem.POST /imports/{import_id}/undoreverses the import inside its undo window. Outside it, the reply is409.
consent, or when consent exists already. The same file again
changes nothing. You can read only the imports you made.