Appearance
Tenant communication model — branding, isolation and credits
Can a merchant or enterprise customer have WhatsApp messages reach their customers under their branding, orchestrated by QRSETU — and can QRSETU sell prepaid communication credits against it?
Both are possible, and the answer is better than "pick one" — but not in the shape it first appears. A customer's branding requires a WhatsApp account owned by that customer; QRSETU billing for their traffic requires QRSETU's credit line attached to it, which Meta grants only to Solution Partners. Those two facts combine rather than conflict, and everything else on this page follows from them.
Meta capabilities verified against live documentation on 2026-08-22; repo facts measured against the live migration set the same day. Governing decisions: ADR-0029 · ADR-0030.
The four constraints that decide the whole design
1 · What isolates, and what does not
| Scoped at | What lives there | Isolates between tenants? |
|---|---|---|
| Phone number | display name (verified_name), business profile including logo, description, address, website (POST /{phone_number_id}/whatsapp_business_profile), quality rating, OBA badge, message-webhook override | ✅ yes |
| WABA (the account) | message templates, policy enforcement, ban state | ❌ no |
| Business portfolio | messaging limits — moved from per-number to per-portfolio on 2025-10-07 | ❌ no |
- Templates have no phone-number scope.
POST /{waba-id}/message_templatestakes no phone-number parameter andGEToffers no filter, so on a shared account tenant A's templates are visible and sendable by tenant B, in one namespace wherenamemust be unique. - Capacity is shared portfolio-wide. Meta's words: "it's possible for one number to consume all of the portfolio's messaging capability."
- Enforcement is account-level — the field is
waba_ban_state, and the ladder includes "a 5, 7, or 30 day block on sending any messages."
2 · A WABA per client is contractually mandated, not merely advisable
This is the constraint that settles the shared-account question, and it is a signed obligation rather than a review heuristic. Meta's Terms for Service Providers:
"You are solely responsible for (a) creating a WABA account for each Client"
"You may not create a WABA on behalf of your Client without that Client's request and consent."
"you must reasonably and in a timely manner (not to exceed 30 calendar days following your Client's request) assist your Client to transfer the WABA account"
The 30-day portability clause is independently decisive: an account holding many merchants' numbers cannot be transferred to any one of them, so the first merchant who asks to leave puts the platform in breach it cannot cure.
3 · Who Meta invoices — and the one mechanism that moves it
Meta bills the owner of the account. The single documented exception is a Solution Partner's credit line attached to a client-owned account, after which, in Meta's words:
"You are the 'Bill To Party' for all businesses sharing your credit line. You are liable for and will pay Meta for all WhatsApp Business Platform spend made by these businesses."
That is the resale mechanism, and it is a real API — POST /{EXTENDED_CREDIT_LINE_ID}/whatsapp_credit_sharing_and_attach, with INR among the supported currencies. Tech Providers do not have credit lines; their onboarded customers must attach their own payment method and Meta bills them directly.
4 · ⚠ Attaching a credit line is irreversible in substance
"Credit lines cannot be changed after being attached to a WABA. If the WABA needs a different credit line, a new WABA must be created."
Revoking is possible; re-pointing is not. Moving a merchant between billing arrangements means a brand-new account — new number registration, new display-name review, forfeited quality history and messaging tier. So the billing model has to be chosen before the first customer is onboarded, which makes this a decision with a deadline rather than an option to keep open.
The tiers
| T1 · Platform | T2 · Merchant-branded content | T3 · Customer account, customer billed | T4 · Customer account, QRSETU billed | |
|---|---|---|---|---|
| Sender shown | QRSETU | QRSETU | the customer's | the customer's |
| Account owner | QRSETU | QRSETU | the customer | the customer |
| Who Meta invoices | QRSETU | QRSETU | the customer | QRSETU ("Bill To Party") |
| QRSETU can sell credits | ✅ for our own sends | ✅ for our own sends | ❌ | ✅ this is the resale tier |
| Isolation (templates/capacity/ban) | n/a | n/a | ✅ full | ✅ full |
| QRSETU's Meta status needed | direct developer | direct developer | Tech Provider (Advanced Access App Review) | Solution Partner, or Tech Provider paired via Multi-Partner Solutions |
| Customer must do | nothing | nothing | own business verification, attach own card | own business verification |
| Suits | all merchants, R1 | all merchants, R1 | enterprises happy to hold their own Meta billing | enterprises wanting one bill from QRSETU |
T2 does most of the work and costs nothing. "Balaji Murti Kendra: your order QR6D7M is ready for collection", sent from QRSETU's number, is fully supported today — brand_name already reaches workspaces and setu_cards at provisioning (provision_merchant_workspace), so the merchant's identity is a template variable with no Meta work and no new schema. For a solo vendor this is very likely the entire requirement.
T4 is the answer to the owner's question as asked: the customer's own verified identity in the chat, and one bill from QRSETU that prepaid credits can be deducted against. It needs Solution Partner status — or, at lower cost, Multi-Partner Solutions, where a Tech Provider pairs with an existing Solution Partner who supplies the credit line while QRSETU supplies the software. That trades a revenue split for skipping an approval programme with no published criteria.
⚠ Rejected: many customers' brands on QRSETU's own account
The apparent shortcut — a dedicated number per customer inside QRSETU's account, carrying their name and logo — fails on four independent grounds, and it is worth stating all four because the first one alone is often read as a loophole:
- The impersonation policy has a real but narrow exception. It bars presenting another business "without permission" — so authorisation matters. But the Display Name Guidelines then require that "the relationship between the business represented in the display name and end-client business must be evident and clear in both parties' business websites". Unsatisfiable at scale for stall vendors, tutors and electricians. (Meta's canonical guideline page is JS-rendered and could not be fetched directly; the text is reproduced identically by three independent partner mirrors citing that exact URL — high confidence, not first-party verified.)
- Constraint 2 above: a WABA per client is contractually required, and the 30-day portability clause cannot be honoured from a shared account.
- No isolation: shared template namespace, portfolio-wide capacity,
waba_ban_state— one tenant's violation can silence every merchant. - Meta enforces this at review. A documented case: a portfolio registered as "Tolaram Grp" applied for the display name "Colgate" and was rejected; the remedy was a separate account under the correctly-named entity.
⚠ Note what makes this dangerous rather than merely wrong: the mechanism permits it. Display names are genuinely per-number and settable, so a shared-account design builds, deploys and works until enforcement arrives. It fails on policy and contract, not on API.
Credits, and what actually blocks them
What already exists — the half people expect to be missing
Usage limits and controls need no new mechanism. feature_grants already carries limit_value, limit_period and on_exceed ('block_new' | 'read_only' | 'grace_period') at eight scopes with defined precedence (platform → archetype → industry → plan → workspace_group → workspace → workspace_member → user). Plan differentiation is configuration against a built table.
⚠ And in R1 it is an availability control, not a pricing lever. Because every merchant sends from QRSETU's portfolio at T1/T2, and Meta's messaging limit is portfolio-wide, a per-workspace cap is the only thing standing between one merchant's campaign and every merchant's order confirmations. It must exist before the first campaign runs. (At T3/T4 the problem dissolves — each customer has their own portfolio.)
The audit trail exists — audit_log, append-only, actor_kind covering user | system | support | webhook.
The placement exists — PlanBilling.dc.html (both consoles) is already the destination behind the Settings plan row. Its own discipline is the one credits must inherit: it removed the download-invoice button because there is no invoice document in the product, and it refuses to guess a UPI handle.
What does not exist — and one of these is decisive
THE HARD PREREQUISITE: QRSETU CANNOT CHARGE A MERCHANT AT ALL TODAY
Model A (a merchant pays Digious) has no collection path. The payments status page records it plainly: the ₹9,999 Business plan "is taken out of band and the subscription row inserted by hand" — acceptable for twelve known vendors, and not a payment module. Prepaid credits are Model A by definition, so they are blocked on the same unbuilt path as self-serve subscriptions. A wallet built before it would be untoppable.
There is no Ledger and no Balance table. Measured: 51 tables in the live migration set, neither primitive among them — though both are among the eleven process primitives.
⚠ Credits would be the fourth design waiting on that primitive, which is what should decide how it gets built:
| Design | Where | Wants |
|---|---|---|
commission_ledger | ADR-0005 | append-only cash accrual per partner |
points_ledger | ADR-0005 | append-only earn/redeem wallet per profile |
| loyalty points | ADR-0025 | Ledger + Balance per Party |
| communication credits | this page | append-only purchase/spend per workspace |
ADR-0025 already wrote the condition and called it "the single cheapest forward-compatibility decision available": Ledger and Balance must carry a unit code, not a hardcoded CHECK (currency = 'INR'). Credits are a fourth unit. Build the primitive once with a unit dimension; never a bespoke communication_credits table — four private ledgers is the duplicate-source-of-truth class (QRS-249) chosen at design time.
The credits shape, when it is built
Append-only, balance derived — never a mutable balance column, for the same reason orders.payment_status is derived from the payments ledger rather than set from an event:
ledger_entries—workspace_id,unit(INR/MSG_CREDIT/POINTS),delta(signed; purchases positive, sends negative),reason(purchase/send/refund/grant/expiry/adjustment),correlation_type/correlation_id(thecommunication_messagesrow a debit paid for, or thepaymentsrow a purchase came from),idempotency_keyunique. No UPDATE or DELETE policy, per the repo's convention for financial tables.- Balance =
sum(delta)per(workspace_id, unit), materialised only if measurement demands it. Low-balance alerts read the same fold; nothing maintains a second number. - The dispatcher debits inside its existing policy gate. Insufficient balance becomes another
suppressed_reason— the mechanism already designed for "refused, not dropped". - Cost basis is ours. Meta's
pricing_analyticsCOST is reconciled against consumption; a mismatch is an exception to investigate. Meta has no billing API — no payment-method, invoice or top-up endpoint — so a credit balance is entirely a QRSETU construct on top of our postpaid account. It cannot mirror a Meta wallet, because there is no Meta wallet.
⚠ The commercial finding that should temper the whole idea
Volume discounts accrue at the business-portfolio level, reset monthly, and exist for utility and authentication only — never marketing. Because each customer at T3/T4 owns their own portfolio, volume never pools: a platform pushing large aggregate traffic across many merchants earns no aggregate discount, and every merchant sits alone in the lowest tier permanently. Marketing — the category a vendor actually wants — has no volume tier at all.
So reselling messaging has no wholesale arbitrage in it. Any margin on credits must come from the service (orchestration, templates, analytics, support), priced deliberately, not from buying cheaply and selling dearly. Modelling a bulk-rate saving would be modelling something Meta does not offer.
Operational facts to plan around, not discover
- Every customer starts at 250 unique recipients/24h, and QRSETU's own verification does not help them — limits are never inheritable. Tiers are 250 → 2,000 → 10,000 → 100,000 → Unlimited (there is no 1,000 tier).
- Partner-Led Business Verification lets a partner submit a client's documents and reach 2,000 quickly — but it is Select/Premier Solution Partners only, capped at three submissions per client, and it is not inheritance: the client is verified on their own documents.
- Tier progression cannot be promised. Automatic scaling requires using at least half the current limit in 7 days, so a stall vendor never advances past 2,000 regardless of quality.
- Onboarding throughput is capped: 10 new customers per rolling 7 days by default, rising to 200 after Business Verification, App Review and Access Verification — three distinct steps.
- OBO (partner-owned "on behalf of" accounts) is dead — deprecated, with existing accounts auto-migrated to clients through end-2025. There is no model where QRSETU owns a customer's asset.
- Embedded Signup v2 is deprecated 2026-10-15 — build against v4, which onboards Cloud API, Marketing Messages and Ads-that-Click-to-WhatsApp in one consent flow.
- Customers own their assets absolutely: "you cannot restrict this access in any way", and they may work with several partners at once. No lock-in is available in either direction.
- Coexistence keeps an enterprise's existing WhatsApp Business App history (up to six months synced) on the same number — partner-only, capped at 20 messages/second, with broadcast lists and view-once disabled. Without it, migrating a number deletes the app account and permanently loses chat history, which is exactly the surprise that sinks an enterprise onboarding.
- Template and account webhooks cannot be routed per tenant. Message traffic supports per-WABA and per-number overrides;
message_template_status_update,template_category_updateandaccount_updatealways land on the app's default callback URL. QRSETU fans them out itself bywaba_id, at every tier — already the shape ADR-0029's webhook EF takes. - Marketing Messages API (formerly "MM Lite") is GA, India-eligible, reports up to ~9% better marketing delivery than Cloud API, and requires a client-owned account — so a correctly built T3/T4 gets it by construction. No pricing discount; do not model a saving.
Complexity, honestly compared
| T1 + T2 | T3 (customer billed) | T4 (QRSETU billed) | Credits layer | |
|---|---|---|---|---|
| Meta programme | none | Tech Provider: business verification + Advanced Access App Review + Embedded Signup v4 | T3 plus Solution Partner status (no published criteria) or a Multi-Partner pairing | none |
| Customer-side burden | none | own verification + attach a card to Meta | own verification | none |
| New schema | none beyond ADR-0029 | tenant account/number/token rows (columns already reserved) | as T3 | Ledger + Balance (shared with 3 designs) |
| New payment capability | none | none | none | Model A collection — does not exist |
| Ongoing ops | one account | per-customer template queues, quality, verification chasing, 10-200/week onboarding cap | as T3 + credit exposure, float, dunning | reconciliation, expiry, disputes |
| Reversibility | high | high | ⚠ credit-line attachment is one-way | — |
The asymmetry worth naming: T3/T4's cost is operational and commercial, not architectural. The schema already carries workspace_id on every message and owner_workspace_id on every account row, and the dispatcher resolves its sender per message — so the code absorbs a tenant sender without redesign. What does not absorb cheaply is a two-person team running per-customer Meta account administration, and a credit line QRSETU is legally liable for.
Recommendation
Ship T1 + T2 in R1. Do not attempt a shared account with customer branding. Treat T4 as the enterprise offering when there is a named enterprise, and sequence credits behind Model A.
The commercial argument comes from the platform's own strategy assessment rather than from enthusiasm: the enterprise motion is put there at 0-3 deals in twelve months (₹0-10 lakh) against 20-40 team/upline deals worth ₹20-45 lakh, and enterprise already carries a heavier unbuilt prerequisite in the org-admin portal, which does not exist. Add that a T4 offering requires Solution Partner status with no published qualification path, and the sequencing is clear: the capability is real, the demand is not yet.
Three things to do now, all cheap:
- Make merchant-branded content the default for every transactional message (T2).
- Build the per-workspace send cap before the first campaign — at T1/T2 it is an availability control, and it is the one item on this page that is not deferrable.
- Decide the eventual billing model before onboarding any customer onto their own account, because credit-line attachment cannot be re-pointed later without recreating the account. Writing the decision down costs nothing today and is expensive to postpone.