# RingCentral + Hyperconnect Implementation Log

**Project:** Rise CRM Hyperconnect communications provider expansion  
**Date:** 2026-03-06  
**Scope:** Add RingCentral as a parallel SMS/MMS provider (alongside Twilio), while preserving existing Hyperconnect event model and associations.

---

## 1) Original Objectives

1. Review current Hyperconnect communication behavior (Twilio and related flows).
2. Add RingCentral as a parallel provider, toggleable from settings.
3. Preserve existing canonical storage and associations:
   - `tblhyperconnect_events`
   - association fields (`rel_type`, `rel_id`, `client_id`, `center_id`, etc.)
4. Keep inbound/outbound behavior consistent with existing UX.
5. Support center-based dynamic sending behavior (multiple numbers/centers).
6. Maintain secure credential handling similar to existing secret patterns.

---

## 2) Discovery Findings (Research Phase)

### Existing System Reality

- Hyperconnect already handled communications primarily around Twilio/Gmail patterns.
- Canonical event storage is `tblhyperconnect_events`; UI and downstream behavior depend on this table and existing relation semantics.
- Any provider extension should write into the same event model to avoid UI regressions.

### RingCentral Integration Constraints

- RingCentral outbound SMS endpoint is extension-scoped (`/account/~/extension/{extensionId}/sms`).
- Sender number ownership is enforced by RingCentral.
- Typical RingCentral failures encountered:
  - `CMN-102` (invalid `extensionId` in path)
  - `MSG-304` ("Phone number doesn't belong to extension")
- Extension number (e.g., `102`) is not always equivalent to extension **ID**.

---

## 3) High-Level Architecture Implemented

### Parallel Provider Model

- Added provider selection (`twilio` | `ringcentral`) under Hyperconnect settings.
- Existing send endpoints now dispatch via provider abstraction.
- Twilio behavior preserved.
- RingCentral enabled in parallel without changing canonical event schema.

### Inbound + Outbound

- **Outbound** SMS/MMS can route through selected provider.
- **Inbound** RingCentral webhook endpoint added and normalized into canonical events.

### Subscriptions

- RingCentral webhook subscription management added:
  - create/list/renew
  - admin actions
  - cron auto-renew maintenance

---

## 4) Files Added / Modified

### New Files

1. `modules/hyperconnect/libraries/Hyperconnect_ringcentral_client.php`
   - RingCentral API wrapper.
   - JWT auth flow.
   - SMS/MMS send.
   - message/call fetch helpers.
   - media upload support.
   - subscription list/create/renew.
   - diagnostics output.
   - extension resolution and sender-number ownership matching logic.

2. `modules/api/controllers/Hyperconnect_ringcentral.php`
   - RingCentral webhook receiver/controller.
   - verification token handling.
   - event parsing/normalization.
   - persistence into `tblhyperconnect_events`.

### Updated Files

1. `modules/api/config/routes.php`
   - Added API route for RingCentral webhook endpoint.

2. `modules/hyperconnect/hyperconnect.php`
   - Added RingCentral settings defaults.
   - Added subscription defaults.
   - Added cron hook for auto-renew behavior.

3. `modules/hyperconnect/views/settings_group.php`
   - Added Communication provider selection UI.
   - Added RingCentral settings fields (server URL, extension ID, client ID/secret, JWT, credentials path, webhook token, subscription options).
   - Hardened tab JS (`window.jQuery` guard) to avoid `$ is not a function` failures.
   - Removed/adjusted problematic `required` fields that blocked unrelated tab saves.
   - Added explicit submit markers for team settings branch separation.

4. `modules/hyperconnect/controllers/Hyperconnect.php`
   - Added provider dispatch helpers.
   - Routed multiple send endpoints through provider abstraction.
   - Added RingCentral settings save behavior.
   - Added RingCentral subscription action handling.
   - Improved phone normalization.
   - Added center extras extension extraction and center-based extension resolution.
   - Ensured ticket/lead/guest/membership paths pass provider options for RingCentral.
   - Fixed branch logic that previously mis-routed settings saves.

5. `application/composer.json`
   - Added RingCentral SDK dependency.

---

## 5) Package / Dependency State

- RingCentral package installed:
  - `ringcentral/ringcentral-php` `3.0.4`

---

## 6) Detailed Debugging Timeline and Resolutions

### A) UI/Settings Save Breakages

**Symptoms:**
- Save action failing unexpectedly.
- Team settings logic firing during non-team saves.
- Front-end JS error: `$ is not a function`.

**Fixes made:**
- Team save path now gated by explicit submit markers.
- Non-relevant required fields removed from unrelated tabs.
- JS wrapped with safer jQuery availability handling.

---

