Skip to main content

Guests and bots in identity

Applies to: identity after 0.35 (creating worker bots — after 0.40, messaging after 0.155), the coordinator and messaging in platform_token: identity mode · Checked: 01.10.2026

Everything here works once the platform is switched to identity's token (platform_token: identity, ADR-0047 §8a). In hmac mode — the default — guests and bots live on the coordinator as described in Guests, contractors and family members, and nothing on this page changes.

In identity mode a guest and a bot are identity accounts like employees, and each has a person who answers for it: a guest its sponsor, a bot or an assistant its owner. They sign in through the same issuer, and their token is revoked by the same revocation journal.

Guests​

The invitation​

The apps still invite through the coordinator — POST /v1/guests, with the guests:invite permission checked by the coordinator. In identity mode it does not create the guest itself: it passes the invitation on to identity, naming the caller as the sponsor. identity's answer (the refusal's code and text) reaches the app unchanged.

identity keeps the invitation's rules now — identity console → Guests:

SettingDefaultMeaning
Longest invitation365 daysa longer term is cut to it
Invitation when none is given30 daysthere is no invitation without an expiry
Active guests per sponsor0 — no limitat the limit a new invitation gets 409

The coordinator's guest-max-days setting does not apply in identity mode: these settings took its place.

The invitation carries the guest's features (see Access to SimpleTwo): chat and calls only by default. Such a guest is inside the access model like an employee; guests invited earlier on the coordinator stay outside it and reach every service as before.

Check: identity console → Guests shows the guest's sponsor, expiry and the sign-in state "card pending".

The card and the first sign-in​

The guest gets a single-use card code, not a password: 72 hours, never longer than the invitation. The app redeems it through the coordinator's POST /v1/auth/enroll/confirm, which passes it on to identity; the guest chooses the password under identity's password policy. A new card is issued only while the guest has never signed in.

The recovery code​

As before, the sponsor gets the guest back in: in the app, My guests → Help sign in issues a six-digit code that the sponsor reads out. identity issues it and keeps only its keyed hash. The rules are the same: 10 minutes, once, five wrong tries per record — issuing again adds no tries, and once they are spent a new code is issued only after the old one expires. The guest redeems it with their phone through POST /v1/auth/recover/confirm; the new password ends every earlier session.

The guest's "I cannot sign in" (POST /v1/auth/recover) stays on the coordinator: it marks the request on the sponsor's list and writes to them from the support bot, but mints no code of its own any more.

An administrator issues a code for any guest in the coordinator's console (as before) or in the identity console → Guests → Recovery code.

Extend, revoke, review​

  • Extend — +30 days from the current expiry (or from today when it has passed), never past the longest invitation.
  • Revoke — reversible: the account is disabled and all its sessions in identity end.
  • Access review — the "Review guests older than" policy in the identity console → Access reviews: the sponsor answers keep or revoke, and silence revokes.

A guest who lapsed, was revoked or failed a review goes onto the revocation journal as an account event: every service refuses its token at once, without waiting for it to expire.

The guest's token​

A guest signs in on identity's page like everyone and gets a platform token marked knd: guest. The guest's fences are the ones of today: the directory per the role's policy, only the conversations they were invited to, no mailbox. A guest session the coordinator minted is not accepted in identity mode — the guest signs in again.

Bots and assistants​

The owner​

Every bot and assistant has an owner — the employee who answers for it. identity console → Bots lists every bot, its owner and its credentials.

  • The owner left (disabled, deleted, expired) — their bots and assistants are disabled with them, each with an account event on the revocation journal. The organisation's system bots (support, announcements) are not disabled: the list marks their owner as inactive.
  • Hand over — give the bot a new owner (an active employee) and enable it at once; Disable — leave it off.
  • Owner review — the "Review bots with their owner every, days" policy in the identity console → Access reviews; silence disables the bot.

A new worker bot​

In identity mode worker bots are created in the identity console → Bots → New worker bot. The coordinator's console no longer creates them or rotates their token: in place of New bot it links to identity's console. In hmac mode it is the other way round — bots are created on the coordinator as before, and identity has no such button.

The form's fields:

