Engine & deployment
Operator checklist
What the operator sets before launch, in order: domains, email, payments, telephony, prices, tax details, legal pages, status and alert emails.
For the person running the platform, not for customers. Each item names the setting (.env.prod unless said otherwise), what it needs, and how to check it. Nothing here is done for you by the software.
Warning: The platform ships with placeholders everywhere a business decision is needed: prices, tax details, legal text, the sender address of emails. A deployment that skips this list will send emails from
no-reply@localhost, charge placeholder prices and show draft legal pages.
1. Domains and TLS
| Set | Where | Check |
|---|---|---|
API_DOMAIN, DASHBOARD_DOMAIN, ACME_EMAIL | .env.prod (the proxy issues certificates) | https://api.<domain>/ready answers {"status": "ready"} |
PUBLIC_BASE_URL, DASHBOARD_URL, CORS_ORIGINS, SITE_ORIGINS | .env.prod | Sign-in from the dashboard works; the website's forms submit |
NEXT_PUBLIC_SITE_URL, NEXT_PUBLIC_PLATFORM_URL, NEXT_PUBLIC_API_URL | website build environment | The site's Open the dashboard and Log in links go to the real dashboard; sitemap.xml uses the real domain |
| Brand domain and email | scripts/rebrand/brand.sonic.json in the website | No .example address is left on the site (the footer, structured data, docs examples) |
2. Email
Without a mail server, sign-up verification and password reset do not work and nobody is told about invoices or low balance.
| Set | Where | Check |
|---|---|---|
EMAIL_BACKEND=smtp, EMAIL_FROM, SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD | .env.prod | Sign up with a real address; the verification email arrives, from the right sender, and the link opens the dashboard |
| SPF, DKIM and DMARC records for the sending domain | DNS, with the email provider | The test email is not in spam; the provider's dashboard shows DKIM passing |
BRAND_NAME | .env.prod | It is the name in every subject line and signature |
REQUIRE_VERIFIED_EMAIL=true | .env.prod | An unverified workspace cannot place outbound calls |
The wording of every email is in app/services/email_templates.py; read them once as a customer would.
3. Payments
| Set | Where | Check |
|---|---|---|
PAYMENT_PROVIDER (razorpay or stripe; mock is refused in production) and its keys and webhook secret | .env.prod | A top-up in the provider's test mode goes through end to end: payment page, webhook, wallet credited, receipt email |
| Webhook URL registered with the provider | provider dashboard | POST /billing/webhooks/<provider> receives the event; a wrong signature is refused |
SIGNUP_CREDIT, LOW_BALANCE_THRESHOLD | .env.prod | A new workspace shows the credit; the low-balance email arrives when the wallet crosses the threshold |
| A refund | provider dashboard + POST /admin/billing/payments/{id}/refund | The ledger shows the refund and the balance drops |
Neither payment adapter has taken real money yet. Do one real small payment and one real refund yourself before customers do.
4. Telephony (Exotel)
| Set | Where | Check |
|---|---|---|
TELEPHONY_MODE=live, DEFAULT_ENGINE=own | .env.prod | A simulated call runs on the own engine |
EXOTEL_API_KEY, EXOTEL_API_TOKEN, EXOTEL_ACCOUNT_SID, EXOTEL_APP_ID, EXOTEL_SUBDOMAIN | .env.prod (platform defaults) or per workspace through POST /providers | An outbound call to your own phone connects and the status callback arrives |
| The Exotel flow (Voicebot → Passthru → Connect / Hangup) and its app id | Exotel App Bazaar | An inbound call to the ExoPhone reaches the agent; a transfer reaches a person |
ENGINE_PUBLIC_WS_URL, the engine's Exotel user and password, ENGINE_SHARED_SECRET, the engine's allow-lists | .env.prod and .env.voice.prod | The engine refuses a connection from an address not on its list |
| DLT registration, headers and the number series for commercial calls | with Exotel / the access provider | The compliance toolkit's DND check uses the list you upload: no official registry is connected |
See Exotel for what has and has not been proven on a live account.
5. Prices
| Set | Where | Check |
|---|---|---|
| The price book: all-in per-minute rates (phone line included), the web-call rate, plans and minimums | app/data/price_book.json (or PRICE_BOOK_FILE). The owner-approved launch book (2026-10-19) is app/data/price_book.launch.json; it is not active until you copy it over or point PRICE_BOOK_FILE at it, and set SIGNUP_CREDIT=200 | GET /billing/price-book returns your numbers; Billing in a workspace shows them; an agent's per-minute estimate matches |
| The website's pricing page | node scripts/export_pricing.mjs in the website, then rebuild | The page's numbers match the book |
| Per-workspace prices for enterprise customers | PUT /admin/billing/accounts/{id}/price-overrides | The workspace's price book says "custom": true |
Everything else about the numbers (what a minute costs us, what the market charges, the proposed plans) is in docs/PRICING_DRAFT.md.
6. Tax details and invoices
Decide these with your accountant; the platform renders them, it does not know them.
| Set | Where | Check |
|---|---|---|
BILLING_SELLER_NAME, BILLING_SELLER_TAX_ID (GSTIN), BILLING_SELLER_ADDRESS, BILLING_SELLER_STATE, BILLING_SELLER_STATE_CODE | .env.prod | They print at the top of a test invoice (uv run python -m scripts.issue_invoice <email> --period YYYY-MM) |
BILLING_TAX_RATE_PERCENT (18 for GST on this kind of service, to confirm), BILLING_TAX_MODE | .env.prod | The invoice shows CGST + SGST for a buyer in your state and IGST otherwise |
BILLING_HSN_SAC: the service accounting code on every line. The platform default is 998319 (other IT services); 998314 (IT design and development) and 998315 (hosting and infrastructure provisioning) are also used for software platforms | .env.prod | Accountant to confirm which code applies and whether one code covers the telephony lines too |
INVOICE_PREFIX: the series, numbered <prefix>/<financial year>/<sequence> without gaps | .env.prod | The first invoice is <prefix>/2627/00001 in 2026-27 |
| Customers' tax details | each workspace, Billing → Tax details | A buyer's GSTIN and state code print on the invoice and decide the tax split |
| Not implemented: credit notes, e-invoicing (IRN), export or reverse-charge cases | Ask the accountant whether any customer needs them before launch |
7. Legal pages
| Set | Where | Check |
|---|---|---|
| Terms, privacy, refunds, acceptable use, DPA outline, cookie notice | content/legal/*.md in the website; drafts at /legal | A lawyer has reviewed them; every [placeholder] is filled; the Lawyer: and Accountant: notes are resolved |
| Grievance officer name and contact | the privacy policy | The named person knows they are named |
| Once approved | src/app/robots.ts and src/lib/seo.ts in the website | Remove the legal pages from the robots disallow list, add them to the sitemap, and link them from the dashboard's sign-up page |
8. Status page and alert emails
| Set | Where | Check |
|---|---|---|
PLATFORM_ADMIN_TOKEN | .env.prod (reachable on the internal network only) | GET /admin/billing/price-book with X-Admin-Token answers |
The status publisher: node scripts/publish_status.mjs in the website, every two minutes, with API_URL and PLATFORM_ADMIN_TOKEN | cron on the site host | /status shows "All systems operational" with a recent published time; scripts/status.notes.json is where you post a notice |
| Alert emails for uptime and model health | the ops settings, when present | A deliberate stop of a model server produces one email to the operator |
METRICS_TOKEN and a scrape of GET /metrics | .env.prod and your monitoring | Request counts and call counts move |
9. Before the first customer
RECORDING_RETENTION_DAYSdecided and written in the privacy policy.- The demo voice on the website cleared for use, or the "Hear it speak" button left hidden.
- The sk_dummy workspace and any test numbers removed from the production database.
- A backup restored once on a scratch machine (Deployment).
- The security notes walked through.