ESCALATEOPS
Prepared for Unicity
Technical Handoff · Intercom ↔ RingCentral Bridge

From AI to human, without the customer noticing

Summary for handing control of the project over to the Unicity team. Explains what was built, how data persists, what's resolved, and what's still open — without needing to read the code line by line.

Client: Unicity
EscalateOps contact: Matias Gema
Status: core logic closed · pending RingCentral
Infra: DynamoDB + Docker
What this is, in one sentence Architecture

A small service (the "bridge") that sits between Intercom and RingCentral. When Fin (Intercom's AI agent) can't resolve something and escalates the conversation to a human, this service translates messages between the two systems so the customer sees one continuous conversation, without ever knowing the system changed behind the scenes.

How it works, step by step Flow
  1. The customer writes to Fin in Intercom.
  2. If Fin can't resolve it, an Intercom Workflow fires a call to the bridge (POST /internal/escalate) with that conversation's data.
  3. The bridge notifies RingCentral that there's a new conversation to handle, and saves a "mapping" — which Intercom conversation corresponds to which RingCentral thread.
  4. The human agent replies from RingCentral. RingCentral notifies the bridge (POST /webhooks/ringcentral), and the bridge injects that reply into the original Intercom conversation.
  5. If the customer writes again, Intercom notifies the bridge (POST /webhooks/intercom), and the bridge forwards it to RingCentral. Steps 4-5 repeat for as long as the conversation with the agent continues.
How data persists DynamoDB

The core piece of data the bridge needs to remember is the "Intercom conversation ↔ RingCentral thread" mapping. Without it, it wouldn't know where to forward each new message.

Partition key (pk)ContentWhat it's for
conv:<intercom_conv_id>{ ringcentral_thread_id }When a new message arrives from the customer in Intercom, we look this up to know where to forward it
rc:<ringcentral_thread_id>{ intercom_conversation_id }When the agent replies in RingCentral, we look this up to know which Intercom conversation to inject the reply into
evt:<intercom_event_id>{ ttl }Record of "this event was already processed" — prevents duplicates if Intercom retries a webhook
Why DynamoDB and not server memory

If the server restarts (a deploy, a crash, a scheduled restart), a process's memory is wiped entirely. With DynamoDB the mapping survives restarts, and if the service ever runs on more than one instance to spread load, they all share the same source of truth instead of each keeping its own inconsistent copy.

How it self-cleans

evt:* records (duplicate control) carry a ttl attribute — DynamoDB deletes them automatically after 24 hours, no manual cleanup needed. Mapping records (conv:* and rc:*) aren't auto-deleted today; cleaning up old conversations is a pending task to define, based on how long Unicity wants to retain mapping history.

Security implemented Auth
ChannelHow it's protected
Intercom → bridgeCryptographic signature (HMAC-SHA1) sent by Intercom on every request; the bridge recomputes and compares it. If it doesn't match, it rejects with 401.
RingCentral → bridgeA token Unicity chooses when creating the subscription, returned by RingCentral on every subsequent notification.
Intercom Workflow → bridgeA separate secret stored as an "Authentication Token" in the Intercom Data Connector (encrypted on Intercom's side, never visible as plain text).
bridge → Intercom / RingCentralAuth tokens in the Authorization header, never in the URL.

All secret comparisons use a timing-attack-safe method (crypto.timingSafeEqual), the minimum standard for this kind of validation.

What's 100% closed and tested 5 / 5 done
Checklist for whoever takes over (Nilesh / DevOps) Action

One-time, on first deploy:

0 / 7

Recurring, once RingCentral info arrives:

0 / 4
Blockers · depends on Unicity

Everything below is not pending work on our side — it's information only Unicity can confirm. With these 4 pieces of info, two specific code files get completed; nothing else in the system needs to change.

  1. Which RingCentral product they're using — RingCentral CX / contact center, or RingEX / the core platform. These are different APIs.
  2. Credentials for a service app in the RingCentral Developer Console (Client ID, Client Secret, JWT), with permission to read and send messages in the queue where escalated conversations land.
  3. The exact shape of the notification RingCentral sends when an agent replies (which webhook or event, and its data shape).
  4. The endpoint to create a new interaction in a RingCentral queue from an external system (used for the initial handoff).
Contact

For any question about the bridge's business logic (not infrastructure), the point of contact remains Matias Gema (EscalateOps) until RingCentral's information is confirmed and that last piece is closed.