Unified.to
All articles

How to Trigger Notifications from Deal Events Using Unified's CRM API


February 2, 2026

Notifications based on deal activity sound straightforward. A call happens. A meeting is scheduled. A note is added. Send a message to Slack or email the account owner.

In practice, this only works if you have a reliable way to detect what actually changed on a deal.

CRMs don't represent activity the same way. Calls, meetings, emails, tasks, and form submissions all live as different objects with different timestamps and payloads. Some CRMs attach activity directly to deals. Others attach it to contacts or companies and infer deal context later. Even the idea of 'new activity' depends on which timestamp you look at.

If you don't handle that carefully, notifications become noisy or incomplete. You miss important events, or you notify on the same event multiple times.

Unified's CRM API gives you a normalized Event object that represents deal-related activity across CRMs. Instead of reacting to vendor-specific activity models, you can poll a single event stream, filter by deal, and trigger notifications based on documented event types and fields.

This guide shows how to trigger notifications from deal events using Unified's CRM API in TypeScript. It uses polling with incremental checkpoints, event type filtering, and deal context lookup. No webhooks, no UI assumptions, and no undocumented behavior.

Prerequisites

  • Node.js v18+
  • A Unified account with a CRM integration enabled
  • Your Unified API key
  • A customer CRM connectionId

Step 1: Set up your project

mkdir crm-deal-event-notifications
cd crm-deal-event-notifications
npm init -y
npm install @unified-api/typescript-sdk dotenv

Create a .env file:

UNIFIED_API_KEY=your_unified_api_key
CONNECTION_CRM=your_customer_crm_connection_id

Step 2: Initialize the SDK

import "dotenv/config";
import { UnifiedTo } from "@unified-api/typescript-sdk";

const { UNIFIED_API_KEY, CONNECTION_CRM } = process.env;

if (!UNIFIED_API_KEY) throw new Error("Missing UNIFIED_API_KEY");
if (!CONNECTION_CRM) throw new Error("Missing CONNECTION_CRM");

const sdk = new UnifiedTo({
  security: { jwt: UNIFIED_API_KEY },
});

const connectionId = CONNECTION_CRM;

Step 3: Understand CRM events and what they represent

Unified normalizes CRM activity into a single Event object. Every event represents an activity or engagement and is always associated with at least one deal, contact, or company.

Event types

Each event has a type field with one of the following values:

  • NOTE
  • EMAIL
  • TASK
  • MEETING
  • CALL
  • MARKETING_EMAIL
  • FORM
  • PAGE_VIEW

The type determines which nested object is present on the event payload.

Examples:

  • NOTEevent.note
  • CALLevent.call
  • MEETINGevent.meeting
  • TASKevent.task
  • EMAILevent.email

Relationship fields

Events can reference multiple entities:

  • deal_ids[]
  • company_ids[]
  • contact_ids[]
  • lead_ids[]

For deal-based notifications, deal_ids[] is the primary join surface.

Timestamps

Events include two timestamps:

  • created_at — when the event was created
  • updated_at — when the event was last updated

The list endpoint supports incremental filtering using updated_gte, which filters on updated_at. This is the safest checkpoint to use for polling.

Step 4: Decide which events should trigger notifications

Before writing code, decide what actually matters.

Common examples:

  • Notify when a CALL is logged on a deal
  • Notify when a MEETING is scheduled
  • Notify when a NOTE is added
  • Ignore passive events like PAGE_VIEW

Because type is a documented enum, you can filter explicitly without guessing.

Step 5: Poll deal events incrementally

Unified does not document CRM event webhooks, so notifications should be triggered using polling.

The event list endpoint supports:

  • pagination via limit and offset
  • filtering via updated_gte
  • filtering by deal_id
  • filtering by type
type Sort = "name" | "updated_at" | "created_at";
type Order = "asc" | "desc";

