---
summary: Connect your first SIP trunk, configure a dialplan, place an inbound call.
  Ten minutes if your carrier is ready.
title: Quickstart
path: quickstart
status: published
---

This walks you from "nothing configured" to "a call rings my extension" in one sitting.

## Prerequisites

- A ScaiGrid tenant with the `scaidial:trunks:manage`, `scaidial:extensions:manage`, and `scaidial:dialplan:manage` permissions on your API key. (Tenant admins get all three.)
- A SIP trunk from any ITSP — Twilio Elastic SIP, Telnyx, Voxbone, your own Asterisk / FreeSWITCH, etc. The credentials you need: server FQDN, username, password.
- One DID (phone number) the carrier will deliver to that trunk.

If you only have a carrier account but no trunk configured yet, do that first on the carrier side — ScaiDial registers as a SIP client to whatever you create.

## 1. Add the trunk

```bash
curl -X POST https://scaigrid.scailabs.ai/v1/modules/scaidial/trunks \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "primary",
    "sip_server_address": "sip.example.com",
    "sip_server_port": 5060,
    "sip_transport": "udp",
    "sip_auth_mode": "credentials",
    "sip_auth_username": "your-trunk-user",
    "sip_auth_password": "your-trunk-secret",
    "sip_inbound_numbers": ["+31201234567"],
    "sip_media_encryption": "disabled"
  }'
```

```python
import httpx, os

resp = httpx.post(
    "https://scaigrid.scailabs.ai/v1/modules/scaidial/trunks",
    headers={"Authorization": f"Bearer {os.environ['SCAIGRID_API_KEY']}"},
    json={
        "name": "primary",
        "sip_server_address": "sip.example.com",
        "sip_server_port": 5060,
        "sip_transport": "udp",
        "sip_auth_mode": "credentials",
        "sip_auth_username": "your-trunk-user",
        "sip_auth_password": "your-trunk-secret",
        "sip_inbound_numbers": ["+31201234567"],
        "sip_media_encryption": "disabled",
    },
)
print(resp.json())
```

```typescript
const resp = await fetch(
  "https://scaigrid.scailabs.ai/v1/modules/scaidial/trunks",
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.SCAIGRID_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "primary",
      sip_server_address: "sip.example.com",
      sip_server_port: 5060,
      sip_transport: "udp",
      sip_auth_mode: "credentials",
      sip_auth_username: "your-trunk-user",
      sip_auth_password: "your-trunk-secret",
      sip_inbound_numbers: ["+31201234567"],
      sip_media_encryption: "disabled",
    }),
  },
);
console.log(await resp.json());
```

The response includes a `sync_status` field. Poll the trunk until it transitions to `synced` — that means livekit-sip has successfully REGISTERed to your carrier. If it parks in `error`, read `sync_last_error` for the carrier's response and either fix the credentials or retry via `POST /trunks/{id}/resync`.

If your carrier requires source-IP whitelisting, fetch the tuple to whitelist from `GET /network-info`: egress IPv4, SIP port, and the RTP UDP range.

## 2. Create a dialplan and an extension

A minimal "ring my desk when anyone calls the DID" setup needs two objects: an extension to ring, and a dialplan whose only rule fires `ring_extension` on the always-match condition.

```bash
# Extension — type=wave routes the call to a tenant user.
EXT_ID=$(curl -s -X POST https://scaigrid.scailabs.ai/v1/modules/scaidial/extensions \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "number": "1001",
    "type": "wave",
    "target_ref": "usr_your_user_id_here",
    "display_name": "My desk",
    "ring_timeout_s": 30
  }' | jq -r .data.id)

# Dialplan with one rule: always → ring extension 1001.
DPLAN_ID=$(curl -s -X POST https://scaigrid.scailabs.ai/v1/modules/scaidial/dialplans \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "main"}' | jq -r .data.id)

curl -X POST https://scaigrid.scailabs.ai/v1/modules/scaidial/dialplans/$DPLAN_ID/rules \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"priority\": 100,
    \"match_type\": \"always\",
    \"match_params\": {},
    \"action_type\": \"ring_extension\",
    \"action_params\": {\"extension_id\": \"$EXT_ID\"}
  }"
```

## 3. Wire the DID to the dialplan

```bash
curl -X POST https://scaigrid.scailabs.ai/v1/modules/scaidial/dids \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"e164\": \"+31201234567\",
    \"trunk_id\": \"$TRUNK_ID\",
    \"dialplan_id\": \"$DPLAN_ID\",
    \"enabled\": true
  }"
```

## 4. Test the inbound path

Dial the DID from your phone. The dialplan fires the `always → ring_extension` rule, ScaiDial places the call into a LiveKit room, and your wave user gets a ring via the in-browser softphone (if they're logged into the admin) or via voicemail fallback after `ring_timeout_s`.

You can watch this happen live on the admin page at `/scaidial/calls` — the call appears in `ringing`, then `active` once you answer, then disappears when you hang up. The full record stays in `/scaidial/history`.

## Next steps

- **Place outbound.** With the same trunk synced, your owned extension can click-to-call any E.164 or SIP URI from `/my/dial`. See [concepts/architecture](./concepts/architecture) for the outbound flow.
- **Route to a bot.** Change the extension type to `bot` and point `target_ref` at a ScaiBot bot ID. Calls land in a LiveKit room with the bot worker; no extra wiring on the ScaiBot side.
- **Add time-of-day routing.** Use the `time_window` match type to ring different extensions during business hours vs. after-hours. The visual builder on `/scaidial/dialplans` handles the day-of-week + start/end picker for you.
- **Forwarding rules.** Each tenant user manages their own from `/my/dial`: forward on busy, on no-answer, on unavailable, or always.
