For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
Log inSign up
Support
GuidesReferenceClick-to-Call
GuidesReferenceClick-to-Call
  • Getting Started
    • Overview
    • Authentication
    • RxJS Primer
    • Migrate from v3
  • Web Components
    • Overview
    • Click-to-Call
    • Theming
    • Customization
  • Build Voice & Video Apps
    • Overview
    • Outbound Calls
    • Inbound Calls
    • Device Management
    • Screen Sharing
    • Call Controls
    • Layouts
    • Messaging & Chat
  • Manage Resources
    • Overview
    • Users
    • Address Book
    • Client Preferences
    • Capabilities
  • Deploy
    • Overview
    • Framework Integration
    • SSR & Next.js
    • Troubleshooting
LogoLogoSignalWire Docs
Log inSign up
Support
On this page
  • Getting the directory
  • Paging
  • What an Address gives you
  • Resource type
  • Channels
  • Looking up an address by URI
  • Dialing
  • Messaging and call history
  • Room state
  • A complete directory UI
  • Reference
Manage Resources

Address Book & Directory

|View as Markdown|Open in Claude|
Was this page helpful?
Edit this page
Previous

Client Preferences

Next
Built with

client.directory is the runtime view of every Address the authenticated user can reach — other Users, video rooms, AI agents, SWML scripts, anything the platform has surfaced into this user’s scope. Each entry is an Address instance that you can read identity from, dial, message, and inspect for call history.

The directory is paginated, observable, and lazily loaded: subscribe to addresses$ and pages stream in as you call loadMore().

Getting the directory

1import { SignalWire, StaticCredentialProvider } from "@signalwire/js";
2
3const client = new SignalWire(
4 new StaticCredentialProvider({ token: "YOUR_SAT" })
5);
6
7client.directory$.subscribe((directory) => {
8 if (!directory) return; // not yet connected
9
10 directory.addresses$.subscribe((addresses) => {
11 renderList(addresses);
12 });
13
14 directory.loadMore(); // trigger the first page
15});

client.directory$ emits once the client is connected; subscribe to it instead of reading client.directory synchronously to avoid the “not yet authenticated” race. The directory itself outlives any single addresses$ subscription — it’s the manager that owns the state.

Paging

The directory does not load on its own — addresses$ emits [] until you call loadMore(). Subscribe first, then trigger the first page; every subsequent page works the same way.

1const directory = await firstValueFrom(client.directory$.pipe(filterNull()));
2
3// Subscribe to the full, growing list
4directory.addresses$.subscribe((addresses) => {
5 console.log(`now have ${addresses.length} addresses`);
6});
7
8// Track whether more pages exist
9directory.hasMore$.subscribe((hasMore) => {
10 loadMoreButton.disabled = !hasMore;
11});
12
13// Track loading state to disable the button mid-fetch
14directory.loading$.subscribe((loading) => {
15 spinner.hidden = !loading;
16});
17
18loadMoreButton.onclick = () => directory.loadMore();
19
20// Kick off the first page.
21directory.loadMore();

Reading directory.addresses synchronously before loadMore() has resolved gives you an empty array — it’s the snapshot of state the SDK currently holds, not a promise that fetches. Always drive your UI from addresses$.

The collection is reactive — when a server-side update lands (e.g. a new contact added in the background), new entries appear in the existing addresses$ stream without you re-fetching.

What an Address gives you

Identity (name, displayName, type, resourceId), visuals (preview / cover URLs), communication channels (audio / video / messaging URIs), room state, and the conversation handle (sendText, textMessages$, history$). Like everything in the SDK, mutable state is exposed twice — as a synchronous getter and as a $ observable. The full shape is on the Address reference page; this guide covers the fields you’ll actually drive UI off of.

Resource type

Address.type tells you what kind of Resource is on the other end:

typeWhat it is
'subscriber'Another user. Direct peer-to-peer.
'room'A video room. Multi-party.
'app'A SWML script or AI agent.
'call'A platform call resource (gateway, queue, etc.).

Use it to drive UI affordances — show a video icon for rooms, a phone icon for users, an avatar for AI agents:

1function iconFor(address) {
2 switch (address.type) {
3 case "room": return "video";
4 case "subscriber": return "user";
5 case "app": return "robot";
6 case "call": return "phone";
7 }
8}

Channels

address.channels reports which communication modes the resource supports. A video room exposes { audio, video, messaging }; a phone address might be { audio } only. The defaultChannel getter picks the right one for a one-click dial (video for rooms, audio otherwise).

1const call = await client.dial(address.defaultChannel ?? address.name, {
2 audio: true,
3 video: address.type === "room",
4});

Looking up an address by URI

When you know the URI (/public/support, /private/jane) and need the Address instance — to inspect channels, send a message, or hand to client.dial() — use findAddressIdByURI:

1const id = await directory.findAddressIdByURI("/public/support");
2if (id) {
3 const address = directory.get(id);
4 // address is now usable
5}

findAddressIdByURI checks the local cache first, then queries the server. directory.get(id) is a pure local lookup — call it only after the id is known to exist.

For the reactive equivalent, directory.get$(id) returns an Observable<Address> that emits whenever the entry’s state changes.

Dialing

client.dial() accepts either the URI directly or an Address instance:

1// by URI
2await client.dial("/public/support", { audio: true });
3
4// by Address — equivalent
5const address = directory.get(addressId);
6await client.dial(address, { audio: true });

Passing the Address lets the SDK pick the right channel automatically when one isn’t pinned in the URI.

Messaging and call history

Each address owns its own conversation: address.sendText(), address.textMessages$, and address.history$. See Messaging & Chat for the patterns — same pagination shape as the directory, lazy-loaded on first subscribe.

Room state

For room-type addresses, locked$ reports whether the room is currently accepting new joins. Lock state changes mid-call propagate through the same observable:

1address.locked$.subscribe((locked) => {
2 joinButton.disabled = locked;
3 joinButton.textContent = locked ? "Room locked" : "Join";
4});

previewUrl$ and coverUrl$ carry the room’s thumbnail and banner images when the platform has them.

A complete directory UI

1import { SignalWire, StaticCredentialProvider } from "@signalwire/js";
2import { filter, firstValueFrom } from "rxjs";
3
4const client = new SignalWire(
5 new StaticCredentialProvider({ token: "YOUR_SAT" })
6);
7
8const directory = await firstValueFrom(
9 client.directory$.pipe(filter((d) => !!d))
10);
11
12directory.addresses$.subscribe(renderList);
13directory.hasMore$.subscribe((more) => (loadMoreBtn.hidden = !more));
14loadMoreBtn.onclick = () => directory.loadMore();
15
16directory.loadMore(); // trigger the first page
17
18function renderList(addresses) {
19 list.innerHTML = "";
20 for (const address of addresses) {
21 const li = document.createElement("li");
22 li.textContent = `${address.displayName} (${address.name})`;
23 li.onclick = () =>
24 client.dial(address, {
25 audio: true,
26 video: address.type === "room",
27 });
28 list.appendChild(li);
29 }
30}

This is the same shape <sw-directory> builds on top of in the web components — see the Web Components reference if you’d rather drop in a pre-styled list.

Reference

  • SignalWire.directory$ / directory — the directory manager
  • Directory interface — addresses$, loadMore(), hasMore$, loading$, get(), get$(), findAddressIdByURI()
  • Address — the per-entry class (name, displayName, type, channels, sendText, textMessages,history, history,history, locked,previewUrl, previewUrl,previewUrl, coverUrl$)
  • SignalWire.dial() — accepts an Address or URI