Skip to main content

Screen Pop

Open the customer record in your app on the very first ring — before anyone says hello.

Two event surfaces can drive a Screen Pop; pick the one that matches your architecture.

Option 1: frontend via Server-Sent Events (simplest for embedded integrations)

If the customer record already lives in a browser tab, let the browser itself subscribe to DialStack events and react. No backend round-trip.

DialStack pushes real-time events to the browser over Server-Sent Events (SSE) — a long-lived HTTP connection that streams events one-way from server to client. It's simpler than WebSockets and reconnects automatically.

Use the SDK to open an SSE stream with a session token:

import { loadDialStack } from '@dialstack/sdk';

const dialstack = await loadDialStack('pk_live_YOUR_KEY', {
fetchClientSecret: async () => {
const res = await fetch('/api/dialstack/session');
return res.json();
},
});

dialstack.on('call.incoming', async (event) => {
const customer = await lookupByPhone(event.from_number);
if (customer) openCustomerRecord(customer.id);
});

Latency: sub-second from the first ring to the record opening. Connection stays alive as long as the browser tab is open. SSE reconnects automatically.

See Real-Time Events for the full SSE API, authentication, and reconnection behavior.

Option 2: backend webhook → your own realtime channel

If the customer lookup requires your backend (to cross a firewall, join a DB, call a CRM), handle the Screen Pop server-side and push to the frontend over your own channel:

// POST /webhooks/dialstack — configured as your platform's webhook_url
app.post('/webhooks/dialstack', express.raw({ type: 'application/json' }), async (req, res) => {
verifySignature(req); // see webhook-events.md
const event = JSON.parse(req.body);

if (event.type === 'call.incoming') {
const customer = await db.customers.findOne({ phone: event.data.from_number });
if (customer) {
// push to the user's open session via YOUR realtime channel
pusher.trigger(`user-${event.data.user_id}`, 'screen-pop', {
customerId: customer.id,
callId: event.data.call_id,
});
}
}

res.status(200).end();
});

Webhooks are fire-and-forget — respond 200 quickly. Don't do the customer lookup on the webhook's critical path if it can be slow; queue it.

What makes a good Screen Pop

  • Speed over completeness. Pop the record in < 500ms. Enrich later.
  • Degrade gracefully. Unknown caller? Show the unknown-caller state, don't hide the Screen Pop entirely.
  • Idempotent. The same event.id can be delivered more than once. De-dupe by event.id.
  • No blocking UI. If lookup fails, log it and continue — never block the ring.

Where the data comes from

The call.incoming event carries:

FieldTypeUse for
from_numberstringMatch to a customer record (E.164)
from_namestring | nullFall-back display name when you have no match
to_numberstringWhich of your numbers was dialed (useful for multi-location routing)
user_idstring | nullThe user the call is routed to (when known)
call_idstringStable call identifier — use to attach the opened record to your activity log, and later to fetch call details, recording, or transcript via the API

See Webhook Events for the full call.incoming payload, envelope, and signature verification.

See also