Let a browser dial your agent with no dashboard setup
AI Agentsprogrammatic WebRTC voice agent access
Create a dialable voice AI endpoint and a restricted guest token over REST, without configuring either in the dashboard.
call-fabrictokens
The claim
Three REST calls the vendored spec documents. POST /api/fabric/resources/swml_webhooks creates a resource. Its one required field is primary_request_url, “Primary URL SignalWire fetches the SWML document from when the webhook fires”. used_for says whether it handles calls or messages. GET /api/fabric/resources/{id}/addresses lists the resource’s Fabric addresses, each with an id, a name and its channels. That an address is there to list is the platform’s side; the recipe asks until one is. POST /api/fabric/guests/tokens requires allowed_addresses, “List of up to 10 UUIDs representing the allowed Fabric addresses”, takes an expire_at, and answers 201 with token and refresh_token. You reach them as client.fabric.swml_webhooks.create, list_addresses and client.fabric.tokens.create_guest_token.
How it works
def register(name="front-desk", wait=time.sleep):
resource = client.fabric.swml_webhooks.create(
name=name, used_for="calling", primary_request_url=AGENT_URL,
primary_request_method="POST")
for attempt in range(ADDRESS_TRIES): # the list can lag the create
listed = client.fabric.swml_webhooks.list_addresses(resource["id"])
addresses = listed.get("data", [])
if addresses:
return resource["id"], addresses[0]
if attempt < ADDRESS_TRIES - 1:
wait(1)
raise RuntimeError(f"resource {resource['id']} listed no address after {ADDRESS_TRIES} tries")
def guest_token(address_id, now=None):
if now is None:
now = time.time()
return client.fabric.tokens.create_guest_token(
allowed_addresses=[address_id], expire_at=int(now) + TOKEN_TTL_SECONDS)
What the platform receives:
POST /api/fabric/resources/swml_webhooks
{"name": "front-desk", "used_for": "calling",
"primary_request_url": "https://<user>:<password>@<your-host>/front-desk/",
"primary_request_method": "POST"}
GET /api/fabric/resources/<resource_id>/addresses
POST /api/fabric/guests/tokens
{"allowed_addresses": ["<address_id>"], "expire_at": 1788351300}
The agent URL carries the agent’s basic-auth pair, the SWML_BASIC_AUTH_* values from the agent’s own .env, because the platform fetches the document from it. The spec does not say when the address appears, so register asks up to five times, a second apart, and fails with a message rather than an IndexError. A guest token names one address and expires. Your page asks your server for a fresh one and hands it to the Browser SDK, which is the client side and outside this recipe.
Limitations
You prove the requests and the documented shapes. Whether the address rings your agent, and what the browser hears, are the platform’s side of a live call.
A guest token is a credential. Mint it on your server per visitor and hand it over HTTPS; the default fifteen-minute expire_at here is the recipe’s choice.
What to change first
Change guest_token to take a list of address ids as its first argument, pass two, and run the verifier. The exact-body assertion fails. The spec says why you might want that anyway: a token may allow up to ten addresses, so one page can reach several desks.