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

SetWhereCheck
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.prodSign-in from the dashboard works; the website's forms submit
NEXT_PUBLIC_SITE_URL, NEXT_PUBLIC_PLATFORM_URL, NEXT_PUBLIC_API_URLwebsite build environmentThe site's Open the dashboard and Log in links go to the real dashboard; sitemap.xml uses the real domain
Brand domain and emailscripts/rebrand/brand.sonic.json in the websiteNo .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.

SetWhereCheck
EMAIL_BACKEND=smtp, EMAIL_FROM, SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD.env.prodSign 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 domainDNS, with the email providerThe test email is not in spam; the provider's dashboard shows DKIM passing
BRAND_NAME.env.prodIt is the name in every subject line and signature
REQUIRE_VERIFIED_EMAIL=true.env.prodAn 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

SetWhereCheck
PAYMENT_PROVIDER (razorpay or stripe; mock is refused in production) and its keys and webhook secret.env.prodA 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 providerprovider dashboardPOST /billing/webhooks/<provider> receives the event; a wrong signature is refused
SIGNUP_CREDIT, LOW_BALANCE_THRESHOLD.env.prodA new workspace shows the credit; the low-balance email arrives when the wallet crosses the threshold
A refundprovider dashboard + POST /admin/billing/payments/{id}/refundThe 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)

SetWhereCheck
TELEPHONY_MODE=live, DEFAULT_ENGINE=own.env.prodA 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 /providersAn outbound call to your own phone connects and the status callback arrives
The Exotel flow (Voicebot → Passthru → Connect / Hangup) and its app idExotel App BazaarAn 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.prodThe engine refuses a connection from an address not on its list
DLT registration, headers and the number series for commercial callswith Exotel / the access providerThe 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

SetWhereCheck
The price book: all-in per-minute rates (phone line included), the web-call rate, plans and minimumsapp/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=200GET /billing/price-book returns your numbers; Billing in a workspace shows them; an agent's per-minute estimate matches
The website's pricing pagenode scripts/export_pricing.mjs in the website, then rebuildThe page's numbers match the book
Per-workspace prices for enterprise customersPUT /admin/billing/accounts/{id}/price-overridesThe 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.

SetWhereCheck
BILLING_SELLER_NAME, BILLING_SELLER_TAX_ID (GSTIN), BILLING_SELLER_ADDRESS, BILLING_SELLER_STATE, BILLING_SELLER_STATE_CODE.env.prodThey 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.prodThe 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.prodAccountant 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.prodThe first invoice is <prefix>/2627/00001 in 2026-27
Customers' tax detailseach workspace, Billing → Tax detailsA 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 casesAsk the accountant whether any customer needs them before launch
SetWhereCheck
Terms, privacy, refunds, acceptable use, DPA outline, cookie noticecontent/legal/*.md in the website; drafts at /legalA lawyer has reviewed them; every [placeholder] is filled; the Lawyer: and Accountant: notes are resolved
Grievance officer name and contactthe privacy policyThe named person knows they are named
Once approvedsrc/app/robots.ts and src/lib/seo.ts in the websiteRemove 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

SetWhereCheck
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_TOKENcron 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 healththe ops settings, when presentA deliberate stop of a model server produces one email to the operator
METRICS_TOKEN and a scrape of GET /metrics.env.prod and your monitoringRequest counts and call counts move

9. Before the first customer

  • RECORDING_RETENTION_DAYS decided 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.