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 spend later fighting the data model.
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. That isolation is the whole point of one smith per person.
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 99% of the behaviour is 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. The 99% 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.
So: shared config → agent; shared knowledge → a tool; per-person history and memory → the smith; the org itself → a customer.
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 is what makes this manageable at volume:
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.