# Integrations

How to connect Devwebs to the three outside services it uses: **Meta (WhatsApp Cloud API)**, **Razorpay** and an
**SMTP mail server**. Each one has a simulation or "off" mode. Devwebs labels an integration *Not configured*,
rather than pretending it works, until it is set up. Admin → **System health** shows the live status of each.

The Meta and Razorpay steps below were checked against their official documentation in October 2026. Both
companies change their dashboards often. If a screen looks different, follow the linked page; the values Devwebs
needs stay the same.

---

## 1. WhatsApp Cloud API (Meta)

Devwebs uses only Meta's official Cloud API (Graph API, `v26.0` by default). It does not automate WhatsApp Web.
Devwebs is not a Meta Business Solution Partner and does not claim to be: each customer connects a number from a
Meta app they own.

### 1.1 What the customer needs from Meta

1. A **Meta Business portfolio** and a **Business app** in [Meta for Developers](https://developers.facebook.com/apps)
   with the **WhatsApp** product added.
2. A **WhatsApp Business Account (WABA)** with a registered phone number. Note:
   - the **WhatsApp Business Account ID**;
   - the **Phone number ID** (WhatsApp → API Setup). This is not the phone number itself.
3. A permanent **system user access token**, created in Business settings → Users → System users. Give the system
   user the WhatsApp account and the app as assets, then generate a token with these permissions:
   - `business_management`
   - `whatsapp_business_messaging`
   - `whatsapp_business_management`

   Do not use the temporary 24-hour token from the API Setup page; it stops working the next day.
4. The app's **App secret** (App settings → Basic). Devwebs uses it to check that webhooks really come from Meta.

### 1.2 Connecting in Devwebs

The workspace owner opens **WhatsApp setup → Connect a number** and pastes the four values. Devwebs then:

1. checks the number with Meta (`GET /<PHONE_NUMBER_ID>`) and stores nothing if Meta rejects the details;
2. subscribes the app to the WhatsApp account (`POST /<WABA_ID>/subscribed_apps`);
3. shows a **Callback URL**, unique to this number, and a **Verify token**.

In the Meta app, go to **WhatsApp → Configuration → Webhook**:

1. Paste the Callback URL and Verify token, then click **Verify and save**. Meta calls the URL, and Devwebs answers
   only if the token matches.
2. Under **Webhook fields**, subscribe to:
   - `messages`: incoming messages, plus delivery and read statuses with pricing;
   - `message_template_status_update`: template approvals and rejections.

Send a test template from the number's page, then reply from your phone. The reply appears in the inbox within
seconds once the webhook works.

### 1.3 One Meta app, several numbers or workspaces

A Meta app has one app-level callback URL. If a single app serves numbers that are connected to different Devwebs
workspaces (or several numbers in one workspace), give each one its own callback using Meta's **webhook
overrides**:

- **Per WhatsApp account:** `POST /<WABA_ID>/subscribed_apps` with `override_callback_uri` (the number's
  Devwebs Callback URL) and `verify_token`.
- **Per phone number:** `POST /<PHONE_NUMBER_ID>` with a `webhook_configuration` object.

Meta routes each event to the phone number override first, then the account override, then the app URL. You can
make these calls in Meta's Graph API Explorer with the system user token. Each Devwebs callback URL accepts events
for its own number only, so a misrouted event is ignored, never delivered to another workspace.

### 1.4 Rules Devwebs enforces

- **Templates:** only approved templates are sent. **Marketing** templates never go to opted-out contacts.
  "STOP"/"UNSUBSCRIBE" replies opt a contact out at once.
- **Free-form messages:** sent only inside the 24-hour customer service window.
- **Campaign speed:** each number sends at most `CAMPAIGN_MESSAGES_PER_SECOND`. Raise it only if Meta has raised
  your throughput.
- **Credentials:** tokens and app secrets are stored encrypted. Only the last 4 characters are ever shown.
- **Expired or revoked tokens:** they show as **Token rejected** after the next health check, and sending stops.
  The owner pastes a new token under **Update credentials**.
- **Disconnecting:** unsubscribes the app, deletes the stored token and secret, and disables the callback URL.
  Message history is kept.

### 1.5 Simulation mode

`WHATSAPP_PROVIDER=fake` (the default outside production) runs the same code against an in-process imitation of
Meta. The UI shows a banner and lets you simulate incoming messages and delivery statuses. Production uses
`cloud`. The preflight check warns if `fake` is left on.

### 1.6 Platform app (Embedded Signup)

`/api/webhooks/whatsapp` with `WHATSAPP_APP_SECRET` and `WHATSAPP_VERIFY_TOKEN` is ready for numbers onboarded
through a Devwebs-owned Meta app. Embedded Signup itself needs Meta's Tech Provider approval, so it is **not
enabled**. Customers connect with their own app as above.

### 1.7 Message pricing

Meta bills per delivered template message by category and country, and changes its rates. Enter Meta's current
rate card in **Admin → Message pricing**, both your price to customers and your cost. The seed values are examples
only.

---

## 2. Razorpay

### 2.1 Keys and settings

1. In the Razorpay Dashboard, generate **API keys** (Account & Settings → API Keys). Use test-mode keys on a
   staging server and live-mode keys in production.
2. In Devwebs, go to **Admin → Billing settings** and enter:
   - the **Key ID**;
   - the **Key secret**;
   - a **Webhook secret**: any long random string, which you will also give Razorpay.

   They are stored encrypted. The environment variables `RAZORPAY_KEY_ID`, `RAZORPAY_KEY_SECRET` and
   `RAZORPAY_WEBHOOK_SECRET` work too, but the admin settings take precedence.
3. Set `PAYMENTS_PROVIDER=razorpay` (the default in production). Until keys are present, online payments show as
   **Not configured** and customers can pay only from their wallet.
4. Fill in the seller details on the same page: legal name, GSTIN, state code, address and invoice prefix. They are
   printed on GST invoices.

### 2.2 Webhook

In the Razorpay Dashboard → **Webhooks → Add new webhook**:

- **URL:** `https://<your domain>/api/webhooks/razorpay`
- **Secret:** the same Webhook secret as in Devwebs
- **Events:** `payment.captured`, `order.paid`, `payment.failed`, `refund.processed`, `refund.failed`

Devwebs checks `X-Razorpay-Signature` (HMAC-SHA256 of the raw body) on every webhook and drops repeats by
`x-razorpay-event-id`. A wallet is credited only after the server has verified the payment, through the checkout
callback or the webhook, whichever arrives first. The browser is never trusted to credit a wallet.

If your firewall allow-lists inbound webhook sources, Razorpay publishes its webhook IP addresses in its
[IPs and certificates page](https://razorpay.com/docs/security/whitelists/). Razorpay advises validating
signatures even so, and Devwebs always does.

### 2.3 Checkout and the Content Security Policy

Razorpay Checkout loads `https://checkout.razorpay.com/v1/checkout.js` and opens frames from Razorpay domains.
Devwebs' Content Security Policy allows `checkout.razorpay.com` for scripts and `*.razorpay.com` for frames and
connections. Razorpay does not publish an official CSP list. If a checkout ever fails with a CSP error in the
browser console, add the blocked Razorpay host to `next.config.ts` and rebuild the web app.

### 2.4 Checking it works

- Pay a ₹10 recharge with a real card or UPI in live mode.
- The wallet is credited and an invoice appears.
- In Admin → Payments, refund it. The wallet is debited and the status changes to *Refunded* once Razorpay sends
  `refund.processed`.
- Admin → Reconciliation shows no differences.

---

## 3. Email (SMTP)

Devwebs sends:

- password reset links;
- low balance, renewal, payment, campaign and support notifications;
- staff alerts (worker watchdog).

Any SMTP service works: Amazon SES, Postmark, SendGrid, Mailgun, Zoho, Google Workspace and others.

```
SMTP_HOST=email-smtp.ap-south-1.amazonaws.com
SMTP_PORT=587
SMTP_SECURE=false        # true only for port 465
SMTP_USER=...
SMTP_PASSWORD=...
MAIL_FROM=Devwebs <no-reply@crm.example.com>
```

- Use a sender domain with **SPF** and **DKIM** records (and ideally **DMARC**), or messages land in spam.
- Admin → System health → Email → **Check login** tests the connection without sending anything.
- Emails are queued and retried. Sent emails are kept for `RETENTION_EMAILS_DAYS`.
- With no `SMTP_HOST`, email is off and shown as *Not configured*. In-app notifications still work, but **forgot
  password cannot deliver links**, which is why production warns about it.
