Skip to main content
Salfio Docs

Search documentation

Type to search this documentation.

Replace a client's assignment rules

PUT/clients/{clientId}/assignment-rulesReplace a client's assignment rules

Replace-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 · uuidpathrequired

Client UUID.

Request body

required
application/json
objectSaveAssignmentRulesRequest

Full replacement set of a client's assignment rules.

rulesarray of objectrequired

The 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`.

enabledboolean

Whether 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.

default true

filterCriteriavalue

Raw rich querybuilder criteria (`{condition, rules}`). Use instead of `slackChannels` for full control or for non-Slack integrations.

integrationIdstring · uuidrequired

The integration to bind to the client.

slackChannelsarray of string

Convenience 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
Example request
{
  "rules": [
    {
      "enabled": true,
      "filterCriteria": null,
      "integrationId": "00000000-0000-0000-0000-000000000000",
      "slackChannels": [
        "general",
        "acme-project"
      ]
    }
  ]
}

Responses

200The saved rules plus the re-apply summary.application/json

Headers

X-RateLimit-Limit

The request cap for the tighter of the per-organization / per-endpoint windows that applied to this request.

X-RateLimit-Remaining

Requests remaining in the current window.

X-RateLimit-Reset

Unix timestamp (seconds) when the current window resets.

value
allOf · 2 options
Option 1objectEnvelope
datavaluerequired

The response payload — shape depends on the endpoint.

metaobjectrequired

Pagination metadata — populated on list endpoints, empty on single-resource endpoints.

Show child attributes
cursornull | string

Opaque cursor to pass to the next request.

hasMoreboolean | null

True when further pages of results are available.

Option 2object
dataobject
Show child attributes
reapplyStatsvalue
Show child attributes
anyOf · 2 options
Option 1value

Summary of how the save re-evaluated the client's conversations. May be `null` if the re-apply could not run.

allOf · 1 option
Option 1objectReapplyAssignmentRulesStats

How a save re-evaluated the client's existing conversations against the new rule set.

assignmentsCreatedinteger
assignmentsRemovedinteger
conversationsEvaluatedinteger
conversationsMatchedinteger
Option 2nullnullable
rulesarray of objectrequired
Show 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.

chatLabelsobject

Only 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-timerequired

UTC timestamp when the rule was created (`null` when none is set).

enabledbooleanrequired

Whether the rule is active.

filterCriteriavaluerequired
Show child attributes
anyOf · 2 options
Option 1value

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"}]}`.

Option 2nullnullable
integrationIdstring · uuidrequired

The integration this rule binds to the client. Pass it back in a PUT to configure the rule.

integrationTypestringrequired

Provider of the bound integration (e.g. `slack`, `gmail`).

priorityintegerrequired

Rule ordering (lower number = higher priority).

providerEmailstringrequired

The account/email the integration authenticates as.

readOnlyboolean

The 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 · uuidrequired

Stable UUID of the rule, or `null` when none is configured yet.

rulesHiddenboolean

The 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-timerequired

UTC timestamp of the most recent rule update (`null` when none is set).

Example response
{
  "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
  }
}
400Request body / query parameters failed validation.application/json
objectErrorEnvelope
errorobjectrequired
Show child attributes
codestringrequired

Machine-readable error category.

one of "unauthorized", "forbidden", "not_found", "invalid_argument", "conflict", "rate_limited", "internal_error", "not_implemented"

detailsobject

Optional per-code context (field names, retry windows, …).

messagestringrequired

Human-readable description — intended for operator logs, not end-user display.

Example response
{
  "error": {
    "code": "invalid_argument",
    "message": "limit must be an integer between 1 and 100"
  }
}
401Missing or invalid bearer token. The message is intentionally opaque — do not rely on it to distinguish "missing" from "invalid".application/json
objectErrorEnvelope
errorobjectrequired
Show child attributes
codestringrequired

Machine-readable error category.

one of "unauthorized", "forbidden", "not_found", "invalid_argument", "conflict", "rate_limited", "internal_error", "not_implemented"

detailsobject

Optional per-code context (field names, retry windows, …).

messagestringrequired

Human-readable description — intended for operator logs, not end-user display.

Example response
{
  "error": {
    "code": "unauthorized",
    "message": "Authentication required"
  }
}
404The referenced resource does not exist, or belongs to a different organization than the one owning the API key. The public API does not distinguish between these cases — both return 404 to avoid leaking cross-tenant existence. application/json
objectErrorEnvelope
errorobjectrequired
Show child attributes
codestringrequired

Machine-readable error category.

one of "unauthorized", "forbidden", "not_found", "invalid_argument", "conflict", "rate_limited", "internal_error", "not_implemented"

detailsobject

Optional per-code context (field names, retry windows, …).

messagestringrequired

Human-readable description — intended for operator logs, not end-user display.

Example response
{
  "error": {
    "code": "not_found",
    "message": "client not found"
  }
}
429Per-organization or per-endpoint rate limit exceeded.application/json

Headers

Retry-After

Seconds the client should wait before retrying.

objectErrorEnvelope
errorobjectrequired
Show child attributes
codestringrequired

Machine-readable error category.

one of "unauthorized", "forbidden", "not_found", "invalid_argument", "conflict", "rate_limited", "internal_error", "not_implemented"

detailsobject

Optional per-code context (field names, retry windows, …).

messagestringrequired

Human-readable description — intended for operator logs, not end-user display.

Example response
{
  "error": {
    "code": "rate_limited",
    "details": {
      "retry_after_seconds": 30
    },
    "message": "Rate limit exceeded"
  }
}

Documentation menu