Modeling your users
Before you write any code, one decision shapes everything: how do your users map onto smiths and threads? Get this right and isolation, billing, and history all fall out for free. Get it wrong — usually by sharing one smith across people — and you fight the data model for the rest of the integration.
Smith or thread?
These are the two units you compose, and they do different jobs:
- A smith is an identity — one end-user. It is the platform's isolation boundary: it owns that person's memory, OAuth connections, deployments, and conversation history. Access is scoped per smith (a smith-scoped token sees exactly one smith, never another).
- A thread is one conversation under a smith. A smith has as many threads
as you like; you pick a
thread_idper conversation and the smith keeps context within it. Threads share the smith's memory — they don't have their own.
The rule:
A new person → a new smith. The same person starting another conversation → a new thread (a fresh
thread_id).
What to avoid: putting several people under one shared smith and telling them
apart with your own thread_id bookkeeping. There is no per-thread access
boundary — the smith is the boundary — so isolation between those people would be
yours to enforce in application code, not a structural guarantee. One missing
filter and one person sees another's conversation. If two users must not see each
other's data, give them separate smiths.
The default: one smith per end-user
Create a smith from your backend, keyed to your own user id. Creating is
upsert-by-(external_id, agent_id), so "ensure a smith" on every login is
safe — with one agent, that's one smith per user:
# Authorization: tenant-admin token (server-side only)
curl https://api.cloud.ingram.tech/v1/smiths \
-H "Authorization: Bearer $IC_TOKEN" \
-H "IC-Api-Version: 2026-05-01" \
-H "Content-Type: application/json" \
-d '{ "external_id": "user_123", "display_name": "Ada Lovelace" }'
Because memory is per smith, one person's facts can never surface in another person's conversation.
One person, several agents
If you run more than one agent (say an accountant, a tax advisor, and an
auditor), the same end-user gets one smith per agent — pass the same
external_id with a different agent_id and each pairing is its own smith, with
isolated memory and connections. Re-POSTing a given (external_id, agent_id)
returns that same smith. So external_id is how you say "this is the same
human"; agent_id is which assistant. The auditor never sees the accountant's
working memory; what they share — the person's identity — is whatever you pass on
each (name, metadata).
Teams and orgs (B2B2B)
When your customer is a company whose employees each use the assistant — even if the behaviour is near-identical for everyone in that company:
- Still one smith per person. Each employee needs their own conversation history and memory, so each is a smith.
- Group the org under a customer. Set
customer_idon every smith in that company; usage rolls up to one invoice and aggregate views, and an org-wide token sees all its smiths. - Shared behaviour → the agent. It lives once in an
agent that the org's smiths clone; they track its rollouts. If behaviour
differs between orgs, give each org its own agent. Per-person touches (a
name, a plan tier) come from per-smith variables
resolved from each smith's
metadata— no overrides, so everyone keeps tracking rollouts. - Shared knowledge is not memory. A smith's memory is private to that person. Knowledge the whole org should see (documents, a wiki, account state) belongs behind a tool / MCP server scoped to the org, not in any smith's memory.
Anonymous and logged-out users
A logged-out visitor is still a smith — just keyed to a token you mint rather
than to one of your user ids. Mint a random token on first contact, persist it
in a cookie, and key the smith under an anon: namespace:
# Authorization: tenant-admin token (server-side only)
curl https://api.cloud.ingram.tech/v1/smiths \
-H "Authorization: Bearer $IC_TOKEN" \
-H "IC-Api-Version: 2026-05-01" \
-H "Content-Type: application/json" \
-d '{ "external_id": "anon:9f2c7b1e…", "auto_memory": false }'
Each visitor gets their own smith, so their conversations (and memory, if you
keep it on) stay isolated — exactly as for a signed-in user. Set
auto_memory: false when a drive-by visitor shouldn't accumulate anything; turn
it on when you want a returning visitor (same cookie) to be remembered. Each
conversation under that smith is just a thread.
The anon: namespace keeps the segment queryable:
GET /v1/smiths?external_id_prefix=anon: lists every logged-out smith, and the
same external_id_prefix on GET /v1/runs pulls the whole segment's
conversation history — no is_anonymous flag needed.
The full embed-and-stream flow is in Build a support agent.
Claiming on login
When an anonymous visitor signs in, you want their in-progress conversation to
follow them. external_id is immutable, so you don't rename it — you keep
the same smith (its threads and memory carry over) and stamp the real identity:
# Authorization: tenant-admin token (server-side only)
curl -X PATCH https://api.cloud.ingram.tech/v1/smiths/smt_… \
-H "Authorization: Bearer $IC_TOKEN" \
-H "IC-Api-Version: 2026-05-01" \
-H "Content-Type: application/json" \
-d '{ "display_name": "Ada Lovelace", "customer_id": "cus_…" }'
Record your_user_id → smith_id on your side so the next login finds the same
smith. (If you'd rather start clean per identity, create a new user:-keyed
smith instead — but then the anonymous history doesn't follow.)
Choosing external_id
- Stable and unique per project — one live smith per value.
- Immutable once set, so pick deliberately; reach for
metadatafor things that change. - Namespaced (
user:…,anon:…) so segments stay filterable withexternal_id_prefix. - Optional: omit it entirely for a throwaway smith you address only by its
returned
smt_…id.