ESCALATEOPS
Prepared for Unicity
Intercom ↔ RingCX · Live hand-off runbook

Bridge Hand-off Runbook

Walkthrough for the live session with Nilesh / DevOps / Zach / John: the real state of the Intercom ↔ RingCX (Engage Digital) bridge, what's confirmed with real calls, how it becomes yours to run and maintain, and what's fully on your side.

Real product: RingCX · Engage Digital Protocol: Channel SDK Signing: HMAC-SHA512 Status: 3/3 directions tested with real calls
Key correction — cover this first

The original assumption was RingEX/MVP (OAuth Client ID/Secret). The real product is RingCX → Engage Digital, with its own request/response protocol (Channel SDK) — not a simple "notify and move on" webhook. Any doc or env var based on RingEX/OAuth/Validation-Token no longer applies.

Walkthrough — in meeting order
01 · Concept What the bridge is

A service that sits between Intercom and RingCX. When Fin escalates to a human, the bridge translates messages between both systems so the customer sees one continuous conversation.

02 · Protocol How the Channel SDK works

RingCX doesn't passively notify: it asks, and our backend has to answer on the spot in the exact shape it expects.

ExchangeDirectionWhat our backend does
implementation.infoRingCX → usDeclares supported objects/actions (messages, threads · create, show, list, reply). Fires when the channel config is saved, then roughly every ~1 min on its own.
messages.createRingCX → usAgent replied in RingCX → forward to Intercom → respond with {id, body}.
*.list / *.showRingCX → usSync request. "Real Time only" mode → we respond [] / {}.
Send APIus → RingCXPOST with action: messages.create and author = customer (avoids the cant_reply_to_agent error from the old REST API, now abandoned).
03 · Security HMAC-SHA512, not what we first assumed
ChannelMechanism
Intercom → bridgeHMAC-SHA1 · INTERCOM_CLIENT_SECRET · header X-Hub-Signature
Intercom Workflow → bridge (/internal/escalate)Shared secret · INTERNAL_HANDOFF_SECRET
RingCX ↔ bridge (Channel SDK, both directions)HMAC-SHA512 · RINGCENTRAL_ENGAGE_SOURCE_SECRET · header X-SMCCSDK-SIGNATURE
04 · Persistence DynamoDB, single-table
pkContentPurpose
conv:<intercom_conversation_id>{ ringcentral_thread_id }New customer message in Intercom → which RingCX thread to forward it to
rc:<ringcx_thread_id>{ intercom_conversation_id }Agent replied in RingCX → which Intercom conversation to inject it into
evt:<intercom_event_id>{ ttl }Duplicate protection for Intercom webhook retries (24h TTL)

Runs on DynamoDB Local for dev (Matías's machine) and points to real DynamoDB on AWS just by changing env vars — no code changes needed.

05 · Finding We generate the thread_id ourselves

Confirmed with real tests: we generate the thread_id with crypto.randomUUID(), send it to RingCX as if it already existed, and RingCX creates it automatically the first time it sees it. No need to parse any ID out of RingCX's response. This significantly simplified escalation.js.

06 · Puppetizable identity What's 100% on Unicity's side

We already created the escalateops-bridge-bot identity with users.create and puppetizable: true. For a real agent to reply, John / Unicity needs to do this in their admin panel:

  1. Digital → Identities → mark that identity as "controlled."
  2. Assign it to whichever agent(s) will be replying.
  3. Confirm read + reply permissions for those agents on this channel.
  4. Confirm the channel is in the "real time" Channel Group (under Routing).
07 · Code hand-off How this becomes yours to run — not Matías's laptop

Nothing gets "transferred running." Unicity rebuilds the exact same thing on its own infrastructure, following this repo plus the checklist below — from that point on, Matías is no longer a dependency for keeping it alive.

  1. Package the codeserver.js, the full src/ folder, package.json, package-lock.json, the Dockerfile, and this documentation. Skip node_modules (rebuilt with npm install) and skip .env — never shared as a file. The local test-*.js scripts are dev-only and optional to include.
  2. Hand it over — as a zip, or better, a private GitHub repo so the integration keeps getting versioned as it evolves.
  3. Secrets travel separately — never in the same package as the code, never pasted in Slack. A quick call or a shared password manager, landing directly in AWS Secrets Manager.
  4. DynamoDB Local → real DynamoDB — the local database used for development isn't moved anywhere; Nilesh creates a brand-new, empty table in Unicity's own AWS account (command in the checklist below).
  5. The server stops living on a laptop — Nilesh builds the included Dockerfile into an image and runs it on Unicity's own infrastructure (ECS/Fargate or equivalent), with its own permanent URL. No more ngrok.
Confirmed with real calls — not simulated

Customer writes in Intercom → lands as a real message in a RingCX thread (the email actually arrived) · Agent replies in RingCX → lands in the real Intercom conversation with zero errors · Brand-new thread created from scratch with a self-generated ID · All 3 security layers working · Persistent store in DynamoDB with duplicate protection.

Hand-off checklist — walk through live
Business pending items 0 / 0
One-time, first deploy 0 / 0
Technical reference
POST /internal/escalate — Fin escalation trigger
Header: Authorization (INTERNAL_HANDOFF_SECRET, stored as an Authentication Token in the Data Connector)
Action: escalation.js generates a new thread_id, creates the thread in RingCX, saves the mapping
Channel SDK RingCX webhook — implementation.info / *.list / *.show / messages.create
Verification: X-SMCCSDK-SIGNATURE (HMAC-SHA512)
Mode: Real Time only → *.list responds [], *.show responds {}
PENDING RingCX admin panel — 4 puppetizable identity steps
100% on John/Unicity's side — see pillar 06 above
Environment variables in use
VariableStatus
INTERCOM_CLIENT_SECRETin use
INTERCOM_ACCESS_TOKENin use
INTERNAL_HANDOFF_SECRETin use
RINGCENTRAL_ENGAGE_SEND_URLin use — Send API URL, includes source_id
RINGCENTRAL_ENGAGE_SOURCE_SECRETin use — HMAC-SHA512 signing
RINGCENTRAL_VERIFICATION_TOKEN, CLIENT_ID/SECRET, JWT, API_TOKEN, SERVER_URL, SOURCE_ID, AUTHOR_IDdead — delete in cleanup — leftovers from RingEX/OAuth and the abandoned REST API
Blockers — who they depend on
  1. Real human agent replying — 100% depends on Unicity/John completing the 4 puppetizable-identity steps (section 06). Without this, a real agent can't reply even though the infrastructure is already tested.
  2. AWS deploy — not started yet. No pending code work, purely DevOps/Nilesh following the checklist above.
  3. End-to-end test of the real escalation trigger — mechanism proven separately (Data Connector reaches us fine, escalation.js creates threads fine); still needs testing together with a real conversation.