Ingram Cloud

Documentation

Modeling your users

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_id per 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_id on 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 metadata for things that change.
  • Namespaced (user:…, anon:…) so segments stay filterable with external_id_prefix.
  • Optional: omit it entirely for a throwaway smith you address only by its returned smt_… id.