external_id.
This page covers the core model. Once it’s in place, the recipes show how to build features on top without fighting rate limits:
Tenant messaging
Retry-safe sends for every customer: idempotency, templates, scheduling
Tenant usage dashboards
Show each customer their consumption and cost from the usage API
Tenant inbox
Conversations per customer: webhook-first, replies, embeds
Building a product, or reselling LazyApp? This section is for platform teams: you call
/v1/platform/* from your own app, and your platform wallet pays ₹999 a month per connected customer number. If you want to put your brand on the LazyApp console and charge businesses your own monthly price, without writing code, see Resellers.Step 1: Create a customer per tenant
When a customer signs up in your app, create their LazyApp customer and store the returnedid (or just keep using your own external_id — LazyApp accepts either).
external_id is your own account id, so you never need a mapping table. Look them up later with GET /v1/platform/customers?external_id=acct_8812.
Creating a customer costs nothing. What you pay LazyApp is ₹999 a month for each customer number that connects, covered in How platform billing works.
markup_bps, pricing_mode, category_markups and platform_fee are no longer stored or returned. LazyApp adds nothing to Meta’s rates, so there is no per-customer markup to configure. A request that still sends them succeeds, and the values are ignored.Step 2: Let them connect WhatsApp
Create a setup link so they connect through Meta’s Embedded Signup. You never handle their credentials.url to your customer — email it, or put it behind a Connect WhatsApp button. The host is LazyApp’s domain unless you set a public connect host under Branding. That value is an origin (https://connect.yourapp.example). Point it at LazyApp so /connect/{token} resolves, and the page shows your brand name, logo, and accent. Meta’s own signup screen still says Facebook.
Subscribe to customer.connected to learn about new connections server-side. Platform deliveries include a customer envelope with your external_id:
setup_link.failed and connection.attention_required / connection.disconnected so you can prompt a reconnect.
The whole onboarding flow, end to end:
Do not trust the redirect alone — it is UX only. Confirm from the webhook. Details in setup links.
Step 3: Send on their behalf
Two patterns. Pick one:Mint a scoped key
scopes to grant the defaults for a customer-scoped key.
Or act with the platform key
X-LazyApp-Customer accepts public_id, uuid, or external_id. The key needs customers:act (send only) or customers:write (full management). See Authentication.
The messaging recipe covers idempotency, templates, and scheduling: Tenant messaging.
Step 4: Route webhooks back to the right customer
Webhook endpoints are configured once on your platform workspace, not per customer. Register one endpoint and route internally. Every event that originated in a customer’s workspace is also delivered to your platform endpoints, with acustomer envelope:
See Webhooks for delivery, retries, and signature verification, and Tenant inbox for a full handler sketch.
Rate limits with many tenants
One platform API key is enough for your whole integration. LazyApp limits requests per key, not per customer — see rate limits for the ceiling and the headers that report it. Meta separately caps how many unique contacts a number may message in 24 hours; that limit is per connection, reported onGET /v1/capabilities.
That budget serves all your tenants together, which has three practical consequences:
- Prefer webhooks over polling. Most per-tenant “is there anything new?” traffic disappears when webhooks push events to you.
- Run background work as one smooth queue. A sync worker with a small concurrency cap uses the budget steadily. Per-tenant cron jobs that all fire at
:00create bursts. - Fairness between tenants is yours to enforce. LazyApp limits your key, not your customers — if one tenant triggers a huge burst, queue it so it cannot starve everyone else.
429, back off and retry with the same Idempotency-Key. Handle 402 distinctly. It is a billing state, not a transient error: the customer is suspended, its spending limit is reached, or a wallet that has to pay for the send is empty. Alert someone; retrying will not help.
Monitor connection health
WhatsApp credentials fail: Meta reviews, quality issues, or a customer reconnects elsewhere. Build the reconnect loop up front:- Subscribe to
connection.attention_requiredandconnection.disconnected— platform deliveries include thecustomerenvelope. - Prompt that customer to reconnect with a new setup link (
allowed_connection_type: "reconnect"). - As a safety net, periodically call
GET /v1/capabilitieswithX-LazyApp-Customer(or a scoped key) to catch anything a webhook missed.
How platform billing works
A platform team pays LazyApp ₹999 a month for each connected customer number, debited from your platform wallet. Creating customers and minting keys cost nothing, and so do sends on customer-paid numbers. Add money to the wallet from Billing in your platform workspace; see Wallets. In return your team is on the Platform plan: every feature, including the API, webhooks, automations, Flows, payments and catalog, with no caps on members, numbers or broadcast sends. Each managed customer workspace inherits your plan, so a customer never hits a plan limit that your team does not. The console marks the wallet low once it holds less than one month of your number cost.Who pays Meta
LazyApp adds 0% to Meta’s message rates, and platform customers carry no markup. Who pays Meta for a customer’s messages is set once per WhatsApp Business Account, bymeta_billing_mode on the setup link:
Customer-paid numbers are the default because they keep you out of the money flow: Meta invoices your customer, and your wallet only covers the ₹999 per number. See Meta message billing for how Meta charges.
Billing modes
billing_mode decides whose wallet takes a charge when there is one — that is, only for messages LazyApp pays Meta for:
Set
spending_limit_cents to cap what a customer’s sends may cost in a month, and usage_alert_cents to receive customer.usage_alert before they reach it. Both measure total_charge, so on a customer-paid number they never trigger. Read consumption with GET /v1/platform/customers/{customer}/usage.
You bill your own customers however you like, outside LazyApp.
Offboarding a customer
Embedding UI
Rather than rebuilding an inbox, drop LazyApp components into your product with short-lived, origin-locked tokens. See embedded components.Related
- Tenant messaging — retry-safe sends per customer
- Tenant usage — per-customer consumption dashboards
- Resellers — sell LazyApp under your own brand
- Plans and limits — what each plan includes
- Tenant inbox — webhook-first conversations
- Setup links — Embedded Signup
- Idempotency — safe retries
- Rate limits — windows and backoff
- Webhooks — delivery and signatures