Agentora TechnologiesAgentora
Platform architecture

When the phone number is the account

A consumer product whose users live on WhatsApp cannot start by asking them to make a web account. Here is how signup moved into the chat itself, what it cost to have identities that no auth provider knows about, and how those accounts get a web login later without losing anything.

Agentora EngineeringAugust 21, 20268 min read

What this system does

Agentora's consumer assistant builds a personal growth plan and sends a short daily brief. Its audience reaches WhatsApp far more readily than a web form: many have no work email and type English with difficulty. This case study covers how an account gets created for that person, and what happens when they later want to open the product in a browser.

The problem

Every consumer feature keyed on a profile row, and the only way to get one was the website: make an account, confirm an email, open a settings tab, copy a code, send it to the WhatsApp number, come back. An unrecognised number that asked for a plan was handed those instructions at the moment it was most willing, and that is where the funnel ended.

The obvious fix — create a real auth user for the number — did not survive contact with the platform. The auth provider's phone sign-in was disabled on the project, and not one existing user had a phone identity. A signup that depended on it would have failed on the very first message rather than at some later edge.

Email was not a substitute. The people this is for often do not have an address they check, and asking for one to receive a plan they requested in a chat reintroduces exactly the friction being removed.

So the account had to exist without the auth provider knowing about it — which creates its own problem, because three surfaces in the product are web-only and every one of them would send such a person to a sign-in wall they could never pass.

Constraints

The boundaries the design had to respect, before any solution was chosen.

  • Nothing may be written about a person before they have seen what will be collected and agreed to it — the profile carries health and financial context.
  • A completed signup ends in a paid model generation, and before an account exists there is no per-user allowance to charge it against, so the spend must be bounded some other way.
  • One number must map to exactly one account, enforced by the database rather than by application care.
  • A person who later obtains a web login must keep everything: plan, briefs, learning library, usage history, subscription.
  • Two profiles must never be merged silently. Deciding whose answers survive is not a decision to make on someone's behalf.

The architecture

The design treats the WhatsApp number as a first-class identity rather than a contact detail attached to a real one. Consumer data keys on a profile's user id and nothing references the auth provider's user table, so an identity minted in a chat works everywhere the product actually runs.

  1. 1

    The inbound message is the verification

    WhatsApp delivers a message with the sender's number attested by the platform. We never asked them to type it and they cannot put someone else's number on the webhook, which makes it a stronger proof of possession than a one-time code sent by us — a code can be read out over the phone to somebody impersonating support. The number is therefore marked verified at signup and no code is sent.

  2. 2

    An identity minted in the conversation, materialised at consent

    The interview opens with a notice in three languages saying what will be asked and what happens to it. A user id is generated when the conversation starts so the queued job, the profile and the usage rows all agree on one value — but nothing reaches the profile table until the consent question at the end. An abandoned intake leaves no profile, no plan and no account.

  3. 3

    The identity's origin is recorded, not inferred

    Each profile stores whether it was minted by the auth provider or by WhatsApp. Every surface that offers a link into the web app checks it, so the decision is about what the account IS rather than what the current conversation happens to be doing — an earlier version keyed off a per-conversation flag and correctly withheld the link on the first day, then sent it on every day after.

  4. 4

    Spend bounded per number and globally

    The rate limiter already protecting one-time codes gained a signup channel: a per-number daily ceiling and a global daily ceiling, both failing closed. It is the only bound available before an account exists, so it holds the whole path.

  5. 5

    Claiming: one transaction, every table

    A SQL function re-keys fourteen consumer tables from the WhatsApp identity to a web one and rewrites the profile in the same transaction. Changing only the profile would orphan the rest, since plans, jobs, briefs, subscriptions and usage all follow the user id. It refuses outright when the target account already has a profile of its own.

  6. 6

    Reaching the claim from the chat

    The claim function existed for months reachable only from the link-code path — which requires already having the web account you are trying to obtain. The missing half was an email address, so the chat asks for one, creates the auth user, re-keys the data, and only then asks the auth provider to send its own sign-in link. No password is set, no session is minted, and no token travels through WhatsApp.

