# Regions

> Where your users' voice sessions run: the account home region, the three modes (Best effort, Region affinity, Edge), what to send from your token route, and what Region affinity does and doesn't guarantee.

Every voice session runs in a **region** — a part of the world where the voice connection, the agent, and speech recognition and synthesis run. There are two today: **Europe** and **Americas**. Asia-Pacific is planned and not live yet.

Each account has a **home region**, chosen at signup and changeable any time under [Settings → Account](/settings/account). Each coworker then has a **Regions** setting on its Configure tab that decides how its sessions are placed. This page covers the three modes, what to send from your token route so a session can run near the user, how those signals are used, and what each mode does and doesn’t guarantee.

## The three modes

| Mode | Plans | What it does |
| --- | --- | --- |
| Best effort | Free and up | Runs in your home region. If that region is unavailable or full, the session runs in the nearest other region instead. No residency guarantee. |
| Region affinity | Growth and up | Runs only in the region you pin. If nothing is available there, the session refuses to start rather than move. Pick this when you have a residency requirement. |
| Edge | Growth and up | Runs in the region nearest the user, limited to the regions you allow (the fence). |

Best effort runs everything in your home region and moves to the other region only when your home region is unavailable. Region affinity never moves. Edge is the only mode that places a session by where the user is — and only within the regions you allow.

## What to send from your token route

So a session can run near the user, your token route forwards three optional facts about the user inside the mint’s `user` object:

- `user.timeZone` — the user’s time zone.
- `user.languageCode` — the user’s language.
- `user.ip` — your request’s client IP (the first `x-forwarded-for` hop).

The browser SDK stamps the time zone and language into its `getToken` context and posts them to your route; you forward them into the mint. Our Next.js helper `coworkkitSessionRoute` does all of this by default, including reading the client IP:

**`app/api/session/route.ts`**

```ts
import { coworkkitSessionRoute } from "@coworkkit/server/next";

export const POST = coworkkitSessionRoute({
  getUserId: async () => (await auth()).userId, // your auth, server-side
  // forwardClientIp: false,  // opt out of sending the client IP
});
```

Set `forwardClientIp: false` to opt out of the IP. On any other backend, forward the fields by hand:

```ts
import { mintSession } from "@coworkkit/server";

const session = await mintSession(process.env.COWORKKIT_API_KEY!, {
  user: {
    id: userId,                                          // from your auth, server-side
    timeZone,                                            // posted by the browser SDK
    languageCode,                                        // posted by the browser SDK
    ip: req.headers.get("x-forwarded-for")?.split(",")[0]?.trim(),
  },
  session: { region: "europe" },                         // optional — see below
});
```

Optionally, `session.region` (`"europe" | "americas" | "apac"`) states which region you want this session in. It is honoured within your Regions setting and the regions you allow, never above them, and is echoed back in the response only when it was honoured.

One note if you are upgrading an older integration: the retired top-level `userIp` and `userId` fields are gone — sending either returns `400 retired_field`. Use `user.ip` and `user.id` instead. See [Backends](/docs/backends) for the full mint contract.

## How the signals are used

Only **Edge** uses the user’s location; Best effort and Region affinity ignore it. When Edge places a session, the IP wins over the time zone. If neither is present or can’t be resolved, the session runs from your home region.

Both signals are used only to choose the region for that one session, and are **never stored or logged**. The IP is resolved to a country using a database inside our own service — there is no external lookup.

## Opting out

Nothing has to be forced. If you don’t send `user.ip` and `user.timeZone`, that user’s session simply runs from your home region. Send them only for users who haven’t declined — there is no flag to tell us; leaving the fields out is the opt-out.

If you mention this in your own privacy notice, one sentence you can adapt:

*“When you start a voice session, this app sends your approximate location (IP address and time zone) to our voice provider so the session connects to the nearest region. It is used only for that and is not stored.”*

## What Region affinity guarantees — and what it doesn’t

With Region affinity, the voice connection, the agent, and speech recognition and synthesis all stay in the region you pinned. Two things do not: the language model runs in a global region, and your account, usage and session records are kept in our control plane in the United States.

So Region affinity keeps the live session in one region, but it is **not** full data residency. For the formal statements, see our [Privacy Policy](/legal/privacy), [Data Security](/legal/data-security), and [Terms](/legal/terms).

## Seeing where a session ran

The **Sessions** tab shows the region each session ran in. The same value is on the sessions API and the `session.ended` webhook as a `region` field, so your backend can record it too — see [Usage, quotas & webhooks](/docs/usage).

## Errors

Two refusals relate to regions:

- `no_cell_in_region` — Region affinity is on and the pinned region is unavailable or full right now, so the session refuses to start rather than move.
- `transport_unassigned` — the coworker isn’t provisioned yet.

Both are listed with the message the button shows for each in [Error codes](/docs/error-codes).

Related: [Usage, quotas & webhooks](/docs/usage) for the session record, [Backends](/docs/backends) for the mint contract, and [Network requirements](/docs/network-requirements) for where the voice connection runs.
