> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stateset.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Upsert workflow binding

> Create or update the brand's binding for {workflow_type}. Upserts on (brand_id, workflow_type, template_key, template_version)…

Create or update the brand's binding for \{workflow\_type}. Upserts on (brand\_id, workflow\_type, template\_key, template\_version).

Because template\_key is part of that key, writing a DIFFERENT template\_key for a workflow\_type the brand already has would create a SECOND row — an ambiguity the dispatch read path refuses to arbitrate (it skips the auto-trigger entirely when more than one enabled row matches). Such a write returns 409 WORKFLOW\_BINDING\_TEMPLATE\_CONFLICT naming the conflicting template\_key, unless `?replace=true` is passed, which deletes the other-template\_key rows in the same transaction. DELETE followed by PUT is the equivalent explicit path.

For workflow\_type `payment-recovery` with `enabled: true`, `binding_config.connector_key_mail` is required and must be a non-blank string — without it the Stripe auto-trigger cannot send and would silently skip every event (400 otherwise).

NOTE: `PATCH /v1/brands/&#123;brand_id&#125;` with a `workflow_bindings` array delete-and-reinserts ALL bindings for the brand and will clobber rows created here.

### Path parameters

<ParamField path="brand_id" type="string (uuid)" required />

<ParamField path="workflow_type" type="string (string)" required />

### Query parameters

<ParamField query="replace" type="boolean">
  Delete the brand's existing bindings for this workflow\_type under a DIFFERENT template\_key, in the same transaction. Without it such a write is a 409 (see the endpoint description).
</ParamField>

### Request body

`WorkflowBindingWrite`

<ParamField body="workflow_type" type="string">
  Optional; must equal the path value when present.
</ParamField>

<ParamField body="template_key" type="string" required />

<ParamField body="template_version" type="integer" />

<ParamField body="task_queue" type="string">
  Defaults to the served task queue.
</ParamField>

<ParamField body="binding_config" type="object">
  Deterministic config for the bound workflow. May hold connector KEYS, never secrets. For an enabled payment-recovery binding, connector\_key\_mail is required and must be non-blank.
</ParamField>

<ParamField body="priority" type="integer" />

<ParamField body="enabled" type="boolean" />

<ParamField body="metadata" type="object" />

### Response

`WorkflowBindingRecord`

<ResponseField name="id" type="string (uuid)" required />

<ResponseField name="brand_id" type="string (uuid)" required />

<ResponseField name="workflow_type" type="string" required />

<ResponseField name="template_key" type="string" required />

<ResponseField name="template_version" type="integer" required />

<ResponseField name="task_queue" type="string" />

<ResponseField name="binding_config" type="object" />

<ResponseField name="priority" type="integer" />

<ResponseField name="enabled" type="boolean" required />

<ResponseField name="metadata" type="object" />

<ResponseField name="created_at" type="string (date-time)" />

<ResponseField name="updated_at" type="string (date-time)" />

### Status codes

| Code  | Meaning                                                                                                                                                                                          |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `200` | Success                                                                                                                                                                                          |
| `400` | Validation error                                                                                                                                                                                 |
| `401` | Unauthorized                                                                                                                                                                                     |
| `404` | Not found                                                                                                                                                                                        |
| `409` | WORKFLOW\_BINDING\_TEMPLATE\_CONFLICT — the brand already has a binding for this workflow\_type under a different template\_key. Retry with ?replace=true, or DELETE the existing binding first. |
| `429` | Rate limited                                                                                                                                                                                     |
| `500` | Internal error                                                                                                                                                                                   |

<RequestExample>
  ```bash cURL theme={null}
  curl --request PUT \
    --url 'https://api.workstream.stateset.com/v1/brands/{brand_id}/workflow-bindings/{workflow_type}' \
    --header 'Authorization: Bearer YOUR_API_KEY' \
    --header 'Content-Type: application/json' \
    --data '{
    "workflow_type": "string",
    "template_key": "string",
    "template_version": 1,
    "task_queue": "string",
    "binding_config": {},
    "priority": 1,
    "enabled": true,
    "metadata": {}
  }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "brand_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "workflow_type": "string",
    "template_key": "string",
    "template_version": 1,
    "task_queue": "string",
    "binding_config": {},
    "priority": 1,
    "enabled": true,
    "metadata": {},
    "created_at": "2026-07-24T12:00:00Z",
    "updated_at": "2026-07-24T12:00:00Z"
  }
  ```
</ResponseExample>