Key decisions and their trade-offs

Every decision below cost something. The trade-off is stated alongside the reasoning.

Treat the WhatsApp number as an identity rather than create an auth user for it

Why
The platform's phone provider was disabled and no existing user had a phone identity, so depending on it would have broken signup on the first message. Consumer data keys on a profile id and nothing FKs to the auth user table, so a chat-born identity works everywhere the product runs.

Trade-off
Three web-only surfaces cannot be reached by such an account until it is claimed, and each had to check identity origin before offering a link. That accumulation of special cases is what eventually justified building the claim flow.

Verify the number by the platform's attestation instead of sending a code

Why
The inbound message already proves possession more strongly than a code we send, and asking someone to receive and retype a code inside the app they are already messaging from is friction with no security gain.

Trade-off
The proof is only as good as the messaging platform's own sender attestation, and it does not extend to any other channel.

Mint the user id at the start of the conversation, write the profile only at the end

Why
The plan job, the profile and the usage rows must agree on one identifier, and consent must gate the data rather than the conversation.

Trade-off
A session row exists for a number that has no account yet, so the conversation state is briefly held for someone who has consented to nothing beyond continuing to talk.

Refuse to merge when both sides already have a profile

Why
Reconciling two sets of answers means deciding whose health and financial context survives. Doing that silently is worse than declining and naming a human.

Trade-off
A real person with two accounts is stuck until support intervenes, which is a manual step the product cannot resolve on its own.

Have the auth provider send the sign-in link rather than issuing our own

Why
It keeps the credential between the provider and the person's inbox. Minting a session and delivering it over a chat message would put a working credential in a transport we do not control and cannot revoke.

Trade-off
The flow depends on the provider's email delivery working, and on its template being configured to carry a token the server can verify rather than a browser-bound one.

Technologies used

Messaging

  • WhatsApp Cloud API
  • Pre-approved message templates
  • Interactive list messages

Data

  • PostgreSQL
  • Partial unique index on the number
  • SECURITY DEFINER re-key function
  • Row Level Security, service-role only

Application

  • Next.js route handlers
  • Server-side session cookies
  • Rate limiting via an advisory-locked claim function

Outcome

  • A person with no email address and no web account can start on WhatsApp, answer eleven short questions in Gujarati, Hindi or English, and receive a generated plan — with the account created at the moment they consent and not before.
  • One number maps to one account by database constraint, so a second signup on the same number fails at the index rather than producing a duplicate the application would have to reconcile later.
  • A claim moves the whole account: in the first production run, fourteen tables and every row belonging to the identity moved together, with none left behind.
  • The refusal paths are honest rather than silent: an account that already has a login is told where to sign in, and an unresolvable two-profile collision names a human instead of guessing.

Known limitations

What this design does not do. Stated because an architecture without documented trade-offs has usually not been examined closely enough.

  • A claimed account depends on email delivery working. When the provider's mail was misconfigured, the flow completed the re-key and then could not tell anyone — the message says so explicitly rather than pretending, but the person still needs another route in.
  • Two profiles for one person cannot be merged by the product. The schema and the function are ready for it; the decision about whose answers survive is not one the system should make.
  • The identity origin is a column, so any new surface that links into the web app has to remember to check it. Three did not, in sequence, before the pattern was noticed.
  • A conversation session exists for numbers that never complete signup. It is transient and carries no answers, but it is state held about someone who has not yet consented to anything.
  • Nothing here helps a person who loses access to both the number and the email. There is no recovery path that does not involve support.

Want this level of rigour on your AI initiative?

Start with a free AI Readiness Assessment, or book a Discovery Workshop to get a scored, costed roadmap.