Manage Chatwoot contacts and companies
Use this guide to find authoritative customer records, resolve ambiguous matches, attach or detach one contact and company, or merge two contacts. Every mutation ends with a provider read instead of relying on the write response.
The safest path is discovery before mutation. Search results identify candidates; they do not prove that two records represent the same person or organization.
When to use this guide
Use this procedure for one bounded customer-data intent:
- find a contact or company without changing provider state;
- attach one known contact to one known company;
- detach one known contact from one known company;
- merge one reviewed contact into another.
Use the complete capability reference for import, export, notes, labels, inbox associations, CRUD, and other operations.
This guide does not define Chatwoot search fields, relationship body fields, or merge participants. Those fields belong to the pinned provider route, not the generic connector schema.
Prerequisites
Before starting, obtain:
- an authenticated AIP caller bound to the intended tenant;
- an admitted Chatwoot connector instance for the intended account;
- the exact pinned Chatwoot request schema for any body-bearing mutation;
- authority to read restricted customer data;
- an approver for every POST, PATCH, or DELETE operation;
- a protected evidence location for identifiers, inputs, and results.
Do not put account_id in Action input. The connector host inserts its
configured account into every provider route.
Choose the narrowest capability
| Intent | Capability | Provider method | Risk | Approval |
|---|---|---|---|---|
| Search contacts | cap:chatwoot:contact.search |
GET | Low | No |
| Apply a provider filter | cap:chatwoot:contact.filter |
POST | Medium | Required |
| Read one contact | cap:chatwoot:contact.get |
GET | Low | No |
| Search companies | cap:chatwoot:company.search |
GET | Low | No |
| Read one company | cap:chatwoot:company.get |
GET | Low | No |
| List company contacts | cap:chatwoot:company.contact.list |
GET | Low | No |
| Attach a contact | cap:chatwoot:company.contact.create |
POST | Medium | Required |
| Detach a contact | cap:chatwoot:company.contact.delete |
DELETE | High | Required |
| Merge contacts | cap:chatwoot:contact.merge |
POST | Medium | Required |
contact.filter has a read-like name but a POST provider method. The connector
therefore publishes it as a governed mutation with unsafe retry. Prefer the GET
search or list surface when it can answer the task.
1. Pin route and Action identity
Read capability discovery for the authenticated tenant. Record:
- connector type, immutable version, instance, and configured account;
- exact capability ID and catalogue revision;
- authenticated actor and tenant;
- credential revision without credential bytes;
- policy version used for any approval.
Stop if the route, account, or capability differs from the reviewed intent. Connector admission, capability discovery, actor authorization, and customer identity are separate decisions.
Create a new Action ID for each read. For a mutation, assign one Action ID and one external-account-scoped idempotency key before approval. Keep the same Action, key, and canonical input while settling an uncertain result.
2. Find candidate records
Use contact.search or company.search first. Both are low-risk GET
capabilities and advertise safe retry.
The common input envelope can carry a query object:
{
"query": {
"...": "fields from the pinned Chatwoot search route"
}
}
This is a structural example, not a copy-ready provider query. The connector bounds query size and scalar values but does not freeze provider field names or matching semantics.
Record the exact query and all candidate IDs needed for the decision. Treat names, email addresses, phone numbers, external identifiers, and company attributes as restricted data.
If provider filtering is necessary, review its body as a mutation:
{
"body": {
"...": "fields from the pinned Chatwoot filter route"
}
}
Approve the exact filter body and retention purpose. A completed filter result does not turn POST into a retry-safe read.
3. Read authoritative before-state
Read every candidate that could be changed:
contact.getfor each source, destination, or relationship candidate;company.getfor the target company;company.contact.listfor the company’s current relationships.
For a contact merge, also inspect the records and relationships that tenant policy considers material. The catalogue exposes contact conversations, labels, notes, and company relationships, but it does not publish one aggregate merge preview.
Stop when:
- more than one candidate remains plausible;
- source and destination identities are equal or unclear;
- records belong to another configured account;
- current relationships conflict with the requested intent;
- the provider schema revision is unknown;
- approval cannot bind the exact body.
Do not resolve ambiguity by choosing the first search result.
4. Attach a contact to a company
Use cap:chatwoot:company.contact.create. The connector builds:
POST /api/v1/accounts/{configured_account_id}/companies/{company_id}/contacts
company_id is a top-level Action input. The contact relationship fields
belong inside the provider-owned body:
{
"company_id": "88",
"body": {
"...": "reviewed fields from the pinned relationship route"
}
}
Before dispatch, bind approval to the company, intended contact, canonical body, account, actor, capability, and policy hash. Keep the exact request with the idempotency record.
After a completed result, call company.contact.list and confirm the expected
relationship once. Do not infer relationship creation from HTTP success alone.
5. Detach a contact from a company
Use cap:chatwoot:company.contact.delete only after the before-state list
confirms the exact relationship. Its provider route contains both identities:
DELETE /api/v1/accounts/{configured_account_id}/companies/{company_id}/contacts/{contact_id}
An exact Action input can be submitted as follows:
COMPANY_ID=88
CONTACT_ID=205
DETACH_ACTION=act_chatwoot_company_detach_001
DETACH_KEY=chatwoot-company-88-contact-205-detach-v1
aipctl action call \
"$AIP_URL" \
cap:chatwoot:company.contact.delete \
--action-id "$DETACH_ACTION" \
--idempotency-key "$DETACH_KEY" \
--input "{\"company_id\":\"$COMPANY_ID\",\"contact_id\":\"$CONTACT_ID\"}"
The operation removes the relationship. Its catalogue description does not claim that it deletes the contact record.
This DELETE is high risk, unsafe to retry, and has no rollback. Re-list company contacts after settlement.
6. Merge two contacts
Use cap:chatwoot:contact.merge only after a human has reviewed distinct
source and destination records. The connector builds:
POST /api/v1/accounts/{configured_account_id}/actions/contact_merge
The route has no contact path parameter. Both identities and any merge options belong to a provider-owned body:
{
"body": {
"...": "source, destination, and fields from the pinned merge route"
}
}
Before approval, retain:
- authoritative snapshots of both contacts;
- the reviewed source-to-destination direction;
- collision decisions for names and customer identifiers;
- material conversations, notes, labels, inboxes, and company relationships;
- the exact canonical provider body;
- the intended post-merge verification fields.
The connector publishes no merge preview, rollback, or per-related-object transaction model. Do not describe the operation as reversible or atomic across all related data.
After settlement, read the destination contact and material relationships. Determine source behavior from provider state rather than assuming deletion, redirection, or archival.
Understand mutation transport
For catalogue mutations, the connector:
- requires a non-blank idempotency key of at most 512 bytes;
- sends
X-AIP-Action-IDwith the Action ID; - sends
Idempotency-Keywith the supplied key; - injects the configured account into the route;
- sends either a JSON
bodyormultipart, never both; - returns the provider HTTP status, parsed body, and request ID when exposed.
All relationship and merge mutations require approval, advertise unsafe retry, contain restricted PII, and declare rollback unsupported. Forwarding a key does not prove that a particular Chatwoot route deduplicates repeated requests.
Verify the intended state
Use new read Action IDs after a mutation. Verify only authoritative fields needed by the task:
| Mutation | Required final read |
|---|---|
| Attach | List company contacts and identify the intended contact once |
| Detach | List company contacts and confirm the intended relationship is absent |
| Merge | Read the destination and each material relationship selected before approval |
Retain the mutation Action ID, idempotency key hash, approval record, exact input, configured account and route, provider status, provider request ID when present, final read Action IDs, and checked fields.
Do not retain credentials or unrelated customer fields merely to enlarge the evidence set.
Recover an uncertain mutation
| Symptom | Read first | Decision |
|---|---|---|
| Attach response lost | Company contacts | Accept the existing relationship or escalate; do not attach blindly |
| Detach response lost | Company contacts and audit evidence | Accept confirmed absence or escalate; do not repeat a high-risk delete blindly |
| Merge response lost | Destination, source when addressable, and material relationships | Accept reconciled state or escalate; do not submit a second merge |
| Provider rejected body | Current objects and pinned provider schema | Correct the intent before creating another Action |
| Authentication failure | Connector route, account binding, and credential revision | Repair configuration; never move the token into Action input |
| Oversized or non-JSON result | Provider state and retained request identity | Verify through bounded reads; do not infer failure from response shape |
Transport, provider-temporary, cancellation, and oversized-response failures
can leave mutation outcome uncertain. Every connector failure reports
retryable: false even though read contracts separately advertise safe retry.
A corrective relationship or record change is a new governed intent against current state. It is not rollback of the earlier Action.
Completion criteria
The workflow is complete only when:
- route, account, actor, and capability match the intended tenant;
- search ambiguity was resolved through authoritative reads;
- each mutation used one exact input, Action ID, key, and approval;
- attach or detach state was verified through company contacts;
- merge direction and material relationships were reviewed and verified;
- restricted evidence follows tenant redaction and retention policy;
- no uncertain mutation was replaced with an uncorrelated retry.
These criteria validate application handling. They do not establish provider payload compatibility, customer identity correctness, or live qualification.