Signup attribution: capture WhatsApp Concierge `ref` and notify the bot on conversion
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 407
- Forks
- 57
- Avg merge
- 5h 6m
- Merged PRs (30d)
- 880
Description
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
- Capture
ref(andsrc) on landing. On/signup(and any entry route that can carry these params), readref/srcand 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.httpOnlynot required, but first-party) or server-side session both work; just don't lose it across redirects. - Fire on successful signup. At the point a new account is created (server-side hook / post-signup callback — not on every login), if a
refis present, POST it to the Concierge (details below). Server-side, exactly once. - No-op when absent. If there's no
ref, do nothing (most signups won't have one). - (Nice to have) Persist
src/refon 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 acceptsx-mcp-auth: <token>or?token=). The token is the Concierge'sMCP_AUTH_TOKEN— store 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 theref(safe to ignore/log). - Idempotent: calling twice is fine (it just re-sets
signedUp). - You may send
{ "phone": "<e164>" }instead ofrefif Studio happens to know the WhatsApp number, butrefis the expected path.
Equivalent option: call the Concierge MCP tool
record_conversion({ ref })if Studio already speaks MCP to it. The/eventPOST 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=ABCand completing signup preservesref=ABCthrough 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 /eventwith the correctref. - The call is authenticated with the shared secret, read from a server-side secret (token never reaches the browser/bundle).
- When no
refis 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
refresults in the Concierge lead showingsignedUp: true(confirmable via the Conciergeget_lead/get_funnel_reportMCP tools). - (If implemented)
src/refstored on the new user/org for Studio-side attribution.
Security notes
- The callback must be server-to-server. Do not embed
CONCIERGE_MCP_AUTH_TOKENin client JS. refis 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
refacross OAuth — first-party cookie set on/signup, or encode into the OAuthstate? - Should we also forward
utm_*params for completeness, or justsrc/ref?
Filed by the Concierge team. Concierge side (link emission, lead store, /event + record_conversion) is already live on main in decocms/concierge.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at Studio's /signup flow and the Better Auth account-creation callback; trace how attribution survives email verification and OAuth redirects. Implement the server-side Concierge /event call using the server secret, then verify no call occurs without ref, signup is not blocked by callback failure, and a known ref is marked signed_up.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, typescript
- Domain
- api, authentication, backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100