Skip to content

Signup attribution: capture WhatsApp Concierge ref and notify the bot on conversion #3747

Description

@vibegui

Context

The DecoCMS Concierge (top-of-funnel WhatsApp bot, repo decocms/concierge) hands every prospect a tracked signup link in its CTA:

https://studio.decocms.com/signup?src=wa&ref=<leadId>
  • src=wa — channel (WhatsApp).
  • ref=<leadId> — an opaque per-conversation id (e.g. wa8hcxfm). It maps back to a specific WhatsApp lead in the Concierge's lead store.

The Concierge already does its half: it emits the link, stores the ref on the lead, and exposes an endpoint to mark a lead converted. What's missing is the Studio side: Studio needs to (1) preserve ref through the signup flow and (2) tell the Concierge when that signup actually completes, so we can close the WhatsApp → signup loop and report true conversion.

Goal

When a user who arrived via …/signup?src=wa&ref=X finishes creating their account, Studio makes one server-side call back to the Concierge with ref=X. That flips the lead to signed_up and lights up conversion in the funnel report.

What Studio needs to do

  1. Capture ref (and src) on landing. On /signup (and any entry route that can carry these params), read ref/src and persist them so they survive the entire funnel — page navigations, email verification, and especially OAuth round-trips (Google/GitHub) where query params are commonly lost. Cookie (e.g. httpOnly not required, but first-party) or server-side session both work; just don't lose it across redirects.
  2. Fire on successful signup. At the point a new account is created (server-side hook / post-signup callback — not on every login), if a ref is present, POST it to the Concierge (details below). Server-side, exactly once.
  3. No-op when absent. If there's no ref, do nothing (most signups won't have one).
  4. (Nice to have) Persist src/ref on the user/org record too, so Studio's own analytics can attribute signups by channel independently of the Concierge.

API contract (Concierge endpoint)

POST https://decocms-concierge.deco-ceo.workers.dev/event
Authorization: Bearer <CONCIERGE_MCP_AUTH_TOKEN>
Content-Type: application/json

{ "type": "signup", "ref": "wa8hcxfm" }
  • Auth: Authorization: Bearer <token> (also accepts x-mcp-auth: <token> or ?token=). The token is the Concierge's MCP_AUTH_TOKENstore it as a Studio server secret; never expose it client-side. We'll share the value via the secrets manager (do not paste it in this issue/PR).
  • Response: 200 { "ok": true, "lead": { "phone", "ref", "signedUp": true, "signedUpAt" } } on success; 404 { "ok": false } if no lead matches the ref (safe to ignore/log).
  • Idempotent: calling twice is fine (it just re-sets signedUp).
  • You may send { "phone": "<e164>" } instead of ref if Studio happens to know the WhatsApp number, but ref is the expected path.

Equivalent option: call the Concierge MCP tool record_conversion({ ref }) if Studio already speaks MCP to it. The /event POST is the simplest path.

Example:

curl -X POST https://decocms-concierge.deco-ceo.workers.dev/event \
  -H "Authorization: Bearer $CONCIERGE_MCP_AUTH_TOKEN" \
  -H "content-type: application/json" \
  -d '{"type":"signup","ref":"wa8hcxfm"}'

Acceptance criteria

  • Visiting https://studio.decocms.com/signup?src=wa&ref=ABC and completing signup preserves ref=ABC through the full flow, including at least one OAuth provider round-trip and/or email verification.
  • On account creation (not login), Studio fires exactly one server-side POST /event with the correct ref.
  • The call is authenticated with the shared secret, read from a server-side secret (token never reaches the browser/bundle).
  • When no ref is present, no call is made and signup is unaffected.
  • Failure of the callback (network/non-200) does not block or fail the signup; it's logged and ideally retried.
  • Verified end-to-end: a test signup with a known ref results in the Concierge lead showing signedUp: true (confirmable via the Concierge get_lead/get_funnel_report MCP tools).
  • (If implemented) src/ref stored on the new user/org for Studio-side attribution.

Security notes

  • The callback must be server-to-server. Do not embed CONCIERGE_MCP_AUTH_TOKEN in client JS.
  • ref is opaque and non-sensitive (no PII); fine to keep in a first-party cookie/query.

Open questions for the Studio team

  • Where is the canonical "account created" server hook in Studio's auth (Better Auth) flow to attach this callback?
  • Preferred mechanism to carry ref across OAuth — first-party cookie set on /signup, or encode into the OAuth state?
  • Should we also forward utm_* params for completeness, or just src/ref?

Filed by the Concierge team. Concierge side (link emission, lead store, /event + record_conversion) is already live on main in decocms/concierge.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions