decocms / decocms/studio

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

Open
#3,747 1 comment 0 reactions 0 assignees View on GitHub

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

  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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.