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

# Account lifecycle

> Every state a Verified Account can be in, what moves it between states, who can move it, and what your platform receives at each step.

A Verified Account has two states on your platform: **linked** and **not linked**. Only the person can link an account. The person, your organization and your server can each unlink it. This page lists every transition and what you receive for it.

```mermaid theme={null}
stateDiagram-v2
    [*] --> NotLinked
    NotLinked --> Linked: The person links with a passkey
    Linked --> NotLinked: The person unlinks in the BotShield app
    Linked --> NotLinked: You revoke (Console or server API)
    Linked --> NotLinked: The person deletes their BotShield ID
    NotLinked --> Linked: The person links again
```

## The states

| State | `GET /secured-accounts/status` | Token claim `trusted` | Widget | Registry status |
| - | - | - | - | - |
| **Not linked** (never linked) | `none` | `false` | The **Link with BotShield ID** chip, when the offer is on | No row |
| **Linked** | `fresh` or `stale` | `true`, with `trusted_since` | **Account secured** | **Linked** |
| **Not linked** (unlinked) | `none` | `false` | The chip again | **Unlinked · by you** or **Unlinked · by human** |

The state belongs to one environment. A Development link never counts as linked, or as a conflict, in Production.

## Link

The person links the account. Nothing else can.

1. Your page shows the offer to a person you have just signed in, with your stable user ID in `platform-user-ref`. See [Quickstart](https://docs.botshield.ai/verified-accounts/quickstart.md).
2. The person taps **Link with BotShield ID**. BotShield names your platform, and the person confirms with their passkey in the BotShield app. The passkey prompt never appears on your page.
3. The account is linked.

You receive:

| Where | What |
| - | - |
| Your page | `botshield:success` with `trusted: true` |
| Your server, on `POST /sdk/verify-token` | `trusted: true`, `trusted_since`, `last_pass_at` |
| Your webhook endpoint | `account.secured` with the `handle`, and `gate.human_verified` with `trusted: true`, `first_time: true` and the same `handle` |

Store the `handle` beside your user. Match it to your user through the `request_id` on `gate.human_verified`, which is the request your page started.

### When a link is refused

An active link is never overwritten. On your platform one BotShield ID links one account, and one account is linked to one BotShield ID.

| Situation | Refusal | What the person does |
| - | - | - |
| This BotShield ID is already linked to a **different** account on your platform | `already_trusted` (`account_already_secured_here`) | Unlinks the other account in the BotShield app first. |
| This account is already linked to a **different** BotShield ID | `rebind_requires_prior_id` (`account_held_by_another_id`) | That BotShield ID unlinks first, or you revoke the link. |
| The person returns to the account they already linked | Not a refusal | Nothing. The widget shows **Account secured**. |

A refused link arrives as `botshield:failure` on the page and `gate.unavailable` on your webhook. See [Token and webhooks](https://docs.botshield.ai/verified-accounts/token-and-webhooks.md).

## Check the status

Ask at any time, without a tap:

* **In the page:** the widget fires `botshield:presence` with `{ state, fresh_until }` on every change.
* **On your server:** `GET /secured-accounts/status` with your API key returns the same answer. See [Server API](https://docs.botshield.ai/verified-accounts/server-api.md#status).
* **On every pass:** the token's `trusted` claim is the signed answer at that moment.

## Unlink

A link ends in one of four ways. Each sends `account.unlinked` with the account's `handle`.

| Who | How | `by` | `reason` |
| - | - | - | - |
| The person | **Unlink** in the BotShield app, under **Accounts** | `human` | `human` |
| The person | **Not you? Undo**, right after a first link | `human` | `human` |
| The person | Deletes their BotShield ID | `human` | `human` |
| Your organization | **Revoke** in the Console registry | `platform` | `platform` |
| Your server | `POST /secured-accounts/revoke` | `platform` | `platform` or `credential_changed` |

Whoever ends it, BotShield also removes your company's agents for that person in the same environment, moves open widgets for the account to `none`, and tells the person in the app.

### Unlink when a sign-in detail changes

The link says a live human runs **this** account. When the account's email, password, phone number or MFA changes, that statement has to be made again. Call:

```bash theme={null}
curl -s https://api.botshield.ai/operations/secured-accounts/revoke \
  -H "Authorization: Bearer $BOTSHIELD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"platform_user_ref": "usr_8f31c2d9", "reason": "credential_changed"}'
```

The person's app shows the account as unlinked because a sign-in detail changed. The person links again in one tap behind your own re-authentication.

## Link again

An unlinked account is an ordinary account. The person links it again from your page, exactly as the first time. The handle is the same when the same person links it again, so your stored handle still matches. The new link has a new linked date, and you receive `account.secured` and `gate.human_verified` with `first_time: true` again.

A person cannot link an account from the app alone. The request always starts on your page, where your sign-in shows which account it is.

## Your responsibilities

| When | Do this |
| - | - |
| Before the offer | Sign the person in or re-authenticate them. Send your stable user ID as `platform-user-ref`, never an email address. |
| On `botshield:success` | Send the token to your server and verify it. Read `trusted` there, not in the browser. |
| On `account.secured` | Store the `handle` beside your user. |
| On `account.unlinked` | Find the user by `handle` and mark the account not linked. |
| When a sign-in detail changes | `POST /secured-accounts/revoke` with `reason: "credential_changed"`. |
| When the account closes or changes hands | `POST /secured-accounts/revoke` with `reason: "platform"`. |
| If you missed an event | Reconcile with `GET /secured-accounts/list` or `GET /secured-accounts/status`. |

## Next steps

> [card] [Server API](https://docs.botshield.ai/verified-accounts/server-api.md)
>
> Status, list and revoke with your API key.

> [card] [Staying in sync](https://docs.botshield.ai/verified-accounts/staying-in-sync.md)
>
> The live widget, webhooks and the token together.

> [card] [Token and webhooks](https://docs.botshield.ai/verified-accounts/token-and-webhooks.md)
>
> Every claim and event field.

> [card] [Managing accounts](https://docs.botshield.ai/verified-accounts/manage-accounts.md)
>
> The Console registry and what the person controls.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.