# Click-to-Call Widget — Design

## Goal

Provide an embeddable website widget where a visitor enters their phone number,
clicks a button, and Netsapiens calls them back and bridges them into a
domain-specific call queue. Customizable per domain (queue + branding).

## Architecture

Extend the existing webresponder Flask app
(`Netsapiens-Scripts/webresponder/app.py`) with two new routes, rather than
standing up a separate service:

- `GET /widget.js?domain=<domain>` — serves an embeddable JS snippet that
  renders a "Call Me" button + phone-input form, styled per the domain's
  branding config.
- `POST /click-to-call` — receives `{domain, phone_number}`, validates it,
  rate-limits, and calls the NS legacy `queuedcall` API to originate the call
  to the visitor and bridge it into that domain's queue.

This reuses the app's existing `NS_TOKEN` (same Bearer token already used for
the v2 SMS API), logging setup, and `domains.json`-driven multi-tenancy
pattern. No new service/systemd unit to deploy.

## Config schema (extends `domains.json`)

Each domain entry gains new optional fields alongside the existing ones used
by the SMS-directions feature:

```json
"acme.com": {
  "api_domain": "acme.com",
  "queue": "500",
  "branding": {
    "button_text": "Call Us Now",
    "button_color": "#1a73e8",
    "company_name": "Acme Corp",
    "logo_url": "https://acme.com/logo.png"
  }
}
```

`queue` is the NS queue extension the visitor is bridged into (used as both
`uid` and `queue` params in the queuedcall API call).

## `POST /click-to-call` flow

1. Parse `domain` and `phone_number` from the JSON body; look up the domain in
   `domains.json`. Unknown domain → JSON error response (this endpoint is
   called by browser JS, so responses are JSON, not the XML used by the
   inbound webresponder routes).
2. Validate `phone_number`: strip formatting, require it to match a US
   10-digit / E.164 pattern. Reject with 400 if invalid.
3. Rate limit (in-process counters — single-instance Flask, no Redis needed):
   - per source IP: e.g. 3 requests/minute
   - per domain: e.g. 30 requests/hour (caps a customer's call spend)
4. Call the NS legacy API:
   `POST https://ucportal.pressone.net/ns-api/?object=queuedcall&action=create`
   with `Authorization: Bearer {NS_TOKEN}` and a **multipart form-data** body:
   `format=json`, `uid=<queue>`, `queue=<queue>`, `destination=<phone_number>`.
   Success response is **202** text/plain with an empty body (async — NS accepted
   the call origination request for processing).
5. Return `{"status": "ok"}` or `{"status": "error", "message": "..."}` with
   an appropriate HTTP status. Log every attempt (success, validation failure,
   rate-limit hit, NS API error), matching the logging style of the existing
   SMS flow.

## Widget (`GET /widget.js`)

A small vanilla-JS file with no framework dependencies. A customer embeds it
with one tag:

```html
<script src="https://.../widget.js?domain=acme.com"></script>
```

- The script is templated server-side per domain, with that domain's branding
  baked in at request time (button text/color, company name, logo).
- Renders a floating button; clicking opens an inline form for the visitor to
  enter their phone number.
- On submit: client-side format validation, disables the button, POSTs to
  `/click-to-call`, shows a success ("We're calling you now!") or error
  message inline.
- No cookies/tracking, so it can be embedded on any site without privacy
  concerns; note any CSP (`script-src`) requirements customers may need to add.

## Error handling & testing

- Error cases: unknown domain, invalid phone format, rate limit exceeded
  (429), NS API failure (502) — each logged and returned as JSON with a
  user-friendly message for the widget to display.
- Tests mirror `tests/test_app.py`: mock `requests` calls to the NS API, cover
  the happy path, each validation/error branch, and rate-limiting (e.g. a 4th
  request within a minute from the same IP is rejected).
- No new deploy artifacts — same `ns-webresponder.service`; just new routes
  and an extended `domains.json`.