### B) Outbound Number Formatting Failures

**Symptoms:**
- `invalid_e164`

**Fixes made:**
- Expanded E.164 normalization handling for NANP and common input formats.

---

### C) RingCentral Configuration Failures

**Symptoms:**
- `ringcentral_not_configured`

**Fixes made:**
- Added richer diagnostics in RingCentral client (missing keys, resolved creds path, etc.).
- Settings/UI and backend save logic aligned for JWT and related credentials.

---

### D) Extension Context Failures (Critical)

#### 1. `CMN-102` - Resource for `extensionId` not found

**Root cause:**
- Extension number (`102`) could be used where RingCentral extension **ID** is required.

**Fixes made:**
- Added extension-input resolution path:
  - direct extension fetch
  - query by `extensionNumber`
  - fallback list lookup
- Prevented unresolved extension input from being blindly treated as extension ID.

#### 2. `MSG-304` - Phone number doesn't belong to extension

**Root cause:**
- RingCentral enforces sender-number ownership for extension context used in send path.
- When fallback/default context is `~`, send fails if that context does not own the `from` number.

**Fixes made:**
- Added multi-step extension attempt strategy and retries.
- Added explicit debug response fields to send failures:
  - `extension_input`
  - `extension_id_used`
  - `attempted_extensions`
  - `configured_extension_id`
  - `sender_extension_auto`
- Ensured center-based extension value from `centers.extras` is passed into send options across all send entry points.

---

## 7) Current Known Behavior

- Integration compiles and static checks pass on modified files.
- Provider toggle and settings are in place.
- Canonical event writes remain preserved.
- RingCentral requests are reaching API and authenticating.
- Remaining blocker is account-side extension/number ownership policy/permissions resulting in `MSG-304` in specific send scenarios.

---

## 8) Why the Remaining Error Happens

Given current responses (`extension_id_used: "~"` and `MSG-304`), RingCentral is rejecting the send under the extension context actually used.

This can happen when:
1. JWT identity cannot send as the target extension.
2. Sender number is not owned/assigned to that extension context.
3. Tenant permissions do not allow cross-extension send on behalf behavior.
4. Number exists on account but not SMS-capable for that extension identity.

---

## 9) RC Configuration Guidance (Centralized JWT Model)

For **single admin/service JWT** model to work reliably:

1. Use a dedicated service identity or properly privileged admin identity.
2. Ensure role can access extensions and phone numbers account-wide.
3. Ensure outbound messaging permissions/scopes are sufficient.
4. Ensure each center sender number is truly usable by the extension context used in path.
5. Validate extension-number-to-extension-id mapping and owner assignment using API.

If tenant policy still restricts sends per extension ownership, move to per-center/per-extension identity strategy.

---

## 10) Work Completed (Checklist)

- [x] Added RingCentral provider in parallel with Twilio.
- [x] Kept canonical event table and associations unchanged.
- [x] Added RingCentral webhook endpoint and route.
- [x] Added RingCentral settings and subscription management UI/actions.
- [x] Added dependency and integrated client library.
- [x] Fixed settings-tab save blockers and JS issue.
- [x] Added E.164 normalization improvements.
- [x] Added center-based extension extraction and pass-through.
- [x] Added extension resolution/retry logic and richer diagnostics.

---

## 11) Remaining Work

1. **Final RingCentral account alignment** (outside code):
   - confirm which extension contexts own which sender numbers.
   - confirm role/permission for on-behalf sends.

2. **Optional hardening in code (if desired):**
   - explicit per-center map to RingCentral extension **ID** (not number).
   - optional per-center credential/JWT support if tenant policy requires strict identity separation.

3. **Validation pass after RC admin updates:**
   - retest sends across multiple centers/numbers.
   - verify expected extension context in diagnostic fields.

---

## 12) Suggested Next Steps (Execution Order)

1. In RingCentral admin, verify ownership and SMS capability for each of the 20 sender numbers.
2. Confirm whether centralized admin JWT can send for all target extension IDs.
3. If yes:
   - store resolved extension IDs per center extras and re-test.
4. If no:
   - implement per-center JWT credentials strategy in Hyperconnect.

---

## 13) Important Notes for Future Maintainers

- Keep all communication writes normalized through `tblhyperconnect_events`.
- Avoid introducing a parallel events schema per provider.
- Preserve provider abstraction boundary in `Hyperconnect.php` send handlers.
- Prefer extension **ID** over extension number whenever constructing RingCentral extension-path API calls.
- Keep current diagnostics fields in place until production is stable.

---

## 14) Log Owner / Source

This document reflects the full implementation and troubleshooting history from the initial research request through current production debugging state, including both completed work and unresolved account-configuration dependencies.
