All docs

HR embed example

- Create/pick the embed agent in Cadau, agree on app_id

Source docs/en/site/sdk-host-hr-guide.md

What this document is

A self-contained conversion handbook for a third-party business system (ERP, OA, industry SaaS, and similar) connecting Cadau’s website embed assistant.

Readers are host-system developers and admins: reading this page and finishing the checklists is enough to complete “register agents → assign by user → mount the conversation assistant on the page → integrate and accept”, without first reading all Cadau site docs or the entire HR example source.

>

Full third-party handbook: [README.md](/docs/sdk-host-embed) (overview). This page is the HR example conversion walkthrough.

- HR example today: path B only (MINDLINK_* in server/.env + embed-session); it no longer uses a static token in web/.env.local. This phase: the whole host shares one Cadau workspace (several HR tenants also register into that same workspace). “One workspace per tenant” is Host agent run and data scope §0.3 — not built yet.

- Cadau product details (full widget params, SSE protocol, error codes): Website integration, Embed contract (V1.5.12).

- Runnable reference: Cadau repo examples/hr-multi-tenant (HR multi-tenant example); below, “HR example”. Paths and table names follow that example; map them to your own names.


0. Quick answers (for decision-makers)

QuestionAnswer
Do I fill an embed token when registering an agent?No. Register only the Cadau agent ID; the token comes from static config (A) or backend minting (B).
Do I generate a token in Cadau for every agent?No. Path B uses one integration account; assignment swaps tokens at runtime.
Which way for production?B (backend minting); A is local testing or an emergency fallback.
Does Cadau store our users?No. Users and permissions stay on the host; Cadau provides agents and conversation. Path B uses host_actor to mark the signed-in user; history / live support / tickets are isolated by that person. Host data queries: Host agent run and data scope §0.
Will two people on the same computer leak conversations?No (path B must pass the signed-in user into the widget). User A cannot see B’s history, live support, or tickets.
Can we embed without changing the business backend?Path A yes (token never hits the backend); path B no (you must have a host BFF mint API).
Do we build live support / tickets on the host?No. Same widget as a static site; turn live support on in Cadau.
The host used to call a model directly — now what?Ask a chosen agent to answer (server call; the user need not see the widget). See Host LLM service.
Can several host customers share one Cadau workspace?Yes, and this phase only does that. People are isolated by the signed-in user; the support desk is still one copy per workspace. “One workspace per tenant” can be finished later; see Host agent run and data scope §0.3.

1. What you will have finished after this page

1.1 Admins

  • Create/pick the embed agent in Cadau, agree on app_id
  • In the host admin UI, register agents that can be connected (name + agent ID)
  • For signed-in users (not business-file ids such as employee id), assign an agent and operation permissions (query / create / edit / delete)
  • Configure the host server Cadau integration account (path B; one set is enough)
  • Confirm the assistant works against the §12 acceptance checklist

1.2 Developers

  • Add a registration table + user assignment table (or equivalent modules) in the host DB
  • Implement an embed-session API (for example GET .../me/embed-session) that internally calls Cadau embed-token
  • On frontend business pages, load the widget script and init; listen for in-reply navigation actions (optional)
  • (Optional) Turn on live support for that agent in Cadau (seats in this workspace or an authorized support team) — the widget shows “Live support / Submit a ticket” by itself; do not build a second support desk on the host
  • (Optional) Implement a task-style API: after the agent is configured in Cadau, point at it from .env or host settings; the server answers, the page previews, then writes to the DB (example: suggest jobs by department)
  • Configure network (same-origin proxy or CORS) and secrets (integration password server-only)
  • Finish integration and go-live on path A or B

2. Architecture: who owns what

flowchart LR
  subgraph Host["Third-party host system"]
    U["Signed-in user"]
    ADM["Admin: register + assign"]
    BFF["Host BFF: mint via embed-session"]
    UI["Business frontend + embed widget"]
    U --> UI
    ADM --> DB[(register and assign)]
    BFF --> DB
    UI --> BFF
  end

  subgraph ML["Cadau"]
    UA["User agents"]
    CHAT["Conversation / knowledge"]
    ET["embed-token API"]
    UA --> CHAT
    ET --> UA
  end

  BFF -->|"integration account calls"| ET
  UI -->|"embed token conversation"| CHAT
PartyOwnsDoes not own
CadauAgents, conversation, knowledge index, embed-token issue, embed widget scriptHost business user ids, host permissions, host business APIs
Host (you)Signed-in users, registering several Cadau agents, assigning by user, minting short-lived tokens, page mount and navigationMaintaining a “register by host user” API inside Cadau

