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

# Python SDK

> Manage your entire woku account from your backend with the woku package: sync and async client over httpx for trackers, VoC tools, tickets, action plans and sends of the v1 API

<Info>
  **Available on every plan, including the free one.** The v1 API and the official SDKs are enabled for every woku account. [See the plans](https://woku.app/pricing).
</Info>

The **`woku`** package is the official **server-side** client for the woku
management API in Python. With a synchronous client (`Woku`) and its
asynchronous twin (`AsyncWoku`) over `httpx` you manage external trackers, VoC
tools (**NPS**, **CSAT**, **CES**), wokus, forms, flows, action plans, support
tickets, survey sends and delivery tracking, all over the public **v1** API. It
is the counterpart of the [JavaScript SDK](/docs/en/development/sdk-javascript), with
the same surface.

<Warning>
  This is a **server-side** SDK. The company secret key grants full management
  access, so it must live only on your backend. Never ship it in a client you do
  not control. To capture feedback from a mobile app use the [React Native
  SDK](/docs/en/development/sdk-react-native), which uses a public capture key.
</Warning>

## Installation

```bash theme={null}
pip install woku
```

Requires Python 3.9 or later. The SDK ships `py.typed`, so type checkers pick up
its types with no extra configuration.

## Initialization

Create a `Woku` instance once and reuse it.

```python theme={null}
from woku import Woku

woku = Woku(api_key="sk_...")  # or set WOKU_API_KEY and call Woku()
```

If you omit `api_key`, the SDK reads the `WOKU_API_KEY` environment variable.

| Option        | Required | Description                                                  |
| ------------- | -------- | ------------------------------------------------------------ |
| `api_key`     | yes      | Company secret key. Defaults to the `WOKU_API_KEY` variable. |
| `base_url`    | no       | API base URL. Defaults to `https://clientapi.woku.app`.      |
| `timeout`     | no       | Per-request timeout in seconds. Defaults to `60.0`.          |
| `max_retries` | no       | Automatic retries for transient failures. Defaults to `2`.   |

Request bodies accept a plain dict (as in the examples) or a Pydantic model
generated from `woku._generated.models`.

## Authentication

The SDK authenticates with the **Company Key**, the same secret key the
[API](/docs/en/development/api) uses. The company owner gets it from the company
**Information** section in the admin app:
[admin.woku.app](https://admin.woku.app).

### Rotate or revoke the key

Rotating generates a new key and **immediately invalidates the previous one**;
store the returned key before continuing.

```python theme={null}
result = woku.company.rotate_key()
# store result["secretKey"] securely; the previous key stops working

woku.company.revoke_key()  # leaves the account without an active key
```

## Quickstart

An end-to-end flow: create a tracker, create an NPS tool, send it and read the
response rate.

```python theme={null}
from woku import Woku

woku = Woku(api_key="sk_...")

# 1. Create a tracker definition (idempotent).
tracker = woku.trackers.create({"name": "Store #1", "system": "retail"})

# 2. Create an NPS tool.
tool = woku.nps_tools.create(
    {"name": "Post-purchase", "npsMessage": "How likely are you to recommend us?"}
)

# 3. Assign a tracker value to the NPS tool, so every response is tagged with
#    the store and reporting groups by it.
woku.trackers.assign_to_entity(
    "nps", tool["_id"], {"name": tracker["name"], "value": "TX-42"}
)

# 4. Send it by email or WhatsApp.
woku.nps.send_invitations(
    {"channel": "email", "npsToolId": tool["_id"], "recipients": ["ana@example.com"]}
)

# 5. Read delivery and response rate.
stats = woku.dispatches.stats({"channel": "email"})
print(stats["responseRate"])
```

## Async client

`AsyncWoku` exposes the same resources with `await` methods and `async for`
iteration. Use it as a context manager to close the connection pool.

```python theme={null}
import asyncio
from woku import AsyncWoku


async def main() -> None:
    async with AsyncWoku(api_key="sk_...") as woku:
        async for ticket in await woku.tickets.list({"severity": "high"}):
            print(ticket["title"])


asyncio.run(main())
```

## Main flows

### Support tickets

Tickets are generated by woku's AI. You can list, filter and curate them.

```python theme={null}
for ticket in woku.tickets.list({"severity": "high"}):
    print(ticket["title"])

stats = woku.tickets.stats()
```

### Action plans

Approve and manage action plans inside woku: change their status and manage
their tasks.

```python theme={null}
woku.action_plans.approve("plan_123")

# Read the plan detail and its timeline.
plan = woku.action_plans.get("plan_123")
events = woku.action_plans.events("plan_123")

# Complete or reopen the plan.
woku.action_plans.complete("plan_123")
```

## Pagination

List methods return an iterable page. Iterate every item across pages, or walk
page by page:

```python theme={null}
for ticket in woku.tickets.list({"severity": "high"}):
    print(ticket["title"])

first = woku.dispatches.list({"channel": "whatsapp"})
if first.has_next_page():
    second = first.get_next_page()
```

The async client iterates with `async for`.

## Idempotency

Creates carry an automatic `Idempotency-Key`, so a retry after a transient
failure never creates twice. Actions (send, test, reply) are **not** retried on
their own. You can pass your own key per call with the `options` argument.

## Error handling

Every failure is a `WokuError`. HTTP errors are typed subclasses carrying the
server `status`, body and `request_id`:

```python theme={null}
from woku import NotFoundError, RateLimitError

try:
    woku.tickets.get("nonexistent")
except NotFoundError as err:
    print(err.status, err.request_id)  # 404, "req_..."
except RateLimitError as err:
    print("retry after", err.retry_after_seconds)
```

Transport failures (DNS, TLS, timeout) are `WokuConnectionError` and
`WokuTimeoutError`. The SDK retries GETs and idempotent writes automatically
with backoff, honoring the `Retry-After` header.

## Per-call configuration

Every method accepts overrides in the `options` argument:

```python theme={null}
woku.tickets.list({"severity": "high"}, options={"timeout": 10.0, "max_retries": 0})
woku.nps_tools.create(body, options={"idempotency_key": "my-key"})
```

## Reference

Complete reference of namespaces and methods. For the exact request and response shapes, see the [API reference](https://woku.app/docs/api-reference).

### action\_plans

Reads and drives action plans, including the managed kanban (tasks, AI conversation, and status transitions).

| Method                                              | Returns                                                                                             | Description                                                                                                         |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `list(params=None, options=None)`                   | `SyncPage[WokuRecord] (AsyncPage[WokuRecord] on the async client; both auto-paginate on iteration)` | Lists action plans, optionally filtered by group, status, source, priority, search, or date range, with pagination. |
| `get(plan_id, options=None)`                        | `WokuRecord`                                                                                        | Fetches a single action plan by id.                                                                                 |
| `events(plan_id, options=None)`                     | `list[WokuRecord]`                                                                                  | Returns the plan timeline as a list of events, oldest first.                                                        |
| `get_conversation(plan_id, options=None)`           | `WokuRecord`                                                                                        | Returns the plan's AI conversation (read-only).                                                                     |
| `reply(plan_id, text, options=None)`                | `WokuRecord`                                                                                        | Sends a reply to the plan AI agent (a paid, asynchronously composed turn with confirm sent automatically).          |
| `create_task(plan_id, body, options=None)`          | `WokuRecord`                                                                                        | Creates a task on the given action plan (idempotent).                                                               |
| `update_task(plan_id, task_id, body, options=None)` | `WokuRecord`                                                                                        | Updates a specific task on the given action plan.                                                                   |
| `reorder_tasks(plan_id, body, options=None)`        | `WokuRecord`                                                                                        | Reorders the tasks of the given action plan.                                                                        |
| `delete_task(plan_id, task_id, options=None)`       | `WokuRecord`                                                                                        | Deletes a specific task from the given action plan.                                                                 |
| `approve(plan_id, options=None)`                    | `WokuRecord`                                                                                        | Approves the action plan (status transition).                                                                       |
| `reopen(plan_id, options=None)`                     | `WokuRecord`                                                                                        | Reopens the action plan (status transition).                                                                        |
| `cancel(plan_id, options=None)`                     | `WokuRecord`                                                                                        | Cancels the action plan (status transition).                                                                        |
| `complete(plan_id, options=None)`                   | `WokuRecord`                                                                                        | Marks the action plan complete (status transition).                                                                 |
| `resume(plan_id, options=None)`                     | `WokuRecord`                                                                                        | Resumes the action plan (status transition).                                                                        |

### action\_plan\_groups

Manages action-plan groups (list, fetch with stats, create, update, enable/disable, and delete).

| Method                                         | Returns            | Description                                                      |
| ---------------------------------------------- | ------------------ | ---------------------------------------------------------------- |
| `list(params=None, options=None)`              | `list[WokuRecord]` | Lists action-plan groups, optionally filtered by a search query. |
| `get(group_id, options=None)`                  | `WokuRecord`       | Fetches one group along with its embedded stats.                 |
| `create(body, options=None)`                   | `WokuRecord`       | Creates a new action-plan group (idempotent).                    |
| `update(group_id, body, options=None)`         | `WokuRecord`       | Updates an existing action-plan group.                           |
| `set_enabled(group_id, enabled, options=None)` | `WokuRecord`       | Enables or disables the action-plan group.                       |
| `delete(group_id, options=None)`               | `WokuRecord`       | Deletes the action-plan group.                                   |

### company

Manages the caller company and its API key at /v1/companies/me.

| Method                     | Returns        | Description                                                              |
| -------------------------- | -------------- | ------------------------------------------------------------------------ |
| `me(options=None)`         | `WokuRecord`   | Gets the caller company.                                                 |
| `rotate_key(options=None)` | `ApiKeyResult` | Rotates the secret key, replacing the current one with the returned key. |
| `revoke_key(options=None)` | `WokuRecord`   | Revokes the secret key so all subsequent requests become unauthorized.   |

### dispatches

Delivery tracking over invitation dispatches (/v1/dispatches).

| Method                             | Returns                                                        | Description                                                                |
| ---------------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `list(params=None, options=None)`  | `SyncPage[Dispatch] (AsyncPage[Dispatch] on the async client)` | Lists the invitation dispatches with delivery status and no recipient PII. |
| `stats(params=None, options=None)` | `DispatchStats`                                                | Returns response-rate metrics computed over the dispatches.                |

### flows

Read-only access to data flows (/v1/flows): list them and fetch one by id.

| Method                            | Returns                                                    | Description                                                                                             |
| --------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `list(params=None, options=None)` | `SyncPage[WokuRecord] (AsyncFlows: AsyncPage[WokuRecord])` | List data flows with optional page/limit pagination params, returning a paginated page of flow records. |
| `get(flow_id, options=None)`      | `WokuRecord`                                               | Fetch a single data flow by its id.                                                                     |

### forms

Reads forms and their responses, and sends form invitations over /v1/forms.

| Method                                               | Returns                                                      | Description                                     |
| ---------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------- |
| `list(params=None, options=None)`                    | `SyncPage[WokuRecord] (AsyncPage[WokuRecord] on AsyncForms)` | Lists forms with pagination.                    |
| `get(form_id, options=None)`                         | `WokuRecord`                                                 | Fetches a single form by id.                    |
| `list_responses(form_id, params=None, options=None)` | `SyncPage[WokuRecord] (AsyncPage[WokuRecord] on AsyncForms)` | Lists the responses of a form (paginated).      |
| `send_invitations(form_id, body, options=None)`      | `InvitationsResult`                                          | Sends a form by email or WhatsApp (idempotent). |

### quarantines

Checks whether a respondent contact is quarantined via the /v1/quarantines endpoint.

| Method                        | Returns      | Description                                                  |
| ----------------------------- | ------------ | ------------------------------------------------------------ |
| `check(params, options=None)` | `WokuRecord` | Checks whether a contact (by email or phone) is quarantined. |

### reports

Reads NPS reports at /v1/reports (company-level and per NPS tool).

| Method                                             | Returns      | Description                                   |
| -------------------------------------------------- | ------------ | --------------------------------------------- |
| `company_nps(params=None, options=None)`           | `WokuRecord` | Fetches the company-level NPS report.         |
| `nps_tool(nps_tool_id, params=None, options=None)` | `WokuRecord` | Fetches the NPS report for a single NPS tool. |

### nps

Sends the NPS survey and reads its responses (/v1/nps).

| Method                                      | Returns                                                            | Description                                             |
| ------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------- |
| `send_invitations(body, options=None)`      | `InvitationsResult`                                                | Sends the NPS survey by email or WhatsApp (idempotent). |
| `list_responses(params=None, options=None)` | `SyncPage[WokuRecord] (AsyncPage[WokuRecord] on the async client)` | Lists NPS responses with pagination.                    |
| `get_response(response_id, options=None)`   | `WokuRecord`                                                       | Gets one NPS response by id.                            |

### csat

Sends the CSAT survey and reads its responses (/v1/csat).

| Method                                      | Returns                                                            | Description                                     |
| ------------------------------------------- | ------------------------------------------------------------------ | ----------------------------------------------- |
| `send_invitations(body, options=None)`      | `InvitationsResult`                                                | Sends the CSAT survey invitations (idempotent). |
| `list_responses(params=None, options=None)` | `SyncPage[WokuRecord] (AsyncPage[WokuRecord] on the async client)` | Lists CSAT responses with pagination.           |
| `get_response(response_id, options=None)`   | `WokuRecord`                                                       | Gets one CSAT response by id.                   |

### ces

Sends the CES survey and reads its responses (/v1/ces).

| Method                                      | Returns                                                            | Description                                    |
| ------------------------------------------- | ------------------------------------------------------------------ | ---------------------------------------------- |
| `send_invitations(body, options=None)`      | `InvitationsResult`                                                | Sends the CES survey invitations (idempotent). |
| `list_responses(params=None, options=None)` | `SyncPage[WokuRecord] (AsyncPage[WokuRecord] on the async client)` | Lists CES responses with pagination.           |
| `get_response(response_id, options=None)`   | `WokuRecord`                                                       | Gets one CES response by id.                   |

### tickets

Reads and curates AI-generated support tickets under /v1/tickets.

| Method                                  | Returns                                                                                       | Description                                                                                                   |
| --------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `list(params=None, options=None)`       | `SyncPage[Ticket] (AsyncPage[Ticket] on the async client; both support pagination iteration)` | Lists support tickets with optional filters (tool, severity, search, destinationId, date range, page, limit). |
| `stats(params=None, options=None)`      | `TicketStats`                                                                                 | Returns aggregate ticket counts by tool and by SAC destination.                                               |
| `get(ticket_id, options=None)`          | `Ticket`                                                                                      | Fetches a single ticket by id.                                                                                |
| `update(ticket_id, body, options=None)` | `Ticket`                                                                                      | Patches a ticket with the given update body.                                                                  |

### ticket\_destinations

Manages SAC ticket destinations under /v1/ticket-destinations.

| Method                                       | Returns                     | Description                                                                              |
| -------------------------------------------- | --------------------------- | ---------------------------------------------------------------------------------------- |
| `list(options=None)`                         | `builtins.list[WokuRecord]` | Lists all configured ticket destinations.                                                |
| `get(destination_id, options=None)`          | `WokuRecord`                | Fetches a single ticket destination by id.                                               |
| `create(body, options=None)`                 | `WokuRecord`                | Creates a new ticket destination (sent idempotently).                                    |
| `update(destination_id, body, options=None)` | `WokuRecord`                | Patches an existing ticket destination.                                                  |
| `delete(destination_id, options=None)`       | `WokuRecord`                | Deletes a ticket destination by id.                                                      |
| `test(destination_id, options=None)`         | `TestConnectionResult`      | Sends a real connectivity test to a saved destination (confirm:true sent automatically). |

### trackers

Manages external tracker definitions and assigns/removes tracker values on wokus and VoC entities (/v1/external-trackers).

| Method                                                                   | Returns                                                            | Description                                                                   |
| ------------------------------------------------------------------------ | ------------------------------------------------------------------ | ----------------------------------------------------------------------------- |
| `list(params=None, options=None)`                                        | `SyncPage[Tracker] (AsyncPage[Tracker] on the async client)`       | List the company tracker definitions, paginated.                              |
| `create(body, options=None)`                                             | `Tracker`                                                          | Create a tracker definition (idempotent).                                     |
| `get(tracker_id, options=None)`                                          | `Tracker`                                                          | Get one tracker definition by id.                                             |
| `update(tracker_id, body, options=None)`                                 | `Tracker`                                                          | Update a tracker definition.                                                  |
| `activate(tracker_id, options=None)`                                     | `Tracker`                                                          | Activate a tracker definition.                                                |
| `deactivate(tracker_id, options=None)`                                   | `Tracker`                                                          | Deactivate a tracker definition.                                              |
| `search_entities(body, options=None)`                                    | `EntitiesByTrackers`                                               | Search VoC entities whose trackers match every provided filter (AND).         |
| `list_woku_values(woku_id, options=None)`                                | `list[WokuRecord]`                                                 | List the tracker values assigned to a woku.                                   |
| `assign_to_woku(woku_id, body, options=None)`                            | `WokuRecord`                                                       | Assign (upsert) a tracker value to a woku by tracker name (idempotent).       |
| `remove_from_woku(woku_id, tracker_name, options=None)`                  | `WokuRecord`                                                       | Remove a tracker value from a woku by tracker name.                           |
| `search_wokus(params, options=None)`                                     | `SyncPage[WokuRecord] (AsyncPage[WokuRecord] on the async client)` | Search wokus by an exact (tracker name, value) pair, paginated.               |
| `list_entity_values(entity_type, entity_id, options=None)`               | `list[WokuRecord]`                                                 | List the tracker values assigned to a VoC entity (nps/csat/ces/form/flow).    |
| `assign_to_entity(entity_type, entity_id, body, options=None)`           | `WokuRecord`                                                       | Assign (upsert) a tracker value to a VoC entity by tracker name (idempotent). |
| `remove_from_entity(entity_type, entity_id, tracker_name, options=None)` | `WokuRecord`                                                       | Remove a tracker value from a VoC entity by tracker name.                     |

### nps\_tools

Manage NPS tool (survey) definitions under /v1/nps-tools.

| Method                                | Returns                                                      | Description                                                                                |
| ------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| `list(params=None, options=None)`     | `SyncPage[NpsTool] (AsyncPage[NpsTool] on the async client)` | List NPS tool definitions with page/limit pagination; the returned page is async-iterable. |
| `create(body, options=None)`          | `NpsTool`                                                    | Create a new NPS tool definition (idempotent POST).                                        |
| `get(tool_id, options=None)`          | `NpsTool`                                                    | Retrieve a single NPS tool by id via the singular /v1/nps-tool/{id} route.                 |
| `update(tool_id, body, options=None)` | `NpsTool`                                                    | Partially update an existing NPS tool definition (PATCH).                                  |
| `delete(tool_id, options=None)`       | `DeletedResult`                                              | Delete an NPS tool definition by id.                                                       |

### csat\_tools

Manage CSAT tool (survey) definitions under /v1/csat-tools.

| Method                                | Returns                                                        | Description                                                                                 |
| ------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `list(params=None, options=None)`     | `SyncPage[CsatTool] (AsyncPage[CsatTool] on the async client)` | List CSAT tool definitions with page/limit pagination; the returned page is async-iterable. |
| `create(body, options=None)`          | `CsatTool`                                                     | Create a new CSAT tool definition (idempotent POST).                                        |
| `get(tool_id, options=None)`          | `CsatTool`                                                     | Retrieve a single CSAT tool by id via the singular /v1/csat-tool/{id} route.                |
| `update(tool_id, body, options=None)` | `CsatTool`                                                     | Partially update an existing CSAT tool definition (PATCH).                                  |
| `delete(tool_id, options=None)`       | `DeletedResult`                                                | Delete a CSAT tool definition by id.                                                        |

### ces\_tools

Manage CES tool (survey) definitions under /v1/ces-tools.

| Method                                | Returns                                                      | Description                                                                                |
| ------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| `list(params=None, options=None)`     | `SyncPage[CesTool] (AsyncPage[CesTool] on the async client)` | List CES tool definitions with page/limit pagination; the returned page is async-iterable. |
| `create(body, options=None)`          | `CesTool`                                                    | Create a new CES tool definition (idempotent POST).                                        |
| `get(tool_id, options=None)`          | `CesTool`                                                    | Retrieve a single CES tool by id via the singular /v1/ces-tool/{id} route.                 |
| `update(tool_id, body, options=None)` | `CesTool`                                                    | Partially update an existing CES tool definition (PATCH).                                  |
| `delete(tool_id, options=None)`       | `DeletedResult`                                              | Delete a CES tool definition by id.                                                        |

### wokus

Manages wokus (feedback collection tools) under /v1/wokus, including reviews, settings, folder moves, invitations, and sharing.

| Method                                             | Returns                                                                        | Description                                                                       |
| -------------------------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
| `list(params=None, options=None)`                  | `SyncPage[Woku] (async AsyncPage[Woku]); pages are async-iterable`             | Lists the company's wokus with page/limit pagination.                             |
| `create(body, options=None)`                       | `Woku`                                                                         | Creates a new woku (idempotent POST).                                             |
| `get(woku_id, options=None)`                       | `Woku`                                                                         | Gets one woku with its aggregated review stats.                                   |
| `update(woku_id, body, options=None)`              | `Woku`                                                                         | Partially updates a woku's fields.                                                |
| `delete(woku_id, options=None)`                    | `DeletedResult`                                                                | Deletes a woku.                                                                   |
| `update_settings(woku_id, body, options=None)`     | `Woku`                                                                         | Applies the woku's boolean settings idempotently (closed, reviewsDisabled, etc.). |
| `move(woku_id, body, options=None)`                | `Woku`                                                                         | Moves the woku into a folder, or to the root with folderId null.                  |
| `list_reviews(woku_id, params=None, options=None)` | `SyncPage[WokuRecord] (async AsyncPage[WokuRecord]); pages are async-iterable` | Lists a woku's reviews with page/limit pagination.                                |
| `send_invitations(woku_id, body, options=None)`    | `InvitationsResult`                                                            | Sends a woku review invitation by email or WhatsApp (idempotent).                 |
| `share(woku_id, body, options=None)`               | `WokuRecord`                                                                   | Shares a woku review link by email.                                               |

## Resources

`trackers`, `nps_tools` / `csat_tools` / `ces_tools`, `nps` / `csat` / `ces`,
`wokus`, `forms`, `flows`, `action_plans`, `action_plan_groups`, `tickets`,
`ticket_destinations`, `dispatches`, `reports`, `company`, `quarantines`.

## Versioning

The SDK follows **semantic versioning** (`MAJOR.MINOR.PATCH`). The current
published version is **`0.1.0`**. We recommend pinning a compatible range (for
example `>=0.1,<0.2`) and reviewing the changelog before a MAJOR bump. Versions
and their notes are on [PyPI](https://pypi.org/project/woku/) and the
[GitHub releases](https://github.com/wokuApp/woku-python/releases).

## Resources

* **PyPI package:** [woku](https://pypi.org/project/woku/)
* **Code and examples:** [github.com/wokuApp/woku-python](https://github.com/wokuApp/woku-python)
* **Equivalent JavaScript SDK:** [JavaScript SDK](/docs/en/development/sdk-javascript)
* **API reference:** [API Integration Guide](/docs/en/development/api)
