{
  "schemaVersion": "1.0",
  "title": "Manage Chatwoot contacts and companies",
  "description": "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.",
  "canonical": "https://getaip.org/docs/connectors/chatwoot/guides/contact-and-company-workflows",
  "route": "/docs/connectors/chatwoot/guides/contact-and-company-workflows",
  "source": "docs/connectors/chatwoot/guides/contact-and-company-workflows.md",
  "protocol": "Agent Interoperability Protocol",
  "protocolVersion": "1.0",
  "section": "Connectors",
  "documentType": "Connector",
  "language": "en",
  "downloads": {
    "md": "/docs/download/connectors/chatwoot/guides/contact-and-company-workflows.md",
    "txt": "/docs/download/connectors/chatwoot/guides/contact-and-company-workflows.txt",
    "json": "/docs/download/connectors/chatwoot/guides/contact-and-company-workflows.json",
    "pdf": "/docs/download/connectors/chatwoot/guides/contact-and-company-workflows.pdf"
  },
  "content": {
    "format": "text/markdown",
    "markdown": "---\ntitle: Manage Chatwoot contacts and companies\ndescription: >-\n  Find, relate, merge, verify, and recover Chatwoot customer records through\n  one governed AIP workflow\nkind: how-to\naudience: application-developer\nappliesTo: \"1.x\"\nwritingStandard: \"aip-docs/1.0\"\nlastReviewedRevision: \"97be86e9efedf07ecf1783b03800f683f107fb04\"\nconnector: chatwoot\ncapabilityIds:\n  - cap:chatwoot:contact.search\n  - cap:chatwoot:contact.filter\n  - cap:chatwoot:contact.get\n  - cap:chatwoot:contact.merge\n  - cap:chatwoot:company.search\n  - cap:chatwoot:company.get\n  - cap:chatwoot:company.contact.list\n  - cap:chatwoot:company.contact.create\n  - cap:chatwoot:company.contact.delete\naliases:\n  - Chatwoot customer record workflow\n  - Chatwoot contact merge and company relationship guide\n---\n\n# Manage Chatwoot contacts and companies\n\nUse this guide to find authoritative customer records, resolve ambiguous\nmatches, attach or detach one contact and company, or merge two contacts. Every\nmutation ends with a provider read instead of relying on the write response.\n\nThe safest path is discovery before mutation. Search results identify\ncandidates; they do not prove that two records represent the same person or\norganization.\n\n## When to use this guide\n\nUse this procedure for one bounded customer-data intent:\n\n- find a contact or company without changing provider state;\n- attach one known contact to one known company;\n- detach one known contact from one known company;\n- merge one reviewed contact into another.\n\nUse the [complete capability reference](../capabilities/contacts-and-companies.md)\nfor import, export, notes, labels, inbox associations, CRUD, and other\noperations.\n\nThis guide does not define Chatwoot search fields, relationship body fields, or\nmerge participants. Those fields belong to the pinned provider route, not the\ngeneric connector schema.\n\n## Prerequisites\n\nBefore starting, obtain:\n\n- an authenticated AIP caller bound to the intended tenant;\n- an admitted Chatwoot connector instance for the intended account;\n- the exact pinned Chatwoot request schema for any body-bearing mutation;\n- authority to read restricted customer data;\n- an approver for every POST, PATCH, or DELETE operation;\n- a protected evidence location for identifiers, inputs, and results.\n\nDo not put `account_id` in Action input. The connector host inserts its\nconfigured account into every provider route.\n\n## Choose the narrowest capability\n\n| Intent | Capability | Provider method | Risk | Approval |\n|---|---|---|---|---|\n| Search contacts | `cap:chatwoot:contact.search` | GET | Low | No |\n| Apply a provider filter | `cap:chatwoot:contact.filter` | POST | Medium | Required |\n| Read one contact | `cap:chatwoot:contact.get` | GET | Low | No |\n| Search companies | `cap:chatwoot:company.search` | GET | Low | No |\n| Read one company | `cap:chatwoot:company.get` | GET | Low | No |\n| List company contacts | `cap:chatwoot:company.contact.list` | GET | Low | No |\n| Attach a contact | `cap:chatwoot:company.contact.create` | POST | Medium | Required |\n| Detach a contact | `cap:chatwoot:company.contact.delete` | DELETE | High | Required |\n| Merge contacts | `cap:chatwoot:contact.merge` | POST | Medium | Required |\n\n`contact.filter` has a read-like name but a POST provider method. The connector\ntherefore publishes it as a governed mutation with unsafe retry. Prefer the GET\nsearch or list surface when it can answer the task.\n\n## 1. Pin route and Action identity\n\nRead capability discovery for the authenticated tenant. Record:\n\n- connector type, immutable version, instance, and configured account;\n- exact capability ID and catalogue revision;\n- authenticated actor and tenant;\n- credential revision without credential bytes;\n- policy version used for any approval.\n\nStop if the route, account, or capability differs from the reviewed intent.\nConnector admission, capability discovery, actor authorization, and customer\nidentity are separate decisions.\n\nCreate a new Action ID for each read. For a mutation, assign one Action ID and\none external-account-scoped idempotency key before approval. Keep the same\nAction, key, and canonical input while settling an uncertain result.\n\n## 2. Find candidate records\n\nUse `contact.search` or `company.search` first. Both are low-risk GET\ncapabilities and advertise safe retry.\n\nThe common input envelope can carry a `query` object:\n\n```json\n{\n  \"query\": {\n    \"...\": \"fields from the pinned Chatwoot search route\"\n  }\n}\n```\n\nThis is a structural example, not a copy-ready provider query. The connector\nbounds query size and scalar values but does not freeze provider field names or\nmatching semantics.\n\nRecord the exact query and all candidate IDs needed for the decision. Treat\nnames, email addresses, phone numbers, external identifiers, and company\nattributes as restricted data.\n\nIf provider filtering is necessary, review its body as a mutation:\n\n```json\n{\n  \"body\": {\n    \"...\": \"fields from the pinned Chatwoot filter route\"\n  }\n}\n```\n\nApprove the exact filter body and retention purpose. A completed filter result\ndoes not turn POST into a retry-safe read.\n\n## 3. Read authoritative before-state\n\nRead every candidate that could be changed:\n\n- `contact.get` for each source, destination, or relationship candidate;\n- `company.get` for the target company;\n- `company.contact.list` for the company's current relationships.\n\nFor a contact merge, also inspect the records and relationships that tenant\npolicy considers material. The catalogue exposes contact conversations,\nlabels, notes, and company relationships, but it does not publish one aggregate\nmerge preview.\n\nStop when:\n\n- more than one candidate remains plausible;\n- source and destination identities are equal or unclear;\n- records belong to another configured account;\n- current relationships conflict with the requested intent;\n- the provider schema revision is unknown;\n- approval cannot bind the exact body.\n\nDo not resolve ambiguity by choosing the first search result.\n\n## 4. Attach a contact to a company\n\nUse `cap:chatwoot:company.contact.create`. The connector builds:\n\n```text\nPOST /api/v1/accounts/{configured_account_id}/companies/{company_id}/contacts\n```\n\n`company_id` is a top-level Action input. The contact relationship fields\nbelong inside the provider-owned `body`:\n\n```json\n{\n  \"company_id\": \"88\",\n  \"body\": {\n    \"...\": \"reviewed fields from the pinned relationship route\"\n  }\n}\n```\n\nBefore dispatch, bind approval to the company, intended contact, canonical\nbody, account, actor, capability, and policy hash. Keep the exact request with\nthe idempotency record.\n\nAfter a completed result, call `company.contact.list` and confirm the expected\nrelationship once. Do not infer relationship creation from HTTP success alone.\n\n## 5. Detach a contact from a company\n\nUse `cap:chatwoot:company.contact.delete` only after the before-state list\nconfirms the exact relationship. Its provider route contains both identities:\n\n```text\nDELETE /api/v1/accounts/{configured_account_id}/companies/{company_id}/contacts/{contact_id}\n```\n\nAn exact Action input can be submitted as follows:\n\n```sh\nCOMPANY_ID=88\nCONTACT_ID=205\nDETACH_ACTION=act_chatwoot_company_detach_001\nDETACH_KEY=chatwoot-company-88-contact-205-detach-v1\n\naipctl action call \\\n  \"$AIP_URL\" \\\n  cap:chatwoot:company.contact.delete \\\n  --action-id \"$DETACH_ACTION\" \\\n  --idempotency-key \"$DETACH_KEY\" \\\n  --input \"{\\\"company_id\\\":\\\"$COMPANY_ID\\\",\\\"contact_id\\\":\\\"$CONTACT_ID\\\"}\"\n```\n\nThe operation removes the relationship. Its catalogue description does not\nclaim that it deletes the contact record.\n\nThis DELETE is high risk, unsafe to retry, and has no rollback. Re-list company\ncontacts after settlement.\n\n## 6. Merge two contacts\n\nUse `cap:chatwoot:contact.merge` only after a human has reviewed distinct\nsource and destination records. The connector builds:\n\n```text\nPOST /api/v1/accounts/{configured_account_id}/actions/contact_merge\n```\n\nThe route has no contact path parameter. Both identities and any merge options\nbelong to a provider-owned body:\n\n```json\n{\n  \"body\": {\n    \"...\": \"source, destination, and fields from the pinned merge route\"\n  }\n}\n```\n\nBefore approval, retain:\n\n1. authoritative snapshots of both contacts;\n2. the reviewed source-to-destination direction;\n3. collision decisions for names and customer identifiers;\n4. material conversations, notes, labels, inboxes, and company relationships;\n5. the exact canonical provider body;\n6. the intended post-merge verification fields.\n\nThe connector publishes no merge preview, rollback, or per-related-object\ntransaction model. Do not describe the operation as reversible or atomic\nacross all related data.\n\nAfter settlement, read the destination contact and material relationships.\nDetermine source behavior from provider state rather than assuming deletion,\nredirection, or archival.\n\n## Understand mutation transport\n\nFor catalogue mutations, the connector:\n\n- requires a non-blank idempotency key of at most 512 bytes;\n- sends `X-AIP-Action-ID` with the Action ID;\n- sends `Idempotency-Key` with the supplied key;\n- injects the configured account into the route;\n- sends either a JSON `body` or `multipart`, never both;\n- returns the provider HTTP status, parsed body, and request ID when exposed.\n\nAll relationship and merge mutations require approval, advertise unsafe retry,\ncontain restricted PII, and declare rollback unsupported. Forwarding a key\ndoes not prove that a particular Chatwoot route deduplicates repeated requests.\n\n## Verify the intended state\n\nUse new read Action IDs after a mutation. Verify only authoritative fields\nneeded by the task:\n\n| Mutation | Required final read |\n|---|---|\n| Attach | List company contacts and identify the intended contact once |\n| Detach | List company contacts and confirm the intended relationship is absent |\n| Merge | Read the destination and each material relationship selected before approval |\n\nRetain the mutation Action ID, idempotency key hash, approval record, exact\ninput, configured account and route, provider status, provider request ID when\npresent, final read Action IDs, and checked fields.\n\nDo not retain credentials or unrelated customer fields merely to enlarge the\nevidence set.\n\n## Recover an uncertain mutation\n\n| Symptom | Read first | Decision |\n|---|---|---|\n| Attach response lost | Company contacts | Accept the existing relationship or escalate; do not attach blindly |\n| Detach response lost | Company contacts and audit evidence | Accept confirmed absence or escalate; do not repeat a high-risk delete blindly |\n| Merge response lost | Destination, source when addressable, and material relationships | Accept reconciled state or escalate; do not submit a second merge |\n| Provider rejected body | Current objects and pinned provider schema | Correct the intent before creating another Action |\n| Authentication failure | Connector route, account binding, and credential revision | Repair configuration; never move the token into Action input |\n| Oversized or non-JSON result | Provider state and retained request identity | Verify through bounded reads; do not infer failure from response shape |\n\nTransport, provider-temporary, cancellation, and oversized-response failures\ncan leave mutation outcome uncertain. Every connector failure reports\n`retryable: false` even though read contracts separately advertise safe retry.\n\nA corrective relationship or record change is a new governed intent against\ncurrent state. It is not rollback of the earlier Action.\n\n## Completion criteria\n\nThe workflow is complete only when:\n\n- route, account, actor, and capability match the intended tenant;\n- search ambiguity was resolved through authoritative reads;\n- each mutation used one exact input, Action ID, key, and approval;\n- attach or detach state was verified through company contacts;\n- merge direction and material relationships were reviewed and verified;\n- restricted evidence follows tenant redaction and retention policy;\n- no uncertain mutation was replaced with an uncorrelated retry.\n\nThese criteria validate application handling. They do not establish provider\npayload compatibility, customer identity correctness, or live qualification.\n\n## Related documentation\n\n- [Contacts and companies](../capabilities/contacts-and-companies.md)\n- [Labels, attributes, and filters](../capabilities/labels-attributes-and-filters.md)\n- [Authentication and account isolation](../getting-started/authentication-and-account-isolation.md)\n- [Approvals and policy](../../../concepts/approvals-and-policy.md)\n- [Error reference](../../../reference/errors.md)\n",
    "text": "Manage Chatwoot contacts and companies\n\nUse this guide to find authoritative customer records, resolve ambiguous\nmatches, attach or detach one contact and company, or merge two contacts. Every\nmutation ends with a provider read instead of relying on the write response.\n\nThe safest path is discovery before mutation. Search results identify\ncandidates; they do not prove that two records represent the same person or\norganization.\n\nWhen to use this guide\n\nUse this procedure for one bounded customer-data intent:\n• find a contact or company without changing provider state;\n• attach one known contact to one known company;\n• detach one known contact from one known company;\n• merge one reviewed contact into another.\n\nUse the complete capability reference (../capabilities/contacts-and-companies.md)\nfor import, export, notes, labels, inbox associations, CRUD, and other\noperations.\n\nThis guide does not define Chatwoot search fields, relationship body fields, or\nmerge participants. Those fields belong to the pinned provider route, not the\ngeneric connector schema.\n\nPrerequisites\n\nBefore starting, obtain:\n• an authenticated AIP caller bound to the intended tenant;\n• an admitted Chatwoot connector instance for the intended account;\n• the exact pinned Chatwoot request schema for any body-bearing mutation;\n• authority to read restricted customer data;\n• an approver for every POST, PATCH, or DELETE operation;\n• a protected evidence location for identifiers, inputs, and results.\n\nDo not put accountid in Action input. The connector host inserts its\nconfigured account into every provider route.\n\nChoose the narrowest capability\n\n| Intent | Capability | Provider method | Risk | Approval |\n\n| Search contacts | cap:chatwoot:contact.search | GET | Low | No |\n| Apply a provider filter | cap:chatwoot:contact.filter | POST | Medium | Required |\n| Read one contact | cap:chatwoot:contact.get | GET | Low | No |\n| Search companies | cap:chatwoot:company.search | GET | Low | No |\n| Read one company | cap:chatwoot:company.get | GET | Low | No |\n| List company contacts | cap:chatwoot:company.contact.list | GET | Low | No |\n| Attach a contact | cap:chatwoot:company.contact.create | POST | Medium | Required |\n| Detach a contact | cap:chatwoot:company.contact.delete | DELETE | High | Required |\n| Merge contacts | cap:chatwoot:contact.merge | POST | Medium | Required |\n\ncontact.filter has a read-like name but a POST provider method. The connector\ntherefore publishes it as a governed mutation with unsafe retry. Prefer the GET\nsearch or list surface when it can answer the task.\n1. Pin route and Action identity\n\nRead capability discovery for the authenticated tenant. Record:\n• connector type, immutable version, instance, and configured account;\n• exact capability ID and catalogue revision;\n• authenticated actor and tenant;\n• credential revision without credential bytes;\n• policy version used for any approval.\n\nStop if the route, account, or capability differs from the reviewed intent.\nConnector admission, capability discovery, actor authorization, and customer\nidentity are separate decisions.\n\nCreate a new Action ID for each read. For a mutation, assign one Action ID and\none external-account-scoped idempotency key before approval. Keep the same\nAction, key, and canonical input while settling an uncertain result.\n2. Find candidate records\n\nUse contact.search or company.search first. Both are low-risk GET\ncapabilities and advertise safe retry.\n\nThe common input envelope can carry a query object:\n\n{\n  \"query\": {\n    \"...\": \"fields from the pinned Chatwoot search route\"\n  }\n}\n\nThis is a structural example, not a copy-ready provider query. The connector\nbounds query size and scalar values but does not freeze provider field names or\nmatching semantics.\n\nRecord the exact query and all candidate IDs needed for the decision. Treat\nnames, email addresses, phone numbers, external identifiers, and company\nattributes as restricted data.\n\nIf provider filtering is necessary, review its body as a mutation:\n\n{\n  \"body\": {\n    \"...\": \"fields from the pinned Chatwoot filter route\"\n  }\n}\n\nApprove the exact filter body and retention purpose. A completed filter result\ndoes not turn POST into a retry-safe read.\n3. Read authoritative before-state\n\nRead every candidate that could be changed:\n• contact.get for each source, destination, or relationship candidate;\n• company.get for the target company;\n• company.contact.list for the company's current relationships.\n\nFor a contact merge, also inspect the records and relationships that tenant\npolicy considers material. The catalogue exposes contact conversations,\nlabels, notes, and company relationships, but it does not publish one aggregate\nmerge preview.\n\nStop when:\n• more than one candidate remains plausible;\n• source and destination identities are equal or unclear;\n• records belong to another configured account;\n• current relationships conflict with the requested intent;\n• the provider schema revision is unknown;\n• approval cannot bind the exact body.\n\nDo not resolve ambiguity by choosing the first search result.\n4. Attach a contact to a company\n\nUse cap:chatwoot:company.contact.create. The connector builds:\n\nPOST /api/v1/accounts/{configuredaccountid}/companies/{companyid}/contacts\n\ncompanyid is a top-level Action input. The contact relationship fields\nbelong inside the provider-owned body:\n\n{\n  \"companyid\": \"88\",\n  \"body\": {\n    \"...\": \"reviewed fields from the pinned relationship route\"\n  }\n}\n\nBefore dispatch, bind approval to the company, intended contact, canonical\nbody, account, actor, capability, and policy hash. Keep the exact request with\nthe idempotency record.\n\nAfter a completed result, call company.contact.list and confirm the expected\nrelationship once. Do not infer relationship creation from HTTP success alone.\n5. Detach a contact from a company\n\nUse cap:chatwoot:company.contact.delete only after the before-state list\nconfirms the exact relationship. Its provider route contains both identities:\n\nDELETE /api/v1/accounts/{configuredaccountid}/companies/{companyid}/contacts/{contactid}\n\nAn exact Action input can be submitted as follows:\n\nCOMPANYID=88\nCONTACTID=205\nDETACHACTION=actchatwootcompanydetach001\nDETACHKEY=chatwoot-company-88-contact-205-detach-v1\n\naipctl action call \\\n  \"$AIPURL\" \\\n  cap:chatwoot:company.contact.delete \\\n  --action-id \"$DETACHACTION\" \\\n  --idempotency-key \"$DETACHKEY\" \\\n  --input \"{\\\"companyid\\\":\\\"$COMPANYID\\\",\\\"contactid\\\":\\\"$CONTACTID\\\"}\"\n\nThe operation removes the relationship. Its catalogue description does not\nclaim that it deletes the contact record.\n\nThis DELETE is high risk, unsafe to retry, and has no rollback. Re-list company\ncontacts after settlement.\n6. Merge two contacts\n\nUse cap:chatwoot:contact.merge only after a human has reviewed distinct\nsource and destination records. The connector builds:\n\nPOST /api/v1/accounts/{configuredaccountid}/actions/contactmerge\n\nThe route has no contact path parameter. Both identities and any merge options\nbelong to a provider-owned body:\n\n{\n  \"body\": {\n    \"...\": \"source, destination, and fields from the pinned merge route\"\n  }\n}\n\nBefore approval, retain:\n1. authoritative snapshots of both contacts;\n2. the reviewed source-to-destination direction;\n3. collision decisions for names and customer identifiers;\n4. material conversations, notes, labels, inboxes, and company relationships;\n5. the exact canonical provider body;\n6. the intended post-merge verification fields.\n\nThe connector publishes no merge preview, rollback, or per-related-object\ntransaction model. Do not describe the operation as reversible or atomic\nacross all related data.\n\nAfter settlement, read the destination contact and material relationships.\nDetermine source behavior from provider state rather than assuming deletion,\nredirection, or archival.\n\nUnderstand mutation transport\n\nFor catalogue mutations, the connector:\n• requires a non-blank idempotency key of at most 512 bytes;\n• sends X-AIP-Action-ID with the Action ID;\n• sends Idempotency-Key with the supplied key;\n• injects the configured account into the route;\n• sends either a JSON body or multipart, never both;\n• returns the provider HTTP status, parsed body, and request ID when exposed.\n\nAll relationship and merge mutations require approval, advertise unsafe retry,\ncontain restricted PII, and declare rollback unsupported. Forwarding a key\ndoes not prove that a particular Chatwoot route deduplicates repeated requests.\n\nVerify the intended state\n\nUse new read Action IDs after a mutation. Verify only authoritative fields\nneeded by the task:\n\n| Mutation | Required final read |\n\n| Attach | List company contacts and identify the intended contact once |\n| Detach | List company contacts and confirm the intended relationship is absent |\n| Merge | Read the destination and each material relationship selected before approval |\n\nRetain the mutation Action ID, idempotency key hash, approval record, exact\ninput, configured account and route, provider status, provider request ID when\npresent, final read Action IDs, and checked fields.\n\nDo not retain credentials or unrelated customer fields merely to enlarge the\nevidence set.\n\nRecover an uncertain mutation\n\n| Symptom | Read first | Decision |\n\n| Attach response lost | Company contacts | Accept the existing relationship or escalate; do not attach blindly |\n| Detach response lost | Company contacts and audit evidence | Accept confirmed absence or escalate; do not repeat a high-risk delete blindly |\n| Merge response lost | Destination, source when addressable, and material relationships | Accept reconciled state or escalate; do not submit a second merge |\n| Provider rejected body | Current objects and pinned provider schema | Correct the intent before creating another Action |\n| Authentication failure | Connector route, account binding, and credential revision | Repair configuration; never move the token into Action input |\n| Oversized or non-JSON result | Provider state and retained request identity | Verify through bounded reads; do not infer failure from response shape |\n\nTransport, provider-temporary, cancellation, and oversized-response failures\ncan leave mutation outcome uncertain. Every connector failure reports\nretryable: false even though read contracts separately advertise safe retry.\n\nA corrective relationship or record change is a new governed intent against\ncurrent state. It is not rollback of the earlier Action.\n\nCompletion criteria\n\nThe workflow is complete only when:\n• route, account, actor, and capability match the intended tenant;\n• search ambiguity was resolved through authoritative reads;\n• each mutation used one exact input, Action ID, key, and approval;\n• attach or detach state was verified through company contacts;\n• merge direction and material relationships were reviewed and verified;\n• restricted evidence follows tenant redaction and retention policy;\n• no uncertain mutation was replaced with an uncorrelated retry.\n\nThese criteria validate application handling. They do not establish provider\npayload compatibility, customer identity correctness, or live qualification.\n\nRelated documentation\n• Contacts and companies (../capabilities/contacts-and-companies.md)\n• Labels, attributes, and filters (../capabilities/labels-attributes-and-filters.md)\n• Authentication and account isolation (../getting-started/authentication-and-account-isolation.md)\n• Approvals and policy (../../../concepts/approvals-and-policy.md)\n• Error reference (../../../reference/errors.md)\n"
  },
  "integrity": {
    "algorithm": "sha256",
    "sourceDigest": "b83023e79a8a56317cb273df373d3bb0d402be669c87a48f20cea232cba28e23"
  }
}
