> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spitshake.io/llms.txt
> Use this file to discover all available pages before exploring further.

# SMS Verification

> Add SMS-based one-time password verification to your signing flows using Twilio.

## Overview

SMS verification adds an extra layer of identity assurance by requiring signers to confirm their phone number via a one-time password (OTP) before completing a submission. Codes expire after 5 minutes and are tied to a specific submission.

## White-label SMS copy

Account settings support `sms_sender_name`, `sms_invitation_template`, and
`sms_otp_template`. Invitation templates may use `{{company_name}}` and must include
`{{signing_url}}`; OTP templates may use `{{company_name}}` and must include `{{code}}`.
Templates are limited to 480 characters, and the final rendered message is limited to 320
characters. Control characters are removed before the rendered length is checked.

Use `PUT /api/settings` with top-level `"preview": true` to validate and render both messages
without saving. Preview uses the account's current brand and domain with a representative
24-character signing slug. The same 320-character check runs again when a message is sent, so
later brand or domain changes cannot produce an oversized SMS.

## Step 1: Configure SMS Provider

Set up your Twilio credentials. This only needs to be done once per account.

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://spitshake.io/api/verify/update_config" \
    -H "X-Auth-Token: YOUR_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "sms_provider": "twilio",
      "twilio_account_sid": "ACe1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6",
      "twilio_auth_token": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
      "twilio_phone_number": "+15559876543"
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "success": true,
    "message": "SMS verification configuration saved successfully",
    "provider": "twilio"
  }
  ```
</ResponseExample>

## Step 2: Check Configuration

Verify that SMS verification is properly configured before initiating a flow.

<RequestExample>
  ```bash cURL theme={null}
  curl -X GET "https://spitshake.io/api/verify/config" \
    -H "X-Auth-Token: YOUR_API_TOKEN"
  ```
</RequestExample>

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "configured": true,
    "provider": "twilio"
  }
  ```

  ```json 200 — Not Configured theme={null}
  {
    "configured": false,
    "provider": null
  }
  ```
</ResponseExample>

## Step 3: Send OTP

Send a 6-digit verification code to the signer's phone number. The code is valid for 300 seconds (5 minutes).

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://spitshake.io/api/verify/{submission_id}/send_otp" \
    -H "X-Auth-Token: YOUR_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "phone": "+15551234567"
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "success": true,
    "message": "Verification code sent",
    "expires_in": 300
  }
  ```

  ```json 422 — Invalid Phone theme={null}
  {
    "success": false,
    "error": "Invalid phone number format. Please use E.164 format (e.g., +15551234567)."
  }
  ```

  ```json 429 — Rate Limited theme={null}
  {
    "success": false,
    "error": "Too many verification attempts. Please wait 60 seconds before requesting a new code."
  }
  ```
</ResponseExample>

## Step 4: Verify OTP

Submit the 6-digit code the signer received. On success, the verification result is stored in the submitter's values.

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://spitshake.io/api/verify/{submission_id}/verify_otp" \
    -H "X-Auth-Token: YOUR_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "phone": "+15551234567",
      "code": "123456",
      "field_uuid": "f1a2b3c4-d5e6-7890-abcd-000000000001"
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 — Verified theme={null}
  {
    "success": true,
    "verified": true
  }
  ```

  ```json 200 — Invalid Code theme={null}
  {
    "success": false,
    "verified": false,
    "error": "Invalid verification code. Please check the code and try again."
  }
  ```

  ```json 200 — Expired theme={null}
  {
    "success": false,
    "verified": false,
    "error": "Verification code has expired. Please request a new code."
  }
  ```
</ResponseExample>

### Stored Verification Data

After successful verification, the following object is saved to the submitter's `values` hash under the corresponding field UUID:

```json theme={null}
{
  "phone": "+15551234567",
  "verified_at": "2026-04-09T14:30:00Z",
  "method": "sms_otp"
}
```

This data is included in the submission response and audit trail, providing a permanent record that the signer verified their phone number.

## Step 5: Check Verification Status

Poll the current status of a pending verification. Useful for showing countdown timers in the signing UI.

<RequestExample>
  ```bash cURL theme={null}
  curl -X GET "https://spitshake.io/api/verify/{submission_id}/status?phone=%2B15551234567" \
    -H "X-Auth-Token: YOUR_API_TOKEN"
  ```
</RequestExample>

<ResponseExample>
  ```json 200 — Pending theme={null}
  {
    "pending": true,
    "expires_in": 245
  }
  ```

  ```json 200 — Not Found theme={null}
  {
    "pending": false,
    "expires_in": 0
  }
  ```

  ```json 200 — Already Verified theme={null}
  {
    "pending": false,
    "verified": true,
    "verified_at": "2026-04-09T14:30:00Z"
  }
  ```
</ResponseExample>

## Complete Flow Diagram

```
┌──────────────────┐
│  Signer reaches  │
│  phone field     │
└────────┬─────────┘
         │
         ▼
┌──────────────────┐      ┌──────────────────┐
│  POST send_otp   │─────▶│  Twilio delivers │
│  phone: +1...    │      │  6-digit SMS     │
└────────┬─────────┘      └──────────────────┘
         │
         ▼
┌──────────────────┐
│  Signer enters   │
│  code in UI      │
└────────┬─────────┘
         │
         ▼
┌──────────────────┐      ┌──────────────────┐
│  POST verify_otp │─────▶│  Result stored   │
│  code: 123456    │      │  in submitter    │
└────────┬─────────┘      │  values          │
         │                └──────────────────┘
         ▼
┌──────────────────┐
│  Signing flow    │
│  continues       │
└──────────────────┘
```

## Phone Number Format

All phone numbers must use [E.164 format](https://en.wikipedia.org/wiki/E.164):

| Format           | Valid |
| ---------------- | ----- |
| `+15551234567`   | Yes   |
| `+442071234567`  | Yes   |
| `+61412345678`   | Yes   |
| `555-123-4567`   | No    |
| `(555) 123-4567` | No    |
| `5551234567`     | No    |