Relation to host HTTP API keys: API keys govern machine calls to host business APIs; embed governs which agent the in-page conversation uses. Configure them independently.


3. Concept map: your system ↔ HR example

Treat the HR example as a walkthrough. Map concepts like this:

General concept (your system)Name in the HR exampleNotes
Host signed-in userhr_users (phone)Subject of agent assignment; not the employee file id
Tenant / org / bookstenants (workspace)Register and assign are isolated by tenant
Registered connected agenthr_mindlink_agentsStores display name, Cadau agent ID, app_id; conversation assistants assigned by role. Generate/analyze is a separate agent in .env or “embed assistant registry”
User–agent assignmenthr_user_agent_assignmentsSigned-in user → registration row + operation permissions
Embed-session BFFGET .../me/embed-sessionRead assignment → mint → return fields the widget needs
Static testing configVITE_MINDLINK_* in web/.env.localOptional path A for other hosts; HR example has dropped this, B only
Server Cadau connectionMINDLINK_* in server/.envPath B integration account and URLs

4. Two paths: A and B

4.1 Compare

DimensionA: static testingB: backend minting (recommended for production)
Token sourcePre-written in config/envHost BFF mints now when the user opens the page
Goes through host backend?No (browser talks to Cadau)Yes
Different agent per person?Usually one config for everyoneYes
Click “generate token” in Cadau UI?Yes (or a script)No
Host must build a BFF?NoYes (at least embed-session)

4.2 How to choose

Need a different agent per signed-in user?
  ├─ Yes → B
  └─ No → testing / demo only → A first is fine; still prefer B for go-live

Can the token appear in frontend build output or a public repo?
  ├─ No → B (or A in dev only)
  └─ Yes (intranet dev only) → A is acceptable

Does the host already have sign-in and permissions?
  ├─ Yes → B; hang assignment on your user id
  └─ No → prove the widget with A; then add sign-in + B

4.3 What “minting” means

Minting = the host server uses a Cadau integration account to call POST /api/v1/user-agents/{agentId}/embed-token and get a short-lived embed token, then issues it to the browser. The user does not sign in to the Cadau main site; the browser does not store a Cadau password.

4.4 HR example vs A + B strategy

HR example (this repo): frontend is path B only — after sign-in it requests embed-session; if unassigned, it does not mount the assistant; it no longer falls back to A or reads web/.env.local.

Other third-party hosts (you may borrow):

  1. User unassigned → skip embed-session, or try A (dev only)
  2. User assigned → request B
  3. B succeeds → use B; B fails → fall back to A (dev only recommended)

Production: assign everyone + B only, drop A’s long-lived token.


5. Cadau-side prep (admin)

Before you change the host, finish this in Cadau:

StepActionOutput
1Create or pick a user agent (bind knowledge documents)Agent ID (UUID)
2Agree on app_id (distinguishes the integrator, for example your-corp-hr)Same as host registration and mint request
3Prepare a Cadau integration account (path B)Email/password; must be a member of the agent’s workspace (need not own the agent)
4Invite/add the integration account to the agent’s workspaceCan switch into that workspace before minting; production: invite in Cadau UI; local testing: §7.3
5Confirm public URLsWidget script URL, API prefix /api/v1
6(Optional) My agents → Manage → Live support: turn on, and assign support seats in this workspace or authorize a support teamWidget shows “Live support / Submit a ticket”; support works them in the Cadau support desk (same as static website embed)
7(Path A, optional for other hosts) that assistant Manage → Website embed → Generate tokenaccess_token, expires_at

Path B does not need you to click “generate token” in the Cadau UI for every user or every agent; minting uses the same API as the UI button. Live support / tickets also do not need a second host-issued token: the embed token from embed-session has the same widget support-API rights as a token pasted on a static site.

Knowledge documents: mount host business notes on the embed agent (menus, bounds, navigation conventions). HR example: docs/宿主知识文档/.


6. Host-system conversion checklist (developers)

6.1 Data layer (required, path B)

At least two tables (names are yours):

Registered connected agents

FieldMeaning
idHost internal primary key
tenant_idTenant isolation
labelDisplay name
mindlink_user_agent_idCadau agent UUID
app_idDefault same as global config
mindlink_workspace_idPrefer the Cadau workspace UUID the agent lives in (this phase same as MINDLINK_WORKSPACE_ID). Mint switches with this value first
statusactive / disabled

User–agent assignments

FieldMeaning
tenant_id + user_idUnique; user_id = host signed-in user id
agent_idPoints at the registration row
scopes_jsonAssistant operation permissions (host-defined enums; HR example §8.3)
user_labelOptional, for display

