# SMS WaboSign sends signing-invitation SMS via configurable providers. v1 ships [BulkVS](https://www.bulkvs.com) only; the architecture leaves room for additional providers behind the same `Sms.send_message` interface. ## What you get - A self-serve `/settings/sms` page where admins paste their BulkVS Basic Auth header and From number. - A **Send test** card on the same page for verifying the config with a one-off SMS to any phone number. - Per-submitter **Send SMS** button on the submission detail page (`/submissions/:id`). - "Send SMS on save" toggle in the submitter edit dialog — already wired into `SubmittersController#maybe_resend_email_sms`, now actually fires the job. - Per-account body template override at `/settings/personalization` → "Signature request SMS". Supports `{account.name}`, `{submitter.link}`, `{submitter.name}`, `{submitter.first_name}`, `{submission.name}`, `{sender.name}` and the rest of the existing `ReplaceEmailVariables` vocabulary. - Sidekiq job (`SendSubmitterInvitationSmsJob`, retry: 5) so a failed BulkVS request retries on its own. ## Configuring BulkVS In the [BulkVS portal](https://portal.bulkvs.com/), open the **API** tab and copy the pre-encoded **Basic Auth Header** value (it's a single base64 string like `dXNlcjp0b2tlbg==`; do NOT include the literal `Basic ` prefix). In WaboSign: 1. Sign in as an admin → **Settings** → **SMS**. 2. Toggle **Enable SMS** on. 3. Paste the BulkVS Basic Auth token. 4. Set **From Number** in E.164 (digits-only with country code, e.g. `15551234567`). 5. *(Optional)* set the **Delivery Status Webhook** to a URL BulkVS will POST status events to. WaboSign does not yet process these inbound events — the field is stored on the config and forwarded to BulkVS so the receipts flow somewhere of your choosing. 6. Save. 7. Use the **Send test** card to verify with a number you own. Errors from BulkVS (bad credentials, malformed number, etc.) come back as `Sms::ProviderError` and are shown inline. ## How sending happens ``` User clicks "Send SMS" on /submissions/:id → SubmittersSendSmsController#create → SendSubmitterInvitationSmsJob.perform_async (Sidekiq, retry: 5) → Sms.send_message(account:, to:, text:) → Sms::Providers::Bulkvs#deliver → HTTPS POST to https://portal.bulkvs.com/api/v1.0/messageSend → returns BulkVS JSON → SubmissionEvent.create!(event_type: 'send_sms') → Submitter.sent_at ||= Time.current ``` The "send SMS on save" toggle in the submitter edit dialog takes the same code path via [SubmittersController#maybe_resend_email_sms](app/controllers/submitters_controller.rb). Body substitution runs through the existing [`ReplaceEmailVariables`](lib/replace_email_variables.rb) module. The account-level template at `submitter_invitation_sms` overrides the i18n default `submitter_invitation_sms_body_sign` when set. ## Adding another provider Two-step extension: 1. **Implement the provider class** at `lib/sms/providers/.rb`. The interface is `#new(config)` + `#deliver(to:, text:, webhook: nil)`. Raise `Sms::ProviderError` on non-2xx responses. See [`lib/sms/providers/bulkvs.rb`](lib/sms/providers/bulkvs.rb) for shape. 2. **Register the provider** in three places: - `Sms::SUPPORTED_PROVIDERS` in [`lib/sms.rb`](lib/sms.rb). - The `case provider` switch in `Sms.send_message`. - The `