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. 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 firstx-forwarded-forhop).
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:
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:
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 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, Data Security, and 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.
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.
Related: Usage, quotas & webhooks for the session record, Backends for the mint contract, and Network requirements for where the voice connection runs.