HR example permission enums: two-dimensional feature module × operation, keys like module.permission (for example employees.query). Operations include query, create, edit, delete; edit and delete carry hallucination risk, so the UI has a safety note and they are off by default.

6.2 Admin features (required, path B)

Admins in the host UI or via API:

  1. Register a Cadau agent (name + agent ID + app_id) — no token
  2. Assign to a signed-in user: pick a registration row + check operation permissions (at least one; default query only)

HR example entry: sidebar User management (workspace admin). Ops may use External access → User agents (cross-tenant tokens).

6.3 Embed-session BFF (required, path B)

Implement something like:

GET /api/v1/tenants/{tenantId}/me/embed-session Header: Authorization: Bearer <host sign-in JWT>

Server logic:

  1. Resolve the current signed-in user
  2. Look up whether this tenant has an active assignment
  3. No assignment → 200 + { "available": false, "reason": "..." } (do not use 404, which browsers treat as a hard error)
  4. Has assignment → sign in to Cadau with MINDLINK_INTEGRATION_*POST /workspaces/{id}/switch using the registration row’s mindlink_workspace_id (if any) or MINDLINK_WORKSPACE_ID

POST /api/v1/user-agents/{mindlink_user_agent_id}/embed-token body: { "app_id": "...", "ttl_seconds": 3600 } (Cadau requires: the integration account is a member of that workspace; the agent must be in that workspace, need not be owned by the integration account)

  1. Return the fields the widget needs (see §9.2)

Integration account and URLs live only in server env vars. Do not commit them to the frontend repo.

6.4 Frontend mount (required)

  1. User opens a page that should show the assistant
  2. (B) Request embed-session with the host JWT; or (A) read static config
  3. Load widget_script dynamically
  4. Call window.MindLinkWidget.init({ app_id, api_base_url, user_agent_id, workspace_id, auth: { token, expires_at }, ... })
  5. Listen widget.on("action", ...) for in-reply navigation (see §10)
  6. Before the token expires, the BFF refreshes and widget.updateAuth({ token, expires_at }) (if you do not renew after expiry, widget live-support / ticket requests 401)
  7. Do not write extra host APIs for live support or tickets; see §10.4

HR example: web/src/mindlinkOrgEmbed.ts, web/src/mindlinkHostActions.ts.

6.5 Network (required)

  • Browser access to Cadau APIs needs a same-origin reverse proxy or allowed CORS
  • Dev example (web/vite.config.ts):

