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

# SMS Messaging Integration Contract

> This document defines the integration contracts for CE-08 (SMS/Text Messaging Integration). CE-08 integrates with multiple internal modules (CE-01, CE-03, CE-0…

**Version:** 1.0.0\
**Created:** 2026-01-24\
**Status:** ✅ Implemented\
**Spec Reference:** `specs/ce/specs/CE-08-sms-messaging-integration.md`

***

## Overview

This document defines the integration contracts for CE-08 (SMS/Text Messaging Integration). CE-08 integrates with multiple internal modules (CE-01, CE-03, CE-04, PF-10) and an external service (RingCentral SMS API).

***

## Integration Patterns

### Pattern Summary

| Integration           | Pattern                    | Direction           | Status        |
| --------------------- | -------------------------- | ------------------- | ------------- |
| CE-01 (Contacts)      | Direct Reference           | CE-08 → CE-01       | ✅ Implemented |
| CE-03 (Telephony)     | Platform Integration Layer | CE-08 → CE-03       | ✅ Implemented |
| CE-04 (Activities)    | Database Trigger           | CE-08 → CE-04       | ✅ Implemented |
| PF-10 (Notifications) | Platform Integration Layer | CE-08 → PF-10       | ✅ Implemented |
| RingCentral SMS API   | External API               | CE-08 ↔ RingCentral | ✅ Implemented |

***

## Internal Integrations

### CE-01: Contacts Integration

**Pattern:** Direct Reference (Foreign Key)\
**Direction:** CE-08 → CE-01

```sql theme={null}
-- SMS messages linked to contacts via foreign key
ce_sms_messages.contact_id → ce_contacts.id

-- Consent tracked per contact phone
ce_sms_consent.contact_id → ce_contacts.id
```

**Integration Points:**

* SMS matched to contact by phone number (E.164 format)
* Contact detail includes SMS conversation tab
* Consent status displayed on contact
* SMS compose from contact detail page

**API Contract:**

```typescript theme={null}
// Phone-to-contact lookup
const contact = await supabase
  .from('ce_contacts')
  .select('id, first_name, last_name, phone, mobile_phone')
  .eq('organization_id', orgId)
  .or(`phone.eq.${normalizedPhone},mobile_phone.eq.${normalizedPhone}`)
  .single();
```

***

### CE-03: Platform Telephony Integration

**Pattern:** Platform Integration Layer\
**Direction:** CE-08 → CE-03

**Usage:** Extend Platform Telephony Layer to include SMS alongside calls

**Import:**

```typescript theme={null}
import { useTelephonyHistory } from '@/platform/telephony';
```

**Integration Points:**

* Unified communication history (calls + SMS)
* Shared phone number formatting utilities
* RingCentral SMS API (if using RingCentral for telephony)

***

### CE-04: Activities Integration

**Pattern:** Database Trigger (Event-Based)\
**Direction:** CE-08 → CE-04

**Trigger Contract:**

```sql theme={null}
-- Trigger: Auto-create activity when SMS sent/received
CREATE TRIGGER ce_sms_create_activity
  BEFORE INSERT ON ce_sms_messages
  FOR EACH ROW
  EXECUTE FUNCTION ce_create_activity_from_sms();
```

**Activity Created:**

```typescript theme={null}
interface SmsActivity {
  organization_id: string;
  contact_id: string | null;
  partner_id: string | null;
  lead_id: string | null;
  activity_type: 'sms';
  subject: string; // "SMS to [contact]" or "SMS from [contact]"
  description: string; // Message preview (first 100 chars)
  status: 'completed';
  activity_date: string; // ISO timestamp
  created_by: string | null;
}
```

***

### PF-10: Notifications Integration

**Pattern:** Platform Integration Layer\
**Direction:** CE-08 → PF-10

**Usage:** In-app notifications for inbound SMS

**Import:**

```typescript theme={null}
import { sendNotification } from '@/platform/notifications';
```

**Notification Types:**

| Event            | Notification Type | Recipients                 |
| ---------------- | ----------------- | -------------------------- |
| Inbound SMS      | `sms_received`    | User assigned to contact   |
| Delivery failure | `sms_failed`      | Sender                     |
| Opt-out received | `sms_opted_out`   | All org users with contact |

**Notification Payload:**

```typescript theme={null}
interface SmsNotification {
  type: 'sms_received' | 'sms_failed' | 'sms_opted_out';
  recipient_user_id: string;
  data: {
    sms_message_id: string;
    contact_name: string;
    phone_number_masked: string; // Last 4 digits only
    preview: string; // First 50 chars
    event_timestamp: string;
    error_code?: string; // For failures
  };
}
```

***

## External Integrations

### RingCentral SMS API Integration

**Pattern:** External API\
**Direction:** Bi-directional

**API Configuration:**

```typescript theme={null}
const RINGCENTRAL_CONFIG = {
  clientId: process.env.RINGCENTRAL_CLIENT_ID,
  clientSecret: process.env.RINGCENTRAL_CLIENT_SECRET,
  jwtToken: process.env.RINGCENTRAL_JWT_TOKEN,
  // Webhooks
  inboundWebhookUrl: '/functions/v1/sms-webhook',
  statusCallbackUrl: '/functions/v1/sms-webhook',
};
```

**API Endpoints Used:**

| Operation         | Endpoint                                                 | Method |
| ----------------- | -------------------------------------------------------- | ------ |
| Send SMS          | `/restapi/v1.0/account/~/extension/~/sms`                | POST   |
| Get message       | `/restapi/v1.0/account/~/extension/~/message-store/{id}` | GET    |
| List messages     | `/restapi/v1.0/account/~/extension/~/message-store`      | GET    |
| Subscribe webhook | `/restapi/v1.0/subscription`                             | POST   |

**Edge Functions:**

* `sms-send/index.ts` - Send SMS via RingCentral
* `sms-webhook/index.ts` - Receive inbound SMS and status updates

**Request/Response Schemas:**

**Send SMS Request:**

```typescript theme={null}
interface SendSmsRequest {
  organization_id: string;
  to_number: string; // E.164 format
  body: string;
  contact_id?: string;
  template_id?: string;
}

interface SendSmsResponse {
  success: boolean;
  message_id?: string;
  external_message_id?: string; // RingCentral message ID
  segment_count?: number;
  error?: string;
}
```

**Webhook Payload (Inbound / Status):**

```typescript theme={null}
// RingCentral delivers a notification envelope whose `body` carries the
// message-store record. sms-webhook parses the JSON body (no form-encoded payload).
interface RingCentralWebhookEnvelope {
  uuid: string;
  event: string; // e.g. /restapi/v1.0/account/~/extension/~/message-store
  subscriptionId: string;
  body: {
    id: number;
    from: { phoneNumber: string }; // E.164
    to: Array<{ phoneNumber: string }>; // E.164
    type: 'SMS' | 'MMS';
    direction: 'Inbound' | 'Outbound';
    messageStatus: 'Queued' | 'Sent' | 'Delivered' | 'DeliveryFailed' | 'SendingFailed';
    subject?: string; // message text
  };
}
```

***

## Event Contracts

### SMS Sent Event

**Event Name:** `ce.sms.sent`\
**Publisher:** CE-08\
**Subscribers:** CE-04 (Activity creation)

**Payload Schema:**

```typescript theme={null}
interface SmsSentEvent {
  event_type: 'ce.sms.sent';
  organization_id: string;
  sms_message_id: string;
  contact_id: string | null;
  to_number_masked: string; // Last 4 digits
  segment_count: number;
  user_id: string; // Sender
  timestamp: string;
}
```

### SMS Received Event

**Event Name:** `ce.sms.received`\
**Publisher:** CE-08 (webhook Edge Function)\
**Subscribers:** PF-10 (Notifications), CE-04 (Activity creation)

**Payload Schema:**

```typescript theme={null}
interface SmsReceivedEvent {
  event_type: 'ce.sms.received';
  organization_id: string;
  sms_message_id: string;
  contact_id: string | null;
  from_number_masked: string; // Last 4 digits
  preview: string; // First 50 chars
  timestamp: string;
}
```

### Consent Changed Event

**Event Name:** `ce.sms.consent_changed`\
**Publisher:** CE-08\
**Subscribers:** Audit logging

**Payload Schema:**

```typescript theme={null}
interface ConsentChangedEvent {
  event_type: 'ce.sms.consent_changed';
  organization_id: string;
  consent_id: string;
  contact_id: string | null;
  phone_number_masked: string; // Last 4 digits
  old_status: string;
  new_status: string;
  method: string; // How consent was changed
  timestamp: string;
}
```

***

## TCPA Compliance Contract

### Consent Requirements

**Express Written Consent (Marketing):**

* Required before sending marketing messages
* Must capture: timestamp, method, source
* Must be stored in `ce_sms_consent` table

**Prior Express Consent (Informational):**

* Required for informational messages
* Can be implied from business relationship
* Document consent source

### Opt-Out Contract

**Keywords Recognized:**

* STOP, UNSUBSCRIBE, CANCEL, END, QUIT (case-insensitive)

**Processing Requirements:**

1. Detect keyword in inbound message
2. Update `ce_sms_consent.consent_status` to `opted_out`
3. Record `opted_out_at` timestamp and `opted_out_keyword`
4. Send confirmation: "You have been unsubscribed. Reply START to resubscribe."
5. Log activity for compliance audit
6. Notify assigned users

**Enforcement:**

* Hard block on sending to opted-out numbers
* Check performed in `sms-send` Edge Function before API call
* RLS does NOT prevent viewing opted-out contacts (for compliance review)

***

## Security Considerations

### Phone Number Privacy

* Phone numbers stored in E.164 format
* Display masked in UI (show last 4 digits only)
* No phone numbers in application logs
* Encrypted in transit (TLS 1.2+)

### RingCentral Credential Security

* Credentials stored in Supabase Secrets
* Never exposed in client-side code
* Webhook signature validation required

### PHI Detection

* Message content scanned for PHI patterns
* Warning shown to user before send (not hard block)
* Patterns: SSN, DOB formats, medical terms
* Configurable via `sms_phi_detection_mode` setting

***

## Module Settings

Settings stored in `ce_module_settings`:

| Setting                        | Type    | Default                 | Description                                   |
| ------------------------------ | ------- | ----------------------- | --------------------------------------------- |
| `sms_retention_days`           | INTEGER | 730                     | Days to retain SMS messages (90-2555)         |
| `sms_opt_out_footer`           | TEXT    | 'Reply STOP to opt-out' | Footer for marketing messages                 |
| `sms_phi_detection_enabled`    | BOOLEAN | true                    | Enable PHI pattern detection                  |
| `sms_phi_detection_mode`       | TEXT    | 'warn'                  | PHI detection behavior: warn, block, disabled |
| `ringcentral_sms_phone_number` | TEXT    | null                    | Organization RingCentral SMS number (E.164)   |
| `sms_business_hours_start`     | TIME    | '09:00:00'              | Start of SMS business hours                   |
| `sms_business_hours_end`       | TIME    | '18:00:00'              | End of SMS business hours                     |

***

## Implementation Status

| Component             | Status     | Notes                    |
| --------------------- | ---------- | ------------------------ |
| Database schema       | 📋 Planned | 3 tables defined in spec |
| RingCentral setup     | 📋 Planned | Credentials and webhooks |
| Send Edge Function    | 📋 Planned | Phase 2                  |
| Webhook Edge Function | 📋 Planned | Phase 2                  |
| Consent management    | 📋 Planned | Phase 3                  |
| Conversation UI       | 📋 Planned | Phase 3                  |
| Templates             | 📋 Planned | Phase 4                  |

***

## Security & Compliance Controls

CE-08 implements comprehensive security and compliance controls for SMS messaging:

### PHI Detection

**Implementation:** [`supabase/functions/sms-send/index.ts`](https://github.com/Encore-OS/encoreos/blob/development/supabase/functions/sms-send/index.ts)

* PHI detection runs before message send (when `phiDetectionEnabled` is true in provider config)
* Detected PHI patterns are logged to `ce_sms_messages.phi_detected` and `phi_detection_result` fields
* Detection uses pattern matching from `_shared/phi-detection.ts` utility
* PHI detection results are included in audit logs for compliance review

### TCPA Opt-Out Handling

**Implementation:** [`supabase/functions/sms-webhook/index.ts`](https://github.com/Encore-OS/encoreos/blob/development/supabase/functions/sms-webhook/index.ts)

* Opt-out keywords detected: `STOP`, `UNSUBSCRIBE`, `CANCEL`, `END`, `QUIT`, `STOPALL`, `UNSUBSCRIBEALL`
* Automatic consent revocation when opt-out keywords are received
* Confirmation message sent: "You have been unsubscribed and will no longer receive SMS messages from us."
* Consent status updated in `ce_sms_consent` table with `consent_status = 'revoked'` and `revoked_at` timestamp
* Future sends blocked via `checkSmsConsent()` validation in `sms-send` function

### Webhook Signature Validation

**Implementation:** [`supabase/functions/sms-webhook/index.ts`](https://github.com/Encore-OS/encoreos/blob/development/supabase/functions/sms-webhook/index.ts)

* RingCentral webhooks validated via the `Validation-Token` header during subscription validation
* Inbound/status notifications are parsed from the RingCentral JSON envelope
* Signature/token validation ensures webhook authenticity and prevents spoofing
* Invalid signatures result in 403 Forbidden responses

### Provider Abstraction & Configuration

**Implementation:** [`supabase/functions/_shared/sms-provider.ts`](https://github.com/Encore-OS/encoreos/blob/development/supabase/functions/_shared/sms-provider.ts), [`supabase/functions/_shared/ringcentral-client.ts`](https://github.com/Encore-OS/encoreos/blob/development/supabase/functions/_shared/ringcentral-client.ts)

* Provider abstraction routes all SMS through RingCentral
* Provider configuration loaded from `ce_module_settings` with fallback to environment variables
* Business hours enforcement via `isWithinBusinessHours()` utility
* Phone number normalization to E.164 format via `normalizeToE164()`
* Segment calculation for multi-part messages via `calculateSmsSegments()`

### Consent Verification

**Implementation:** [`supabase/functions/_shared/sms-provider.ts`](https://github.com/Encore-OS/encoreos/blob/development/supabase/functions/_shared/sms-provider.ts)

* `checkSmsConsent()` validates consent status before sending
* Consent checked per contact phone number in `ce_sms_consent` table
* Required consent status: `'granted'` with valid `granted_at` timestamp
* Revoked consent blocks all future sends until new consent is granted

***

## References

* **Spec:** `specs/ce/specs/CE-08-sms-messaging-integration.md`
* **Plan:** `specs/ce/plans/CE-08-sms-messaging-PLAN.md`
* **Tasks:** `specs/ce/tasks/CE-08-sms-messaging-TASKS.md`
* **RingCentral SMS API:** [https://developers.ringcentral.com/api-reference/SMS](https://developers.ringcentral.com/api-reference/SMS)
* **TCPA Guidelines:** [https://www.fcc.gov/consumers/guides/stop-unwanted-robocalls-and-texts](https://www.fcc.gov/consumers/guides/stop-unwanted-robocalls-and-texts)
* **Integration Patterns:** `docs/architecture/integrations/index.md`

***

**Last Updated:** 2026-01-24