async function fetchDealEvents(opts: {
  dealId: string;
  updated_gte?: string;
  types?: string[];
  pageSize?: number;
}) {
  const pageSize = opts.pageSize ?? 100;
  let offset = 0;
  const out: any[] = [];

  while (true) {
    const page = await sdk.crm.listCrmEvents({
      connectionId,
      limit: pageSize,
      offset,
      updated_gte: opts.updated_gte ?? "",
      deal_id: opts.dealId,
      type: opts.types && opts.types.length === 1 ? opts.types[0] : "",
      sort: "updated_at",
      order: "asc",
    });

    if (!page || page.length === 0) break;

    out.push(...page);
    if (page.length < pageSize) break;

    offset += pageSize;
  }

  return out;
}

Notes:

  • Pagination stops when returned results are fewer than limit
  • type filtering is optional but recommended to reduce noise
  • updated_gte should be stored and reused between runs

Step 6: Deduplicate events safely

Unified does not document idempotency or ordering guarantees for events. The only stable identifier is event.id.

To avoid duplicate notifications:

  • store processed event.id values, or
  • advance your updated_gte checkpoint after each run

A simple in-memory example:

const processedEventIds = new Set<string>();

function isNewEvent(e: any): boolean {
  if (!e?.id) return false;
  if (processedEventIds.has(e.id)) return false;
  processedEventIds.add(e.id);
  return true;
}

Step 7: Fetch deal context for notifications

Events carry IDs, not human-readable deal context. To build a useful notification, retrieve the deal.

async function getDealContext(dealId: string) {
  return sdk.crm.getCrmDeal({
    connectionId,
    id: dealId,
    fields: [
      "id",
      "name",
      "stage",
      "pipeline",
      "amount",
      "currency",
      "company_ids",
      "contact_ids",
    ],
  });
}

Use only fields defined in the deal data model. Avoid assuming deal status or history.

Step 8: Build notification payloads by event type

Each event type has different fields. Build messages defensively and only read fields that exist for that type.

function buildNotification(event: any, deal: any) {
  const base = {
    dealId: deal.id,
    dealName: deal.name,
    stage: deal.stage,
    pipeline: deal.pipeline,
  };

  switch (event.type) {
    case "CALL":
      return {
        ...base,
        message: `Call logged on deal "${deal.name}"`,
        durationMinutes: event.call?.duration,
      };

    case "MEETING":
      return {
        ...base,
        message: `Meeting scheduled for deal "${deal.name}"`,
        startsAt: event.meeting?.start_at,
      };

    case "NOTE":
      return {
        ...base,
        message: `Note added to deal "${deal.name}"`,
        note: event.note?.description,
      };

    default:
      return null;
  }
}

If buildNotification returns null, skip sending.

Step 9: Send notifications from your application

Unified provides the event signal, not the delivery channel.

At this point, hand the payload to your own notification system:

function sendNotification(payload: any) {
  // email, Slack, SMS, push, etc.
  console.log("Notify:", payload);
}

Step 10: Put it all together

async function run(dealId: string, lastCheckpoint?: string) {
  const events = await fetchDealEvents({
    dealId,
    updated_gte: lastCheckpoint,
    types: ["CALL", "MEETING", "NOTE"],
  });

  for (const e of events) {
    if (!isNewEvent(e)) continue;

    const deal = await getDealContext(dealId);
    const notification = buildNotification(e, deal);

    if (notification) {
      sendNotification(notification);
    }
  }

  const newest = events.at(-1)?.updated_at;
  return newest ?? lastCheckpoint;
}

Persist the returned checkpoint (updated_at) and reuse it on the next run.

Optional: Create an event after notifying

If your workflow logs follow-up actions, you can create a CRM event after sending a notification.

await sdk.crm.createCrmEvent({
  connectionId,
  crmEvent: {
    type: "NOTE",
    note: {
      description: "Notification sent to account owner",
    },
    deal_ids: [dealId],
  },
});

Event creation support varies by CRM. Verify write support in the Feature Support tab.

Summary

Using Unified's CRM API, you can trigger notifications from deal events in a consistent way across CRMs:

  • Poll normalized CRM events using updated_gte
  • Filter events by deal and event type
  • Deduplicate using event.id
  • Retrieve deal context for human-readable messages
  • Trigger notifications from your own delivery system
  • Optionally log follow-up events back to the CRM

Start your 30-day free trial

Book a demo

All articles