Why we're not building in-house chat

A woman checking her phone while standing in an office

The original backlog item for this just said “chat module” — the kind of line that, left alone, turns into months of building a worse version of a product that already exists on everyone’s desktop. Before writing any of it, we asked a narrower question instead: not “should Contrayo have chat,” but “how does the rest of this category actually meet people in Slack and Teams,” and whether that answer changes anything about what we should build.

It splits into two real patterns. Notify-and-link-out: a message lands in a channel or DM when something needs attention, and acting on it means clicking through to the actual product. Every CLM vendor we checked ships this and nothing more — Ironclad states it in its own words for both platforms, DocuSign’s original Slack integration reads the same way, PandaDoc and Concord don’t go further either. Genuinely interactive: the message itself renders buttons, and clicking one changes something in the real system without the user ever opening it. That pattern isn’t exotic — it’s the norm for ticketing and ITSM tools. Jira and ServiceNow both let you approve, reject, or comment directly from a Slack message. It just doesn’t show up anywhere in CLM.

That split mattered to us because of what kind of tool our approval engine actually is. A document-editing CLM flow doesn’t map cleanly onto a chat button — there’s usually more to look at than “approve or don’t.” A delegation-of-authority engine is different: sequential steps, a named actor, a discrete decision, an audit trail. That’s architecturally closer to the ITSM shape than to the rest of CLM, which is the reason we didn’t stop at notify-and-link-out once we started building.

Slack shipped first, as a real Slack App posting Block Kit messages rather than a bare webhook — a link to the step, who’s requesting it, and on what contract. Teams followed the next day, through the Workflows app’s current webhook trigger, and it stops at notification-only — not because we chose to hold it back, but because that’s what Microsoft’s own current mechanism supports. The classic incoming-webhook connector for Teams is past its own retirement window; the one that replaced it explicitly doesn’t render buttons, by Microsoft’s own published design.

Then, on Slack, the actual point of doing this at all: real approve/reject buttons rendered directly in the notification. Click one and it resolves through the identical code path a click inside Contrayo itself would use — the same eligibility check, the same segregation-of-duties rule, the same sequential gating, the same audit trail. Nothing about the approval logic is duplicated or approximated for chat; the Slack button and the in-app button call the same function. Every incoming click is verified against Slack’s own request signature before anything else runs, and a Slack identity isn’t automatically treated as a Contrayo one — an admin maps each person’s Slack user ID to their org membership once, so the audit trail always resolves to a real, named account rather than an opaque chat handle.

We’re naming the real limits here rather than rounding them off. That identity mapping is manual, one row at a time — no bulk import yet, which is a real gap for a large org. Teams doesn’t have any of this interactivity today; getting there would mean standing up a hosted bot, a materially heavier decision we’re deliberately not making until Pattern A has real usage to justify it.

None of that changes the actual answer to the original backlog item. We’re not building a chat product. We built the two integrations that let people act on what Contrayo needs from them without leaving the one they already have open — see how it plugs into the notification system and the approval engine underneath it.

← Back to the blog