{
  "schemaVersion": "1.0",
  "title": "Chatwoot mutation approval and idempotency",
  "description": "This reference gives one governance model for all 153 Chatwoot capabilities at the reviewed revision. Use it to decide when approval is required, how to bind an idempotency key, and what to do after an uncertain result.",
  "canonical": "https://getaip.org/docs/connectors/chatwoot/reference/mutation-approval-and-idempotency",
  "route": "/docs/connectors/chatwoot/reference/mutation-approval-and-idempotency",
  "source": "docs/connectors/chatwoot/reference/mutation-approval-and-idempotency.md",
  "protocol": "Agent Interoperability Protocol",
  "protocolVersion": "1.0",
  "section": "Connectors",
  "documentType": "Connector",
  "language": "en",
  "downloads": {
    "md": "/docs/download/connectors/chatwoot/reference/mutation-approval-and-idempotency.md",
    "txt": "/docs/download/connectors/chatwoot/reference/mutation-approval-and-idempotency.txt",
    "json": "/docs/download/connectors/chatwoot/reference/mutation-approval-and-idempotency.json",
    "pdf": "/docs/download/connectors/chatwoot/reference/mutation-approval-and-idempotency.pdf"
  },
  "content": {
    "format": "text/markdown",
    "markdown": "---\ntitle: Chatwoot mutation approval and idempotency\ndescription: >-\n  Apply the exact approval, idempotency, retry, and reconciliation contract to\n  every Chatwoot mutation\nkind: reference\naudience: application-developer\nappliesTo: \"1.x\"\nwritingStandard: \"aip-docs/1.0\"\nlastReviewedRevision: \"97be86e9efedf07ecf1783b03800f683f107fb04\"\nconnector: chatwoot\naliases:\n  - Chatwoot mutation policy\n  - Chatwoot approval and retry rules\n---\n\n# Chatwoot mutation approval and idempotency\n\nThis reference gives one governance model for all 153 Chatwoot capabilities at\nthe reviewed revision. Use it to decide when approval is required, how to bind\nan idempotency key, and what to do after an uncertain result.\n\nApproval authorizes one reviewed intent. Idempotency correlates attempts for\nthat intent. Neither control proves that Chatwoot applied the intended state.\n\n## Capability population\n\n| Population | Count | Approval | Published retry |\n|---|---:|---|---|\n| Catalogue GET reads | 62 | Not required | Safe |\n| Catalogue POST, PATCH, and DELETE operations | 88 | Required | Unsafe |\n| `cap:chatwoot:message:create` | 1 | Required | Unsafe |\n| `cap:chatwoot:conversation:status` | 1 | Required | Safe |\n| `cap:chatwoot:conversation:handoff` | 1 | Required | Unsafe |\n| **All capabilities** | **153** | **91 require approval** | **63 safe, 90 unsafe** |\n\nThe catalogue classifies every non-GET provider method as a mutation. This\nincludes read-like names backed by POST, such as `contact.filter`, and\noperational commands backed by POST.\n\nRisk and approval are related but not identical. Every catalogue mutation\nrequires approval whether its published risk is medium or high. The three\nconnector composites also require approval.\n\n## Shared mutation contract\n\nAll 91 mutations publish:\n\n| Contract field | Reviewed value |\n|---|---|\n| Approval | Required |\n| Approver selector | Tenant policy |\n| Approval validity | 900,000 ms |\n| Approval evidence | Reason, input snapshot, policy decision |\n| Idempotency | Required |\n| Key scope | External account |\n| Collision behavior | Revalidate input hash |\n| Published key lifetime | 86,400,000 ms |\n| Data | Restricted, PII present, redaction required |\n| Completion | Synchronous |\n| Compensation | Rollback not supported |\n\nCatalogue mutations also publish policy version\n`chatwoot-production-mutation-v1`. The three composite approval policies do not\nset that connector-local version field. Runtime and tenant policy still own\nthe admitted approval decision.\n\n## Decide before creating an Action\n\nFor each intended provider change, verify these conditions in order:\n\n1. Confirm the selected capability is admitted for this tenant, connector\n   version, instance, and external account.\n2. Prefer a GET capability when it satisfies the task without changing\n   provider state.\n3. Confirm every path identity is authoritative and tied to the configured\n   account.\n4. Match the provider-owned body to the pinned Chatwoot route.\n5. Identify every customer-visible, destructive, relationship, automation, or\n   disclosure effect that can occur.\n6. Select the final read that can establish the intended state.\n7. Select the recovery decision for a lost provider response.\n\nStop before Action creation when any answer is unknown. Approval should not be\nused to compensate for an unpinned body or ambiguous customer identity.\n\n## Bind approval to one immutable intent\n\nThe approval record needs enough evidence to distinguish this mutation from\nevery nearby alternative:\n\n| Evidence | Bind to |\n|---|---|\n| Actor and tenant | Authenticated AIP identity |\n| Route | Connector type, version, instance, and external account |\n| Capability | Exact ID, including dots or colons |\n| Input snapshot | Canonical path inputs, query, body, or multipart identity |\n| Reason | Human-readable business purpose |\n| Policy decision | Policy version, hash, constraints, and decision identity |\n| Effect review | Visibility, destination, deletion, merge direction, or automation consequence |\n| Verification plan | Authoritative read and fields checked after settlement |\n\nAn approval is not a reusable permission for edited input. Changing message\ntext, `private` visibility, status, assignee, record identity, relationship\ndirection, filter, import, export, merge participants, or provider body creates\na different intent.\n\nThe published approval lifetime is 15 minutes. Expiry does not establish that\nthe provider call did or did not run. Consult Action state and provider\nevidence before requesting a replacement decision.\n\n## Design the idempotency key\n\nOne mutation intent receives one stable key. The published scope is the\nconfigured external account, so the application should include enough\nbusiness context to avoid collision inside that account.\n\nReadable examples:\n\n```text\nchatwoot:conversation:314:status:pending:v1\nchatwoot:company:88:contact:205:detach:v1\nchatwoot:contact-merge:source-205:destination-401:v1\n```\n\nDo not place customer names, email addresses, message text, tokens, or other\nPII in the key. Store the canonical input hash next to the key instead.\n\nThe catalogue connector accepts a key only when it:\n\n- is present for a mutation;\n- is not blank after trimming;\n- is at most 512 bytes;\n- contains no control characters.\n\nThe protocol collision behavior is `revalidate_input_hash`. Reusing a key with\ndifferent canonical input is a collision, not a request to update the original\nintent.\n\nThe published 24-hour lifetime bounds retained idempotency behavior. It is not\npermission to recycle a business key after one day. Use a new versioned key\nfor a genuinely new intent.\n\n## Catalogue enforcement and forwarding\n\nFor the 150 dot-named catalogue capabilities, invocation performs these steps:\n\n1. classify the operation by its frozen provider method;\n2. require and validate a key for non-GET methods;\n3. build the account-scoped provider URL;\n4. add `X-AIP-Action-ID` with the Action ID;\n5. add `Idempotency-Key` when a key is present;\n6. send the bounded query, JSON body, or multipart body;\n7. return provider status, body, and request ID when exposed.\n\nA read may carry an optional key, which the connector also forwards. All 62\nGET reads advertise safe retry.\n\nHeader forwarding establishes correlation at the connector boundary. The\nreviewed code does not prove that every Chatwoot route stores, interprets, or\ndeduplicates `Idempotency-Key`.\n\n## Composite enforcement boundary\n\nThe three colon-named composites publish required external-account\nidempotency, but their direct connector branches do not call the catalogue key\nvalidator.\n\nThey also do not send `Idempotency-Key` or `X-AIP-Action-ID` headers to\nChatwoot. Message and handoff instead place the Action ID inside connector\ncontent attributes when they create a message.\n\n| Composite | Provider effect | Direct key enforcement | Provider key header | Retry |\n|---|---|---|---|---|\n| `message:create` | Create public reply or private note | No | No | Unsafe |\n| `conversation:status` | Set open, resolved, or pending | No | No | Safe |\n| `conversation:handoff` | Assign, then optionally create private note | No | No | Unsafe |\n\nUse the governed AIP path so capability contract, identity, policy, approval,\nand idempotency admission occur before connector invocation. Direct connector\ninvocation is not a substitute for that gate.\n\n## Interpret safe retry narrowly\n\nPublished safe retry means the contract permits a correlated repeat under its\nstated scope. It does not mean repeat without reading current state.\n\n| Operation class | Before a repeat |\n|---|---|\n| GET catalogue read | Reuse or create a read Action according to caller policy; expect a new provider snapshot |\n| `conversation:status` | Read the conversation; accept matching state before considering the same intent |\n| Any other catalogue mutation | Reconcile provider state and retained request identity; do not replay blindly |\n| `message:create` | Read messages and correlate AIP content attributes where exposed |\n| `conversation:handoff` | Read assignment and messages separately because the composite can partially complete |\n\nAll connector failures currently set `retryable: false`. This failure field is\nconservative and does not erase the capability-level safe-retry declaration\nfor reads or status. Use both the contract and authoritative current state.\n\n## Keep Action, key, and input together\n\nFor one mutation, retain:\n\n```json\n{\n  \"action_id\": \"act_chatwoot_example_001\",\n  \"capability_id\": \"cap:chatwoot:example.operation\",\n  \"idempotency_key_hash\": \"sha256:...\",\n  \"canonical_input_hash\": \"sha256:...\",\n  \"approval_id\": \"appr_...\",\n  \"policy_hash\": \"sha256:...\",\n  \"connector_instance\": \"instance:...\",\n  \"external_account\": \"account:...\",\n  \"provider_request_id\": null\n}\n```\n\nThis is an evidence shape, not an AIP request schema. Keep the raw key and\nrestricted input in an appropriately protected store. Do not log credentials\nor full customer data for convenience.\n\nWhen a response is lost, query durable Action state with the original Action\nID. Do not create a replacement Action merely to obtain a new response.\n\n## Interpret completion and failure\n\nA catalogue completion proves that the provider returned a successful HTTP\nstatus within the active response bound. A composite completion proves its\nimplemented provider calls returned success and the connector constructed its\nresult.\n\nNeither result proves:\n\n- customer-channel delivery;\n- absence of later provider processing;\n- atomic handoff;\n- provider-side idempotency retention;\n- intended state without a final read.\n\nProvider `429` and server responses, transport failures, cancellation, and\noversized invocation responses can mark outcome uncertain. The provider may\nhave accepted a mutation before the connector lost or rejected its response.\n\n## Reconcile by effect\n\n| Mutation effect | Authoritative evidence | Safe decision |\n|---|---|---|\n| Create | Search or list using stable provider fields | Accept one matching object or escalate; do not create another blindly |\n| Update or status | Read the exact object | Accept matching state or create a new corrective intent |\n| Send message or note | List messages and correlate Action attributes | Accept the existing message or escalate; do not resend blindly |\n| Attach or detach relationship | List the relationship from its owning object | Accept current state or create a separately approved correction |\n| Merge | Read destination, source when addressable, and material relationships | Accept reconciled state or escalate; never submit another merge blindly |\n| Import or export | Provider job, artifact, and sampled record evidence | Determine whether work or disclosure already exists |\n| Delete | Object absence plus audit and dependent state | Correlate the original Action; absence alone does not identify the actor |\n| Handoff | Assignment first, then private-note evidence | Resolve partial effects independently |\n\nNo Chatwoot mutation publishes an AIP rollback capability. A corrective\nprovider operation is a new Action with a new approval and a key for that new\nintent.\n\n## Verification checklist\n\nBefore declaring a mutation settled, require:\n\n- exact admitted capability, connector version, instance, and account;\n- one canonical input and input hash;\n- one Action ID and one stable idempotency key;\n- one approval matching the exact input and effect;\n- no changed input under the original key;\n- a terminal AIP state correlated to the Action;\n- provider status and request identity where exposed;\n- an authoritative final read for the intended effect;\n- explicit resolution of any uncertain or partial outcome;\n- restricted-data redaction and retention.\n\nThese controls validate use of the reviewed connector contract. They do not\nestablish provider deduplication, production readiness, or live qualification.\n\n## Related documentation\n\n- [Chatwoot capability index](../capabilities/README.md)\n- [AIP composite operations](../capabilities/aip-composite-operations.md)\n- [Actions and sessions](../../../concepts/actions-and-sessions.md)\n- [Approvals and policy](../../../concepts/approvals-and-policy.md)\n- [Error reference](../../../reference/errors.md)\n",
    "text": "Chatwoot mutation approval and idempotency\n\nThis reference gives one governance model for all 153 Chatwoot capabilities at\nthe reviewed revision. Use it to decide when approval is required, how to bind\nan idempotency key, and what to do after an uncertain result.\n\nApproval authorizes one reviewed intent. Idempotency correlates attempts for\nthat intent. Neither control proves that Chatwoot applied the intended state.\n\nCapability population\n\n| Population | Count | Approval | Published retry |\n\n| Catalogue GET reads | 62 | Not required | Safe |\n| Catalogue POST, PATCH, and DELETE operations | 88 | Required | Unsafe |\n| cap:chatwoot:message:create | 1 | Required | Unsafe |\n| cap:chatwoot:conversation:status | 1 | Required | Safe |\n| cap:chatwoot:conversation:handoff | 1 | Required | Unsafe |\n| All capabilities | 153 | 91 require approval | 63 safe, 90 unsafe |\n\nThe catalogue classifies every non-GET provider method as a mutation. This\nincludes read-like names backed by POST, such as contact.filter, and\noperational commands backed by POST.\n\nRisk and approval are related but not identical. Every catalogue mutation\nrequires approval whether its published risk is medium or high. The three\nconnector composites also require approval.\n\nShared mutation contract\n\nAll 91 mutations publish:\n\n| Contract field | Reviewed value |\n\n| Approval | Required |\n| Approver selector | Tenant policy |\n| Approval validity | 900,000 ms |\n| Approval evidence | Reason, input snapshot, policy decision |\n| Idempotency | Required |\n| Key scope | External account |\n| Collision behavior | Revalidate input hash |\n| Published key lifetime | 86,400,000 ms |\n| Data | Restricted, PII present, redaction required |\n| Completion | Synchronous |\n| Compensation | Rollback not supported |\n\nCatalogue mutations also publish policy version\nchatwoot-production-mutation-v1. The three composite approval policies do not\nset that connector-local version field. Runtime and tenant policy still own\nthe admitted approval decision.\n\nDecide before creating an Action\n\nFor each intended provider change, verify these conditions in order:\n1. Confirm the selected capability is admitted for this tenant, connector\n   version, instance, and external account.\n2. Prefer a GET capability when it satisfies the task without changing\n   provider state.\n3. Confirm every path identity is authoritative and tied to the configured\n   account.\n4. Match the provider-owned body to the pinned Chatwoot route.\n5. Identify every customer-visible, destructive, relationship, automation, or\n   disclosure effect that can occur.\n6. Select the final read that can establish the intended state.\n7. Select the recovery decision for a lost provider response.\n\nStop before Action creation when any answer is unknown. Approval should not be\nused to compensate for an unpinned body or ambiguous customer identity.\n\nBind approval to one immutable intent\n\nThe approval record needs enough evidence to distinguish this mutation from\nevery nearby alternative:\n\n| Evidence | Bind to |\n\n| Actor and tenant | Authenticated AIP identity |\n| Route | Connector type, version, instance, and external account |\n| Capability | Exact ID, including dots or colons |\n| Input snapshot | Canonical path inputs, query, body, or multipart identity |\n| Reason | Human-readable business purpose |\n| Policy decision | Policy version, hash, constraints, and decision identity |\n| Effect review | Visibility, destination, deletion, merge direction, or automation consequence |\n| Verification plan | Authoritative read and fields checked after settlement |\n\nAn approval is not a reusable permission for edited input. Changing message\ntext, private visibility, status, assignee, record identity, relationship\ndirection, filter, import, export, merge participants, or provider body creates\na different intent.\n\nThe published approval lifetime is 15 minutes. Expiry does not establish that\nthe provider call did or did not run. Consult Action state and provider\nevidence before requesting a replacement decision.\n\nDesign the idempotency key\n\nOne mutation intent receives one stable key. The published scope is the\nconfigured external account, so the application should include enough\nbusiness context to avoid collision inside that account.\n\nReadable examples:\n\nchatwoot:conversation:314:status:pending:v1\nchatwoot:company:88:contact:205:detach:v1\nchatwoot:contact-merge:source-205:destination-401:v1\n\nDo not place customer names, email addresses, message text, tokens, or other\nPII in the key. Store the canonical input hash next to the key instead.\n\nThe catalogue connector accepts a key only when it:\n• is present for a mutation;\n• is not blank after trimming;\n• is at most 512 bytes;\n• contains no control characters.\n\nThe protocol collision behavior is revalidateinputhash. Reusing a key with\ndifferent canonical input is a collision, not a request to update the original\nintent.\n\nThe published 24-hour lifetime bounds retained idempotency behavior. It is not\npermission to recycle a business key after one day. Use a new versioned key\nfor a genuinely new intent.\n\nCatalogue enforcement and forwarding\n\nFor the 150 dot-named catalogue capabilities, invocation performs these steps:\n1. classify the operation by its frozen provider method;\n2. require and validate a key for non-GET methods;\n3. build the account-scoped provider URL;\n4. add X-AIP-Action-ID with the Action ID;\n5. add Idempotency-Key when a key is present;\n6. send the bounded query, JSON body, or multipart body;\n7. return provider status, body, and request ID when exposed.\n\nA read may carry an optional key, which the connector also forwards. All 62\nGET reads advertise safe retry.\n\nHeader forwarding establishes correlation at the connector boundary. The\nreviewed code does not prove that every Chatwoot route stores, interprets, or\ndeduplicates Idempotency-Key.\n\nComposite enforcement boundary\n\nThe three colon-named composites publish required external-account\nidempotency, but their direct connector branches do not call the catalogue key\nvalidator.\n\nThey also do not send Idempotency-Key or X-AIP-Action-ID headers to\nChatwoot. Message and handoff instead place the Action ID inside connector\ncontent attributes when they create a message.\n\n| Composite | Provider effect | Direct key enforcement | Provider key header | Retry |\n\n| message:create | Create public reply or private note | No | No | Unsafe |\n| conversation:status | Set open, resolved, or pending | No | No | Safe |\n| conversation:handoff | Assign, then optionally create private note | No | No | Unsafe |\n\nUse the governed AIP path so capability contract, identity, policy, approval,\nand idempotency admission occur before connector invocation. Direct connector\ninvocation is not a substitute for that gate.\n\nInterpret safe retry narrowly\n\nPublished safe retry means the contract permits a correlated repeat under its\nstated scope. It does not mean repeat without reading current state.\n\n| Operation class | Before a repeat |\n\n| GET catalogue read | Reuse or create a read Action according to caller policy; expect a new provider snapshot |\n| conversation:status | Read the conversation; accept matching state before considering the same intent |\n| Any other catalogue mutation | Reconcile provider state and retained request identity; do not replay blindly |\n| message:create | Read messages and correlate AIP content attributes where exposed |\n| conversation:handoff | Read assignment and messages separately because the composite can partially complete |\n\nAll connector failures currently set retryable: false. This failure field is\nconservative and does not erase the capability-level safe-retry declaration\nfor reads or status. Use both the contract and authoritative current state.\n\nKeep Action, key, and input together\n\nFor one mutation, retain:\n\n{\n  \"actionid\": \"actchatwootexample001\",\n  \"capabilityid\": \"cap:chatwoot:example.operation\",\n  \"idempotencykeyhash\": \"sha256:...\",\n  \"canonicalinputhash\": \"sha256:...\",\n  \"approvalid\": \"appr...\",\n  \"policyhash\": \"sha256:...\",\n  \"connectorinstance\": \"instance:...\",\n  \"externalaccount\": \"account:...\",\n  \"providerrequestid\": null\n}\n\nThis is an evidence shape, not an AIP request schema. Keep the raw key and\nrestricted input in an appropriately protected store. Do not log credentials\nor full customer data for convenience.\n\nWhen a response is lost, query durable Action state with the original Action\nID. Do not create a replacement Action merely to obtain a new response.\n\nInterpret completion and failure\n\nA catalogue completion proves that the provider returned a successful HTTP\nstatus within the active response bound. A composite completion proves its\nimplemented provider calls returned success and the connector constructed its\nresult.\n\nNeither result proves:\n• customer-channel delivery;\n• absence of later provider processing;\n• atomic handoff;\n• provider-side idempotency retention;\n• intended state without a final read.\n\nProvider 429 and server responses, transport failures, cancellation, and\noversized invocation responses can mark outcome uncertain. The provider may\nhave accepted a mutation before the connector lost or rejected its response.\n\nReconcile by effect\n\n| Mutation effect | Authoritative evidence | Safe decision |\n\n| Create | Search or list using stable provider fields | Accept one matching object or escalate; do not create another blindly |\n| Update or status | Read the exact object | Accept matching state or create a new corrective intent |\n| Send message or note | List messages and correlate Action attributes | Accept the existing message or escalate; do not resend blindly |\n| Attach or detach relationship | List the relationship from its owning object | Accept current state or create a separately approved correction |\n| Merge | Read destination, source when addressable, and material relationships | Accept reconciled state or escalate; never submit another merge blindly |\n| Import or export | Provider job, artifact, and sampled record evidence | Determine whether work or disclosure already exists |\n| Delete | Object absence plus audit and dependent state | Correlate the original Action; absence alone does not identify the actor |\n| Handoff | Assignment first, then private-note evidence | Resolve partial effects independently |\n\nNo Chatwoot mutation publishes an AIP rollback capability. A corrective\nprovider operation is a new Action with a new approval and a key for that new\nintent.\n\nVerification checklist\n\nBefore declaring a mutation settled, require:\n• exact admitted capability, connector version, instance, and account;\n• one canonical input and input hash;\n• one Action ID and one stable idempotency key;\n• one approval matching the exact input and effect;\n• no changed input under the original key;\n• a terminal AIP state correlated to the Action;\n• provider status and request identity where exposed;\n• an authoritative final read for the intended effect;\n• explicit resolution of any uncertain or partial outcome;\n• restricted-data redaction and retention.\n\nThese controls validate use of the reviewed connector contract. They do not\nestablish provider deduplication, production readiness, or live qualification.\n\nRelated documentation\n• Chatwoot capability index (../capabilities/README.md)\n• AIP composite operations (../capabilities/aip-composite-operations.md)\n• Actions and sessions (../../../concepts/actions-and-sessions.md)\n• Approvals and policy (../../../concepts/approvals-and-policy.md)\n• Error reference (../../../reference/errors.md)\n"
  },
  "integrity": {
    "algorithm": "sha256",
    "sourceDigest": "624150a7f43bbb581393544388ef190c228750cb3c67581bf9790088c77ca895"
  }
}
