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.idcan be delivered more than once. De-dupe byevent.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:
| Field | Type | Use for |
|---|---|---|
from_number | string | Match to a customer record (E.164) |
from_name | string | null | Fall-back display name when you have no match |
to_number | string | Which of your numbers was dialed (useful for multi-location routing) |
user_id | string | null | The user the call is routed to (when known) |
call_id | string | Stable 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
- Real-Time Events (SSE) — account-scoped event stream for frontends.
- Webhook Events — platform-scoped webhook firehose.
- Activity Logging — the other half of the event loop: write the call to your system of record.