/clients/{clientId}/assignment-rulesReplace a client's assignment rulesReplace-all upsert of the client's assignment rules. Each rule binds an integration to the client via either a raw filterCriteria object or, for Slack, the slackChannels shorthand — a list of channel names the server expands into the equivalent criteria, so you never have to hand-build the query structure. The two are mutually exclusive, and slackChannels is only valid on a Slack integration.
Replace semantics: any integration omitted from rules has its existing rule removed. Send an empty rules array to clear every rule. A save re-applies the rules to the client's existing conversations (see reapplyStats) and can start a Slack history backfill.
Use a GET-then-PUT flow to avoid unintentionally wiping rules you didn't mean to touch. Cross-tenant clients and integrations return 404 not_found / 400 invalid_argument with no existence leak.
Parameters
clientIdstring · uuidpathrequiredClient UUID.
Request body
requiredapplication/json
Full replacement set of a client's assignment rules.
rulesarray of objectrequiredThe complete rule set. Integrations omitted here have their rule removed; an empty array clears all rules.
Show child attributes
Show array items
A single rule to upsert. Provide at most one of `filterCriteria` or `slackChannels`.
enabledbooleanWhether the rule is active. Defaults to `true` when omitted — so when updating via GET-then-PUT, echo the rule's current `enabled` to preserve it; omitting it re-enables a currently-disabled rule.
filterCriteriavalueRaw rich querybuilder criteria (`{condition, rules}`). Use instead of `slackChannels` for full control or for non-Slack integrations.
integrationIdstring · uuidrequiredThe integration to bind to the client.
slackChannelsarray of stringConvenience shorthand — Slack channel names (with or without a leading `#`) to bind to the client. The server expands these into the matching criteria. Only valid on a Slack integration; mutually exclusive with `filterCriteria`.
Show child attributes
{
"rules": [
{
"enabled": true,
"filterCriteria": null,
"integrationId": "00000000-0000-0000-0000-000000000000",
"slackChannels": [
"general",
"acme-project"
]
}
]
}Responses
Headers
X-RateLimit-LimitThe request cap for the tighter of the per-organization / per-endpoint windows that applied to this request.
X-RateLimit-RemainingRequests remaining in the current window.
X-RateLimit-ResetUnix timestamp (seconds) when the current window resets.
allOf · 2 options
datavaluerequiredThe response payload — shape depends on the endpoint.
metaobjectrequiredPagination metadata — populated on list endpoints, empty on single-resource endpoints.
Show child attributes
cursornull | stringOpaque cursor to pass to the next request.
hasMoreboolean | nullTrue when further pages of results are available.
dataobjectShow child attributes
reapplyStatsvalueShow child attributes
anyOf · 2 options
Summary of how the save re-evaluated the client's conversations. May be `null` if the re-apply could not run.
allOf · 1 option
How a save re-evaluated the client's existing conversations against the new rule set.
assignmentsCreatedintegerassignmentsRemovedintegerconversationsEvaluatedintegerconversationsMatchedintegerrulesarray of objectrequiredShow child attributes
Show array items
A client's assignment rule for a single integration. One is returned per active integration; `ruleId` and the other rule fields are `null` when no rule is configured for the client on that integration yet.
chatLabelsobjectOnly on read-only WhatsApp entries — maps the chat JIDs referenced by `filterCriteria` to display names taken from synced conversations. A ruled chat that never synced a message has no entry.
createdAtnull | string · date-timerequiredUTC timestamp when the rule was created (`null` when none is set).
enabledbooleanrequiredWhether the rule is active.
filterCriteriavaluerequiredShow child attributes
anyOf · 2 options
The rich querybuilder criteria deciding which of the integration's communications are assigned to the client; `null` when no rule is set. For Slack, a channel binding looks like `{"condition":"or","rules":[{"field":"channel","operator":"equals","value":"general"}]}`.
integrationIdstring · uuidrequiredThe integration this rule binds to the client. Pass it back in a PUT to configure the rule.
integrationTypestringrequiredProvider of the bound integration (e.g. `slack`, `gmail`).
priorityintegerrequiredRule ordering (lower number = higher priority).
providerEmailstringrequiredThe account/email the integration authenticates as.
readOnlybooleanThe caller may not write this rule — the integration is configurable only by the user who connected it (WhatsApp). Never include a read-only entry in a PUT replacement set; writing one is rejected. User-authenticated callers still see the rule's real values; callers with no user identity additionally get `rulesHidden: true` with the rule fields withheld.
ruleIdnull | string · uuidrequiredStable UUID of the rule, or `null` when none is configured yet.
rulesHiddenbooleanThe rule was withheld entirely because the caller carries no user identity (org-scoped service keys): `ruleId` and `filterCriteria` are `null` and `enabled` is `false` regardless of what is configured — do not read such an entry as "no rule yet". Deprecated as the do-not-write signal in favor of `readOnly`, which also covers user-authenticated callers.
updatedAtnull | string · date-timerequiredUTC timestamp of the most recent rule update (`null` when none is set).
{
"data": {
"reapplyStats": {
"assignmentsCreated": 0,
"assignmentsRemoved": 0,
"conversationsEvaluated": 0,
"conversationsMatched": 0
},
"rules": [
{
"chatLabels": {
"additionalProp1": "string"
},
"createdAt": "2026-06-09T00:00:00Z",
"enabled": true,
"filterCriteria": null,
"integrationId": "00000000-0000-0000-0000-000000000000",
"integrationType": "slack",
"priority": 0,
"providerEmail": "string",
"readOnly": true,
"ruleId": "00000000-0000-0000-0000-000000000000",
"rulesHidden": true,
"updatedAt": "2026-06-09T00:00:00Z"
}
]
},
"meta": {
"cursor": "string",
"hasMore": true
}
}errorobjectrequiredShow child attributes
codestringrequiredMachine-readable error category.
detailsobjectOptional per-code context (field names, retry windows, …).
messagestringrequiredHuman-readable description — intended for operator logs, not end-user display.
{
"error": {
"code": "invalid_argument",
"message": "limit must be an integer between 1 and 100"
}
}errorobjectrequiredShow child attributes
codestringrequiredMachine-readable error category.
detailsobjectOptional per-code context (field names, retry windows, …).
messagestringrequiredHuman-readable description — intended for operator logs, not end-user display.
{
"error": {
"code": "unauthorized",
"message": "Authentication required"
}
}errorobjectrequiredShow child attributes
codestringrequiredMachine-readable error category.
detailsobjectOptional per-code context (field names, retry windows, …).
messagestringrequiredHuman-readable description — intended for operator logs, not end-user display.
{
"error": {
"code": "not_found",
"message": "client not found"
}
}Headers
Retry-AfterSeconds the client should wait before retrying.
errorobjectrequiredShow child attributes
codestringrequiredMachine-readable error category.
detailsobjectOptional per-code context (field names, retry windows, …).
messagestringrequiredHuman-readable description — intended for operator logs, not end-user display.
{
"error": {
"code": "rate_limited",
"details": {
"retry_after_seconds": 30
},
"message": "Rate limit exceeded"
}
}