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

# End-user identity

> How Cobalt identifies the people who talk to your agents, across every channel.

# End-user identity

End users never sign in to the console. They arrive through a channel — Slack,
Teams, a web widget, email — and Cobalt maps that inbound identity to a single
**canonical person** so the agent knows who it's talking to, applies the right
policy, and (when allowed) acts on their behalf.

## The model

* **Canonical user** — one stable record per real person in your tenant. All
  history, memory, and authorization decisions hang off it.
* **Channel identity** — a typed identifier that links a canonical user to one
  channel, e.g. `slack:T01234/U987`, `teams:<tenant>/<aad-object-id>`,
  `email:alice@acme.com`, `web:<session>`. A person accumulates several.

The agent always reasons about the canonical user; channel identities are just the
doorways.

## How a person gets resolved

Cobalt resolves identity in a strict order — higher sources win:

1. **Directory provisioning (SCIM)** — if you've set up [SCIM](/identity/scim),
   people already exist with verified emails. Most channel identities then resolve
   automatically by email. This is the happy path — no rules, no review.
2. **Verified signals** — a cryptographic proof of ownership (e.g. an OAuth flow
   that returns a verified email) links a channel identity with no admin
   involvement, even without SCIM.
3. **Identity rules** — admin-defined, deterministic rules (match a field →
   resolve to a person, with an optional transform) for environments where SCIM
   and verified signals don't cover everyone. Evaluated in order, first match
   wins. Most fully-SCIM'd tenants never need these.
4. **Manual linking** — an admin links a provisional identity to a person from the
   review queue. The fallback for edge cases.

When a new identity matches nothing, Cobalt creates a **provisional** record and
the conversation still proceeds; it resolves later when a verified signal arrives,
a rule matches, or an admin links it.

### The review queue

Anything the automatic paths can't settle confidently lands in the **review
queue** (**Settings → Identity**) for an admin. The queue is organized by what
happened:

* **Pending Confirmation** — a rule matched with **low confidence**. Nothing was
  linked; the item waits for you. **Approve** links the identity to the
  suggested person, **Reject** leaves it provisional.
* **Medium Review** — a rule matched with **medium confidence**. The identity
  **was linked immediately** so the conversation wasn't blocked; the item is a
  post-hoc check. Reject it if the link looks wrong, then fix it from the
  person's page.
* **Conflicts** — a rule matched **multiple people** (ambiguous), or an email
  was re-claimed after being released. There's often no single suggestion here —
  use **Assign** to pick the right person explicitly.
* The **All** tab additionally shows rarer items, such as a blocked OAuth merge
  or a user who exhausted their phone-verification attempts.

Each row shows the channel, a confidence badge, and the suggested person (when
there is one). You can filter by channel, confidence, and age (last 24 hours /
7 days / 30 days), and select multiple rows for a **bulk** approve, reject, or
assign — Cobalt previews the affected count and a sample before anything is
applied, and every action (single or bulk) is written to the audit log.

Safeguards that protect you here:

* **Email claim/release** — once an email is linked to a person it's reserved for
  them; it can't silently jump to a different person without an admin (or an
  authoritative SCIM event) releasing it first.
* **Phone OTP** — phone numbers get reassigned by carriers, so phone-based linking
  always requires a one-time-code confirmation, and re-verifies periodically.

### The person record

Every canonical person has a page (open them from the queue, a conversation, or
the directory) with two tabs:

* **Identities** — every channel identity linked to them. From here you can
  **link** a provisional identity manually (email links go through the claim
  guard; phone always requires the OTP flow), **unlink** one (it reverts to
  provisional — nothing is deleted), or **release** an email address so a
  different person can claim it.
* **Authorized systems** — the per-user integration authorizations they've
  granted (each connection, its scopes, when it was authorized and when it
  expires). **Revoke** one and the next time the agent needs that system for
  them, they're simply asked to authorize again.

### Deprovisioning a person

When someone leaves, **Deprovision** on their page retires them in one step:
Cobalt previews exactly what will happen — how many channel identities will be
deactivated, integration authorizations revoked, and email addresses released —
and asks you to type a confirmation before proceeding. The cascade is
all-or-nothing: if any step fails, no changes are applied and you can retry.
Revocations at the external vendors complete asynchronously; the page shows a
notice until they're done. (SCIM-managed tenants rarely need this — an IdP
deactivation does the same thing automatically.)

## How each channel identifies users

| Channel        | How the user is identified                                                         | Second sign-in?                                     |
| -------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------- |
| **Slack**      | The Slack workspace user; Cobalt resolves their email from the workspace directory | No — they're already in Slack                       |
| **Teams**      | The Microsoft Entra user; Cobalt resolves email/UPN via Microsoft Graph            | No — they're already in Teams                       |
| **Email**      | The `From:` address                                                                | No                                                  |
| **Web widget** | Anonymous, an app-signed token, or an IdP token — see below                        | Not if you embed it in an already-authenticated app |

Slack and Teams identities are resolved by **directory lookup** and are
**attribution-grade** (good enough to stamp who did something), but not
high-trust enough to act *as* the user — see [trust tiers](#trust-tiers).

### Web widget — no second sign-in

When you embed the widget in an app the user is **already** signed in to, don't
make them log in again. Pick the identity mode that fits the host:

| Mode                      | How identity arrives                                                                                                        | Use when                                                                                                                                                    |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Anonymous**             | None (a per-browser session)                                                                                                | Public or low-sensitivity help                                                                                                                              |
| **Embedded token**        | Your app's **server** mints a short-lived signed token (HS256, your tenant as issuer, ≤1h) and hands it to the widget       | Your app already authenticates the user and can sign a token server-side — e.g. an intranet portal, or a ServiceNow app                                     |
| **Federated (IdP token)** | The host obtains an IdP token for the signed-in user **silently** and passes it; Cobalt verifies it against your IdP's keys | The host can't safely hold a signing secret but can get an IdP token client-side — e.g. a **SharePoint / SPFx** web part using the user's Microsoft sign-in |
| **Hosted sign-in**        | Interactive sign-in on Cobalt's standalone full-page chat surface                                                           | The standalone hosted page, where there's no host app session to reuse                                                                                      |

Reach for **Embedded token** or **Federated** for embedded widgets — both are
silent. Interactive **Hosted sign-in** is only for the standalone hosted page.
Configure the mode on the channel — see [Web widget](/channels/web-widget).

## Trust tiers

When a connection acts on an external system, it declares how the user's identity
is used:

* **Service account** — all users share one identity; the end user's identity
  isn't forwarded. Same result for everyone.
* **Attribution** — the agent uses a tenant credential but stamps the resolved
  user on the record (e.g. ServiceNow `caller_id`). Any **verified** identity
  qualifies.
* **Trusted impersonation** — the agent acts *as* the user. Because a wrong
  identity here means unauthorized access, only the **strongest** verification
  methods qualify: **OAuth, SCIM, and federated IdP tokens**. Notably, an
  **embedded token** or a **Slack/Teams directory lookup** is attribution-grade
  only — it can stamp identity but cannot impersonate.

This check is enforced by the platform on every invocation, not left to the agent.

## Related

* Populate the directory: [SCIM provisioning](/identity/scim).
* Identify widget users: [Web widget](/channels/web-widget).
* Per-channel setup: [Channels](/channels/overview).