- /mindlink-api → Cadau :8080 /api (widget conversation API) - /mindlink-embed → Cadau :8080 (widget script /embed/mindlink-widget.min.js)

  • Frontend api_base_url / widget_script should share origin with the browser address bar (for example all http://localhost:5180), so mixing localhost and 127.0.0.1 does not break CORS
  • Production: under the host domain, /mindlink-api (and the script path) → Cadau gateway

7. Config overview

7.1 Server env (path B, host backend .env)

MINDLINK_API_BASE=https://mindlink.example.com/api/v1
MINDLINK_API_BASE_PUBLIC=https://your-host.example.com/mindlink-api/v1
MINDLINK_WIDGET_SCRIPT=https://mindlink.example.com/embed/mindlink-widget.min.js
MINDLINK_INTEGRATION_EMAIL=integration@your-corp.com
MINDLINK_INTEGRATION_PHONE=
MINDLINK_INTEGRATION_PASSWORD=<server only>
MINDLINK_APP_ID=your-corp-app
MINDLINK_WORKSPACE_ID=
MINDLINK_GENERATE_USER_AGENT_ID=
# The next two are HR example local demo seed only; do not rely on them in production
# HRMS_SEED_MINDLINK_USER_AGENT_ID=
# HRMS_SEED_MINDLINK_DEMO=0
VariableRole
MINDLINK_API_BASEHost server calls Cadau (mint / run)
MINDLINK_API_BASE_PUBLICBrowser calls Cadau (public URL via the host proxy)
MINDLINK_WIDGET_SCRIPTWidget script URL
MINDLINK_INTEGRATION_EMAIL / PHONECadau account used to mint (email or phone, one of them); one set serves all registered agents
MINDLINK_INTEGRATION_PASSWORDIntegration-account password, server only
MINDLINK_APP_IDDefault app_id for the page widget; a registration row may override
MINDLINK_WORKSPACE_IDCadau workspace shared by the whole host in this phase
MINDLINK_GENERATE_USER_AGENT_IDCadau agent UUID used for generate/analyze (for example suggest jobs). Configure it in Cadau first. Choosing it on “embed assistant registry” overrides this. Not the page-widget assistant
HRMS_SEED_MINDLINK_USER_AGENT_IDDemo seed only. When the HR service starts, it registers that UUID into the DEMO workspace and assigns it to the demo account so local sign-in has a bottom-right assistant. Everyday conversation / minting does not read this (it reads assignments in User management). After setup:hr-work-agents and members already have assignments, leave it empty or delete it. Unrelated to MINDLINK_GENERATE_USER_AGENT_ID
HRMS_SEED_MINDLINK_DEMOSet to 0 to turn the demo seed off

MINDLINK_EMBED_TTL_SECONDS and MINDLINK_WIDGET_INLINE have code defaults (3600 seconds, floating widget); they need not go in .env.

7.2 Frontend static config (path A: other hosts may use; HR example has dropped this)

The HR example frontend no longer reads VITE_MINDLINK_* / web/.env.local. The following is only for other systems’ local static testing.

VITE_MINDLINK_WIDGET_SCRIPT=...
VITE_MINDLINK_API_BASE_URL=...
VITE_MINDLINK_APP_ID=...
VITE_MINDLINK_USER_AGENT_ID=<Cadau agent UUID>
VITE_MINDLINK_EMBED_TOKEN=<embed token, do not commit>
VITE_MINDLINK_TOKEN_EXPIRES_AT=...
VITE_MINDLINK_WORKSPACE_ID=...

7.3 Agent ID, integration account, and local scripts

HowNotes
Cadau UIThat assistant Manage → Website embed
Cadau APIGET /api/v1/user-agents
HR example: write server .envIn the web directory npm run setup:mindlink (or node scripts/write-mindlink-env.mjs)

What setup:mindlink does

  1. Calls Cadau POST /api/v1/auth/register for integration account hr-embed-demo@mindlink.local / MindLink-HR-demo-2026 (if it exists, POST /api/v1/auth/login; script default API_BASE=…/api/v1)
  2. Probes or creates a usable workspace, reads agent IDs in the workspace
  3. Writes MINDLINK_* into ../server/.env, and HRMS_SEED_MINDLINK_USER_AGENT_ID (the work-agent UUID it found) so the HR service can one-time register/assign the DEMO demo account at startup. This is not runtime widget config; production should register and assign in host User management and need not depend on this.

So you need not create that email by hand in Cadau. Production: create your own dedicated integration account; do not use the example demo email.

HowNotes
HR example: connect an existing agentIn the web directory npm run link:mindlink-agent -- <MindLink agent UUID>: fix registration mindlink_workspace_id, (when the main DB is SQLite) add the integration account to that workspace, verify embed-token
HR example: work agentsIn the web directory npm run setup:hr-work-agents: three conversation assistants. After the generate/analyze agent is configured in Cadau, write MINDLINK_GENERATE_USER_AGENT_ID, or pick it on “embed assistant registry”

If the workspace on the registration row differs from MINDLINK_WORKSPACE_ID, fill mindlink_workspace_id or run link:mindlink-agent, or embed-session may 502.

Postgres main DB: if local Cadau uses Postgres (not backend/mindlink.db), link:mindlink-agent may say “agent not found”. Invite the integration account into the workspace in the Cadau UI, or re-run setup:mindlink; and check that the HR registration row and .env workspace / agent IDs match.

Automated smoke (web directory; HR and Cadau backends already running):

CommandUse
npm run smoke:embedEmbed path B: sign-in → embed-session → Cadau conversation
npm run smoke:host-agentLegacy-host enhancement: host_actor, access-policy compile, agent-run conversation isolation

Details: [README.md](/docs/sdk-host-embed) §8.1; full manual items: examples/hr-multi-tenant/docs/ACCEPTANCE_HOST_LEGACY.md in the repo.


8. Admin operations (HR example walkthrough)

Verify the following in the HR example; your system should offer an equivalent UI or API.

8.1 Prerequisites

  • Cadau running
  • Host backend and frontend running
  • Workspace admin signed in (demo: 13800138000 / Demo-HR-2026)

8.2 Register an agent

Path: workspace → User managementEmbed assistant registry

Preferred: click Sync from Cadau. The host server uses the integration account to call Cadau GET /api/v1/user-agents?scope=workspace, then upserts agents in the current MINDLINK_WORKSPACE_ID (or the registration workspace) into this tenant’s registration table (updates name and workspace; does not auto-disable local rows that did not appear this time).

You can also register by hand:

FieldRequired
Display nameYes
Cadau agent IDYes
app_idNo (default mindlink-embed-hr)

API: POST /api/v1/tenants/{tenantId}/mindlink-agents/sync (tenant admin; optional body workspace_id / app_id).

8.3 Assign users

User management → member Configure agent (gear) → pick an agent → left: feature module, right: operation permissions → save.

Feature modules (aligned with HR domains):

Module idUI name
org_structureOrg chart
employeesPeople
positionsJobs and levels
employee_lifecycleHire / leave / transfer
competencyCompetency
successionSuccession and pipeline

Operations under each module:

Permission idUI nameNotes
queryQueryAllow the assistant to look up this module; recommended default on
createCreateAllow the assistant to start create-style writes in this module
editEditModels can hallucinate; for data safety, leave off unless you have a special use
deleteDeleteAccidental-delete risk is high; leave off unless you have a special use

Stored as flat keys moduleId.permissionId (for example employees.query). New assignments in the HR example default to Org chart · Query only.


9. API contract and examples

Paths below follow the HR example; keep equivalent meaning in your system.

9.1 Host user sign-in

POST /api/v1/auth/login
Content-Type: application/json

{ "phone": "13800138000", "password": "Demo-HR-2026" }

Response includes access_token (host JWT). Later tenant APIs need:

Authorization: Bearer <access_token>

9.2 Embed session (B core)

GET /api/v1/tenants/{tenantId}/me/embed-session
Authorization: Bearer <host JWT>

Unassigned — 200

{
  "available": false,
  "reason": "No agent has been assigned to this user yet"
}

Assigned and mint succeeded — 200

{
  "available": true,
  "user_id": "...",
  "user_label": "Demo admin",
  "agent_id": "...",
  "agent_label": "HR general assistant",
  "user_agent_id": "4b2819de-8fcf-432c-b1ee-5593c78d4e64",
  "workspace_id": "be02b465-de92-4e3f-9352-67ff37a51dc9",
  "app_id": "mindlink-embed-hr",
  "access_token": "<embed JWT>",
  "expires_at": "2026-05-21T12:00:00Z",
  "scopes": {
    "org_structure.query": true,
    "org_structure.create": true,
    "employees.query": true
  },
  "host_actor": {
    "external_user_id": "...",
    "actor_kind": "business",
    "display_name": "Demo admin",
    "tenant_external_id": "...",
    "roles": ["admin", "manager"],
    "managed_org_unit_ids": []
  },
  "widget_script": "http://127.0.0.1:8080/embed/mindlink-widget.min.js",
  "api_base_url": "http://127.0.0.1:5180/mindlink-api/v1",
  "widget_inline": false
}

The frontend maps access_tokenauth.token, and user_agent_id, app_id, api_base_url, widget_script, host_actorMindLinkWidget.init. On renew, sync updateAuth and updateHostActor. Employee self-service must include employee_id; full field set: Host agent run and data scope §0.1. The widget automatically sends header X-Host-External-User-Id (B: signed-in user id; A: browser visitor id).

Common errors

HTTPMeaning
401Not signed in to the host
503Host missing MINDLINK_API_BASE and similar
502Mint failed (integration account, agent id, app_id, and similar)

9.3 Register an agent (admin)

POST /api/v1/tenants/{tenantId}/mindlink-agents
Authorization: Bearer <host JWT>
Content-Type: application/json

{
  "label": "HR general assistant",
  "mindlink_user_agent_id": "4b2819de-8fcf-432c-b1ee-5593c78d4e64",
  "app_id": "mindlink-embed-hr"
}

9.4 Save a user assignment (admin)

PUT /api/v1/tenants/{tenantId}/members/{userId}/agent-assignment
Authorization: Bearer <host JWT>
Content-Type: application/json

{
  "agent_id": "<registration row id>",
  "scopes": {
    "org_structure.query": true,
    "org_structure.create": true,
    "employees.query": true,
    "employees.create": false,
    "employees.edit": false,
    "employees.delete": false
  }
}

9.5 Membership (optional, for frontend)

GET /api/v1/tenants/{tenantId}/me/membership

Response includes is_admin, has_agent_assignment (when assigned, you can skip unnecessary embed-session failure retries).

9.6 Cadau mint API (host server, not the browser)

Switch workspace first (JWT carries wid), then mint; do not rely on the X-Workspace-Id header (HR example client: mindlinkclient).

POST /api/v1/auth/login
Content-Type: application/json

{ "email": "integration@your-corp.com", "password": "..." }
POST /api/v1/workspaces/{workspaceId}/switch
Authorization: Bearer <access_token from previous step>
POST /api/v1/user-agents/{mindlinkUserAgentId}/embed-token
Authorization: Bearer <access_token returned by switch>
Content-Type: application/json

{
  "app_id": "mindlink-embed-hr",
  "ttl_seconds": 3600
}

When the browser holds the embed JWT for conversation, Cadau checks agent in the workspace + user_agent_id bound on the token registration (the integration account need not own the agent).

9.7 Task-style: suggest jobs by department (server call, not the widget)

Unlike me/agent-run: this does not use the user’s assigned conversation assistant. It calls the chosen generate/analyze agent, each time fresh: true. Results are preview only, not written to the job catalog.

How to choose (one of two; settings page wins):

  1. server/.env: MINDLINK_GENERATE_USER_AGENT_ID=<Cadau agent UUID>
  2. “User management → Embed assistant registry → Generate and analyze agent”

Create the agent in Cadau and add it to the workspace first; only then can you pick it from the dropdown after registering it on this page.

GET /api/v1/tenants/{tenantId}/mindlink-generate-agent
Authorization: Bearer <host JWT>
{
  "agent_id": "<registration row id; empty when not chosen on the settings page>",
  "mindlink_user_agent_id": "<Cadau agent UUID>",
  "label": "…",
  "source": "tenant | env | none",
  "env_user_agent_id": "<UUID from .env>"
}
PUT /api/v1/tenants/{tenantId}/mindlink-generate-agent
Authorization: Bearer <host JWT>
Content-Type: application/json

{ "agent_id": "<registration row id of a registered assistant; empty string falls back to .env>" }

Requires workspace admin. source tenant means the settings page chose it; env means env-var fallback; none means not chosen yet.

POST /api/v1/tenants/{tenantId}/positions/suggest-for-org
Authorization: Bearer <host JWT>
Content-Type: application/json

{ "org_unit_id": "<department ID>" }

Page: “Jobs and levels” → pick a department → Suggest jobs for this department. Notes: Host LLM service §5.1.

Full API list: HR example HOST_USER_AGENTS.md.


10. Frontend mount and in-reply navigation

10.1 Minimal init (path-independent)

const widget = window.MindLinkWidget.init({
  app_id: session.app_id,
  api_base_url: session.api_base_url,
  user_agent_id: session.user_agent_id,
  workspace_id: session.workspace_id,
  auth: {
    token: session.access_token,
    expires_at: session.expires_at,
  },
  // Path B required: same host_actor as embed-session (isolates history / support / tickets; also host data queries)
  host_actor: session.host_actor,
  theme: "auto",
  position: "bottom-right",
  locale: "zh-CN",
  entry: { auto_open: false, auto_execute_navigation: true },
  // The example top bar can also pick a “fixed entry”: hide_launcher: true, a host button calls widget.open()
});

widget.on("action", (ev) => {
  if (ev.type !== "action" || !ev.action) return;
  // Parse mindlink_action (emit_event), jump to a host route
});

// Renew example:
// widget.updateAuth({ token, expires_at });
// widget.updateHostActor(session.host_actor);

Example implementation: examples/hr-multi-tenant/web/src/mindlinkOrgEmbed.ts.

10.2 In-reply navigation (optional but recommended)

Agree Markdown links in the agent knowledge documents:

`Open org chart`

The host page whitelists action names and jumps (HR example: page.overview, page.org, and similar — see mindlinkHostActions.ts).

10.3 Widget experience (aligned with Cadau main site)

The embed widget (mindlink-widget.min.js) already includes: multi-turn conversation quick jump to questions on the right (hover to expand the title), focus back to the input after a reply, floating launcher sticks to the edge and stays in the viewport. Third parties need not reimplement this; upgrade the Cadau-served script.

10.4 Live support and tickets (same as static website embed)

A static marketing site (path A, for example examples/cadau-embed-site) pastes a script into the page; as long as that agent has live support on, “Live support” and “Submit a ticket” appear next to the widget input. Path B (backend minting) uses the same widget script, so both entries are already there. The host must not implement a support queue, message thread, or ticket table.

| | Live support (immediate) | Tickets (async) | |--|---------------------|------------------| | User expectation | Someone chats now | Leave a message and leave; check progress later | | Widget entry | “Live support” | “Submit a ticket”; while waiting, “Don’t wait — submit a ticket instead” | | When available | That agent has live support on, and at least one support person covering this workspace is on duty (a seat in this workspace, or a member of an authorized team who clocked on at the team desk) | Agent has live support on (when nobody is on duty, only this remains) | | Where support works it | Cadau desk Live support | Cadau desk Tickets |

In copy, do not call live help a “ticket”. Mechanism: Cadau docs/core-mechanisms/人工客服.md, 工单.md.

Cadau side (admins)

  1. Open the agent you are embedding → Manage → Live support → turn on. Assign members of this workspace as support seats, or do not check local people and authorize a support team instead (admins are not support by default).
  2. Support opens Cadau top bar Support (visible to seats in this workspace or team members; otherwise enter the desk from the Feature menu) and sets status to on duty. Team members covering several customers switch to the team desk on the right of the support desk, then clock on; they need not change the top-bar current workspace.
  3. After a visitor submits on the host page, the desk list updates (live inbound may play a sound); no full page refresh.
  4. After you turn live support off on the agent, the widget hides both entries.

Host side (developers, path B)

  • The token minted by embed-session already can call widget live support and tickets; do not build a support desk on the host.
  • Must pass embed-session’s host_actor into MindLinkWidget.init; on signed-in user switch, updateHostActor (or init again). If omitted, the same browser may show the previous person’s history / support / tickets.
  • Frontend still updateAuth to renew. After token expiry the widget stops empty-spinning requests (avoids 401 floods); after the user re-enters the page or renews, support entries return.
  • Path B (signed-in host user): history, live support, and tickets are isolated by the current signed-in user — A only sees A’s, not B’s; the same account in another browser is still that person.
  • Path A (unsigned website visitor): in-progress requests are isolated by this browser. Opening again in the same browser is the same visitor; a different browser or cleared site data is a new visitor.
  • Seat replies always show as “Support” on the customer side; seat phone numbers are not exposed.
  • Support colleagues work in Cadau, not in the HR / host admin UI.

Integration tip: the static marketing site and the HR example are often not the same agent or the same workspace. Turning live support on for the static example site does not automatically appear on the HR “action assistant”. Turn it on for the agent you are actually embedding (for example the admin assistant). In this repo npm run setup:hr-work-agents turns live support on for the three demo work agents, uses the integration account as a seat, and sets it on duty. When working inbound: seats in this workspace should confirm the top-bar current workspace is the agent’s workspace; if you use a support team, switch to the team desk on the right to see authorized customer queues — do not rely on switching the top-bar workspace.


11. Recommended rollout order

11.1 Phase 1: prove the widget (A, 1–2 days)

  1. Cadau deploy is reachable
  2. Generate an embed token (UI or write-mindlink-env.mjs)
  3. Static init on any host page; conversation works
  4. Confirm network (proxy/CORS) has no errors

11.2 Phase 2: host BFF + assignment (B, main conversion)

  1. Tables + admin API/UI (register, assign)
  2. Implement embed-session + server MINDLINK_*
  3. Frontend fetches embed-session after sign-in, then inits
  4. Finish assignment for test users

11.3 Phase 3: production

  1. Dedicated integration account, short TTL, HTTPS
  2. Production build does not contain path A long-lived tokens
  3. Mount host knowledge documents
  4. Finish §12 acceptance

12. Acceptance checklist

After conversion, confirm each item:

Cadau

  • [ ] Agent created, knowledge documents mounted
  • [ ] app_id agreed and matches the host
  • [ ] B: integration account can sign in, is in the agent’s workspace, and can call embed-token
  • [ ] Registration mindlink_workspace_id matches the agent’s real workspace (or link:mindlink-agent has been run)
  • [ ] (Optional) that agent has Live support on, with seats in this workspace or an authorized support team; support can switch on duty / on break / off duty at the desk

Host backend

  • [ ] Register and assign APIs work
  • [ ] embed-session for assigned users returns available: true and access_token
  • [ ] embed-session for unassigned users returns available: false (not 404)
  • [ ] MINDLINK_INTEGRATION_PASSWORD is not in the frontend or a public repo

Host frontend

  • [ ] After sign-in the assistant appears bottom-right (or in the chosen container)
  • [ ] When different users are assigned different agents, conversation identity is correct (B)
  • [ ] (B) init received host_actor; two signed-in users cannot see each other’s history, live support, or tickets
  • [ ] Two visitors can ask at once and each get a reply; when overloaded the widget says “Too many people talking right now. Try again in a moment.”
  • [ ] Before token expiry you can refresh, or re-entering the page can mint again
  • [ ] (Optional) clicking navigation links in replies jumps to host pages
  • [ ] (Optional) when the agent has live support on: the widget has “Live support” (someone must be on duty) and “Submit a ticket”; when nobody is on duty, only submit-ticket remains
  • [ ] (Optional) after a visitor submits, Cadau support desk live / ticket lists update without a full page refresh; widget My tickets can show progress

Automation (HR example, optional)

  • [ ] npm run smoke:embed passes
  • [ ] (Legacy-host enhancement) npm run smoke:host-agent passes

Legacy-host enhancement (if Host Agent Run / business-DB queries are on)

  • [ ] embed-session / agent-run include a correct host_actor (employees include employee_id)
  • [ ] Cadau workspace data connection + access policy configured
  • [ ] Employees see only self, manager columns trimmed, conversations do not leak (see ACCEPTANCE_HOST_LEGACY.md)
  • [ ] (Optional) generate/analyze agent chosen (MINDLINK_GENERATE_USER_AGENT_ID in .env or “embed assistant registry”); Jobs and levels can generate job suggestions and does not auto-write the job catalog

Security and ops

  • [ ] Production is B only, or A in dev only
  • [ ] Cadau API is same-origin proxied by the host or CORS is compliant
  • [ ] Revoke/rotate integration-account process is documented

13. FAQ

SymptomCheck
embed-session 404Upgrade the host backend; unassigned should return 200 + available:false
embed-session 502, body contains embed-token / not_found / agent not foundIntegration account not in the agent’s workspace; registration mindlink_workspace_id wrong; invite the integration account in Cadau UI, or (SQLite main DB) npm run link:mindlink-agent -- <agent ID>, or re-run setup:mindlink
link:mindlink-agent says agent not foundCadau main DB is Postgres so the script cannot read mindlink.db; invite in the UI + check .env, or use setup:mindlink only
embed-session 502, cannot switch workspaceMINDLINK_WORKSPACE_ID does not match a workspace the integration account can access
Assistant appears but conversation 400 invalid_user_agentCadau version is too old; it must allow embed tokens to access agents in the workspace (not only “mine”)
Deleted HRMS_SEED_MINDLINK_USER_AGENT_ID and the assistant vanishedIt will not (when members already have assignments). That var only backfills register for the demo account at startup; everyday widget reads User management assignments. Generate/analyze uses MINDLINK_GENERATE_USER_AGENT_ID or “embed assistant registry”
Console CORS / cannot reach Cadau APIapi_base_url same origin as the page; in development do not mix localhost and 127.0.0.1
Console 401 embed-sessionHost JWT missing or expired
A/B assistant behavior differsAlign on the same mindlink_user_agent_id
Clicking User management signs you outTenant APIs must send Authorization: Bearer
A separate token per agent?No; register the ID, mint a different token at runtime
Widget has no “Live support / Submit a ticket”The agent you are embedding does not have live support on (static site being on does not mean the HR assistant is on); or you are looking at the wrong agent / workspace
“Submit a ticket” but no “Live support”Expected: no support covering this workspace is on duty; a seat in this workspace or an authorized team member clocks on at the Cadau support desk
Visitor submitted, support page only updates after refreshConfirm Cadau backend includes live support push; the seat should keep the desk open and stay signed in
Widget support requests keep 401Embed token expired without updateAuth; check embed-session TTL and renew
Should we build a ticket list in HR?No; tickets live in the Cadau support desk; the widget has “My tickets”
One support group covering several customer workspacesUse a Cadau support team (the team creates the group, the customer authorizes, listed in service scope); members switch to the team desk; do not add them as members of every customer workspace
After switching accounts you still see the previous user’s history / support / ticketsinit did not get host_actor, or you did not updateHostActor after switch; see §10.4

14. Related docs and reference code

ResourceNotes
Website integrationCadau generic embed, init, navigation
Embed contractToken, SSE, security contract
[README.md](/docs/sdk-host-embed)Third-party integration overview
Host LLM serviceHost backend asks a chosen agent to answer
Host agent run and data scopeLegacy-host user model, new-host checklist, data-connection policy
HR example docs/HOST_LEGACY_INTEGRATION.mdLegacy-host mapping and SQLite testing (in-repo)
HR example docs/ACCEPTANCE_HOST_LEGACY.mdLegacy-host enhancement manual acceptance (in-repo)
HR example docs/HOST_USER_AGENTS.mdHR example API and table summary (in-repo)
HR example docs/宿主知识文档/00-总则与功能导航.mdAssistant reply bounds (HR example)
examples/hr-multi-tenant/web/src/mindlinkOrgEmbed.tsPath B load and mount
examples/hr-multi-tenant/web/scripts/setup-hr-work-agents.mjsThree conversation assistants
examples/hr-multi-tenant/web/scripts/link-mindlink-agent.mjsConnect an existing Cadau agent locally
examples/hr-multi-tenant/server/internal/mindlinkclient/Mint client
examples/hr-multi-tenant/server/.env.exampleEnv template

15. Maintenance

ItemValue
Doc typeThird-party host conversion handbook
Reference implementationexamples/hr-multi-tenant
Doc version2026-08-17 (aligned with SDK contract V1.5.12)
HR example default app_idConversation mindlink-embed-hr; generate jobs mindlink-embed-hr-generate