FieldMeaning
Username2–64 characters: a lowercase letter, then letters, digits, ., _, -; a taken name (a deleted account's included) is refused
Display namethe name in the directory and in conversations
Ownerthe username of an active employee, who answers for the bot
Scopesat least one of read, send, react, manage-conv; the empty set (the old "full access") is not granted by the console
Conversationsconversation ids, comma-separated; empty — every conversation the bot is added to
Webhook and its secretoptional: where messaging posts events and what signs each delivery
Credentiala key pair made by identity (the private half shown once), the bot's own public key, or a client secret (shown once)

What creating does:

  1. identity asks the coordinator to create the bot's record in messaging — scopes, conversations, webhook, credential: platform (no bot_… token). identity has no way to messaging's control API: it listens on loopback on the messaging host, and its one client is the coordinator through the host's agent. So identity calls the coordinator's door /v1/service/bots as svc:identity.
  2. identity creates the bot's account (kind service, the owner from the form) and issues the credential. If the account cannot be made, the record in messaging is removed again.
  3. The coordinator takes the account from identity's feed within a minute; the bot appears in the directory and on its Bots card.

Check: in the identity console → Bots the bot shows its owner, its key or secret and the May do column — the scopes and conversations of messaging's record. A "?" in that column means messaging's records could not be read through the coordinator; the reason is written above the table.

Edit, disable, delete​

The row's buttons keep messaging's record in step with identity:

  • Edit — scopes, conversations (an empty field lifts the fence), webhook, name. The credential is not touched; the bot's open connections are dropped so the new grants apply at once.
  • Disable — identity's account first (the bot cannot sign in from that moment), then messaging's record. If messaging is unreachable the console says so; pressing again repeats only the second half.
  • Enable, and Hand over with enabling — messaging's record first; if it was not resumed, the bot stays disabled.
  • Delete — messaging's record first, then the account, its credentials and every token. If messaging is unreachable the account is kept. The username stays taken after deletion. The system bots (support, welcome) are removed with what made them, on the coordinator.

When the owner leaves, their bots are disabled on identity and their records in messaging follow.

A worker bot: client_credentials​

A worker bot (support, announcements, an integration) gets its platform token from identity:

  • client_id — bot: followed by the bot's username, e.g. bot:announcer;
  • a key (preferred, private_key_jwt) — New key in the console makes a pair and shows the private half once; a bot can also bring its own public half (POST /control/bots/{username}/keys with public). Rotate key replaces all earlier ones;
  • a secret — New secret, shown once, replaces the previous one.

A request with a key carries a signed assertion (the suite's algorithm, EdDSA by default; iss and sub are the client_id, aud the /oauth/token address, exp at most five minutes ahead, a single-use jti):

curl -fsS -X POST "https://id.<domain>/oauth/token" \
-d grant_type=client_credentials \
-d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
-d client_assertion="$ASSERTION"

With a secret:

curl -fsS -X POST "https://id.<domain>/oauth/token" -u "bot:announcer:$SECRET" \
-d grant_type=client_credentials

The answer is a platform token for an hour: sub is the bot, knd: bot, every token is its own session, and there is no refresh token — the bot asks again.

An assistant: exchanging the owner's token​

An assistant's brain runs on its owner's device, so the assistant has no credentials of its own. The owner's app exchanges the owner's token (RFC 8693):

curl -fsS -X POST "https://id.<domain>/oauth/token" \
-d grant_type=urn:ietf:params:oauth:grant-type:token-exchange \
-d client_id=simpletwo \
-d subject_token="$OWNER_TOKEN" \
-d subject_token_type=urn:ietf:params:oauth:token-type:access_token \
-d requested_subject=ivan.assistant

The assistant must belong to that person. The answer: sub is the assistant, knd: bot, act: {sub: the owner}, the owner's session (signing out ends this token too), services and lifetime never wider than the owner's token. A token with act cannot be exchanged again: an assistant does not act for an assistant.

An assistant's rights are an intersection: a service checks the permission for both the assistant and the owner in the coordinator's RBAC. The owner signing out everywhere, or being disabled or deleted, revokes the assistant's tokens too. What the assistant does — draft or full, in which conversations, the model quota — is still messaging's.

"My agents" (POST /v1/me/agents) in identity mode creates the assistant's account on identity (owned by whoever pressed the button) and its behaviour in messaging with no token of its own; the answer carries token_exchange — the address and parameters of the exchange — instead of token.

Moving existing bots​

In identity mode messaging does not accept the old bot_… tokens — only the platform token. So before the switch:

  1. Check that every bot has an account on identity: identity console → Bots (the coordinator writes bots there since identity exists). Give a bot without an owner one with Hand over.
  2. For each worker bot issue a key (or a secret) and put it into the bot's environment together with its client_id and the /oauth/token address.
  3. Move the bot to getting its token from identity; until the switch services do not accept that token, so it is enough to check that identity issues it.
  4. Assistants need nothing: the owner's updated app gets their token by the exchange.
  5. Switch the platform to identity. The bots' records in messaging — scopes, permissions, conversations — stay as they are; the old bot_… token can be removed by creating the bot again with credential: platform (the way new assistants are created).

Common refusals​

SymptomLikely cause
404 on /control/guests… at identityidentity's issuer is off: guests are still the coordinator's
409 on an invitation: a sponsor has as many guests as allowedthe guest policy's limit is reached
400 a guest is invited by an active personthe sponsor is not an active employee (a guest, a bot, disabled)
429 issuing a recovery codethe five tries are spent; wait ten minutes from the previous code
401 invalid_client for a bota wrong key, secret, or a client_id without bot:; the assertion was already used
400 invalid_grant for a botthe bot's account is disabled (for instance, its owner left)
400 invalid_grant: no assistant of yours by that namethe assistant belongs to somebody else
messaging answers invalid_token to bot_…in identity mode the old bot tokens are not accepted
409 hmac_mode on "New worker bot"the coordinator's platform token is still hmac: the bot is created in the coordinator's console
409 managed_on_identity on New bot in the coordinator's consoleidentity mode: the bot is created in identity's console
409 name_takenthe name is held by an account on the coordinator or by an orphan record in messaging (the coordinator's Bots card lists it)
503 no_relayidentity has no DIR_COORDINATOR_URL, so no way to messaging's records
502 messaging_unreachablethe coordinator could not reach messaging through the host's agent