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

# Handoff and task attributes

> What OpenCX writes to Flex when the AI escalates, how the AI worker fits into your TaskRouter workflow, and who closes Flex tasks.

OpenCX does not replace your TaskRouter workflow — it participates in it as one more worker. This page describes exactly what OpenCX writes to a Flex task on handoff, what your workflow is expected to do with it, and the one thing OpenCX explicitly does **not** do (close the task).

## How handoff works

```mermaid theme={"dark"}
flowchart LR
  A[AI is on the task] --> B{AI can resolve?}
  B -- Yes --> C[AI replies, task stays]
  B -- No --> D[Update task attributes]
  D --> E[Reject AI reservation]
  E --> F[Workflow matches handedOff == true]
  F --> G[Rep picks up from human queue]
  G --> H[Handoff note posted in conversation]
```

When a handoff fires, OpenCX runs these steps in order:

<Steps>
  <Step title="Update the Flex task attributes">
    OpenCX writes the handoff fields (see the table below) into the task's `attributes` JSON via TaskRouter's API. Any attributes your workflow already set on the task are preserved.
  </Step>

  <Step title="Reject the AI reservation">
    The <Tooltip tip="A TaskRouter offer of a task to a worker. OpenCX's AI worker accepted it on creation; rejecting it releases the task back into the workflow so another worker can claim it.">AI reservation</Tooltip> is rejected. The task is now eligible for re-routing.
  </Step>

  <Step title="Workflow re-routes">
    TaskRouter re-evaluates the task against your workflow filters. The `handedOff == true` filter matches, and the task is assigned to your human queue.
  </Step>

  <Step title="Handoff note posts to the conversation">
    OpenCX posts a short system message into the Flex conversation with the summary, sentiment, and reason, so the rep who picks up sees it above their first reply.
  </Step>
</Steps>

## What OpenCX writes to the task

| Attribute          | Type            | Purpose                                                                                     |
| ------------------ | --------------- | ------------------------------------------------------------------------------------------- |
| `handedOff`        | boolean         | Always `true`. The filter your workflow keys on.                                            |
| `handoffReason`    | string          | Short phrase the AI wrote when escalating — e.g. *"customer wants to cancel subscription"*. |
| `handoffSummary`   | string          | One-paragraph summary of the conversation so far.                                           |
| `handoffSentiment` | string          | Detected sentiment — `positive`, `neutral`, or `negative`.                                  |
| `handoffLanguage`  | string          | BCP-47 language code of the conversation — e.g. `en`, `es`, `ar`.                           |
| `handoffTimestamp` | ISO 8601 string | When the handoff fired.                                                                     |

<Note>
  OpenCX only adds these attributes. Every other attribute on the task — whatever your workflow set during routing — stays untouched.
</Note>

## What the rep sees in Flex

* The task in their queue with the six handoff attributes above, visible in the **Task** panel of the Flex UI.
* A system message at the top of the Flex conversation thread: the summary, sentiment, and reason formatted for human reading.
* The full <Tooltip tip="All messages exchanged with the customer, including the AI's replies. Rendered in the Flex conversation just like any other message thread.">customer transcript</Tooltip> above the handoff note — no replay, no separate tab.
* Any `flex_*` custom data the workflow set during routing (see below) preserved on the task.

The AI's replies appear in the conversation as author `AI Support Agent`, so the rep can distinguish them from the customer's own messages.

## Using Flex attributes in Autopilot

Every attribute your Flex workflow attaches to a task is flattened onto the OpenCX session's `custom_data` with a `flex_` prefix. Autopilot actions read from `custom_data` directly — no extra mapping.

<AccordionGroup>
  <Accordion title="Flattening rule" icon="code">
    A Flex attribute named `order_id` becomes `flex_order_id` on the session. Nested attributes are flattened with underscores — `customer.name` becomes `flex_customer_name`. The flattening happens on every task event, so late-arriving attributes show up without you re-running anything.
  </Accordion>

  <Accordion title="Typical attributes worth routing on" icon="sliders">
    Teams commonly set `order_id`, `user_id`, `city`, `zone`, or tier tags on the task. Any of these show up as `flex_order_id`, `flex_user_id`, etc., and can be used in an <Tooltip tip="A condition inside an Autopilot topic that decides whether a topic applies to the current conversation. Reads from custom_data." cta="Learn more" href="/handoff/topics">Autopilot condition</Tooltip>.
  </Accordion>

  <Accordion title="Naming comes from your workflow" icon="fingerprint">
    OpenCX does not define which attributes exist — your Flex workflow does. The only guarantee is the `flex_` prefix. If you rename an attribute on the Flex side, update your Autopilot conditions to match.
  </Accordion>
</AccordionGroup>

## Closing Flex tasks

<Warning>
  **OpenCX does not close Flex tasks.** When the AI resolves or hands off, OpenCX only marks its own session state and rejects the reservation — it never calls `task.complete()` or sets `assignmentStatus: 'completed'`. Closing the Flex task is your rep's or your workflow's responsibility.
</Warning>

If you want tasks to close automatically on AI resolution:

* Add a TaskRouter event callback on `task.wrapup` or `reservation.wrapup` and close via your own automation.
* Or configure a Flex workflow rule that closes tasks whose AI reservation was rejected without a successor assignment — depending on how your queues are structured.

Treat this as product parity with your existing Flex task lifecycle, not as something OpenCX overrides.

***

## Related Documentation

<CardGroup cols={2}>
  <Card title="Connect Twilio Flex" icon="plug" href="/integrations/twilio-flex/connect">
    Credentials, webhook registration, workflow filters.
  </Card>

  <Card title="Channels" icon="grid-2" href="/integrations/twilio-flex/channels/web-chat">
    Per-channel implementation on top of this shared setup.
  </Card>

  <Card title="Autopilot topics" icon="sliders" href="/handoff/topics">
    Read `flex_*` custom\_data in topic conditions and actions.
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/integrations/twilio-flex/troubleshooting">
    Handoff fires but the task stays in the AI queue, and other scenarios.
  </Card>
</CardGroup>
