# NetSapiens SNAPsolution API v2 — Reference (PacketDial / OITVOIP) Source of truth: the live OpenAPI spec at `https://pbx.packetdial.com/ns-api/webroot/openapi/openapi.json` (title "NetSapiens API v2", version 44.4.10 as of 2026-06-02). ## Hosts - **API host:** `pbx.packetdial.com` — `https://pbx.packetdial.com/ns-api/v2` - **Customer portal (no API):** `voip.packetdial.com` — login-gated UI; the Cascades fax/UC dashboard lives here (account 28598). Hitting `/ns-api/*` on this host returns `errors/not_found`. ## Authentication (`bearerAuth`) All calls send `Authorization: Bearer `. Two ways to get the token: 1. **Static API key** — created in `pbx.packetdial.com` Admin > API Keys. Prefix encodes scope: `nsr_` = reseller, `nss_` = system, `nsd_` = domain. Use the key string directly as the bearer token (no exchange step). 2. **OAuth2 password grant** — `POST /ns-api/v2/tokens` form-encoded: `grant_type=password&client_id=..&client_secret=..&username=..&password=..` Response includes `access_token` (JWT) + `expires_in`. The client caches it and refreshes ~60s before expiry. ## Endpoint inventory (239 paths, grouped) Top-level resource groups and the methods present: | Resource | Methods | Notes | |---|---|---| | `/version` | GET | platform version | | `/apikeys`, `/apikeys/{id}`, `/apikeys/~` | GET POST PUT DELETE | `~` = current key | | `/tokens`, `/jwt` | POST (+ GET/DELETE on jwt) | auth / token mgmt | | `/resellers`, `/resellers/{reseller}` | GET POST PUT DELETE | | | `/domains`, `/domains/{domain}` | GET POST PUT PATCH DELETE | `/domains/count` for totals | | `/domains/{domain}/users`, `/users/{user}` | GET POST PUT DELETE | extensions | | `/domains/{domain}/users/{user}/devices` | GET POST PUT DELETE | per-user SIP devices | | `/domains/{domain}/users/{user}/answerrules` | GET POST | call routing | | `/domains/{domain}/users/{user}/cdrs` | GET | per-user CDRs | | `/domains/{domain}/phones` | GET POST PUT DELETE | domain device list | | `/domains/{domain}/phonenumbers`, `/{phonenumber}` | GET POST PUT DELETE | DIDs | | `/domains/{domain}/addresses`, `/addresses/endpoints` | GET POST PUT DELETE | emergency/E911 addrs | | `/domains/{domain}/cdrs`, `/cdrs` | GET | call detail records (`/count` variants) | | `/phonenumbers` | GET | global DID search | | `/smsnumbers` | GET | | | `/subscriptions` | GET POST PUT DELETE | event/webhook subs | | `/connections`, `/routes`, `/routecon` | GET POST PUT DELETE | SIP trunks / routing | | `/configurations`, `/config-definitions`, `/templates` | GET POST PUT DELETE | | | `/certificates`, `/images` | GET POST PUT DELETE | | | `/meetings`, `/video`, `/firebase` | GET POST | UC / video | | `/sipflow` | GET | SIP trace | | `/holidays`, `/cdrs` | GET | | | `/backup`, `/restore` | POST / GET PUT | platform backup | Many resources also expose `/count` (totals) and `#1/#2/#3` openapi-dedup variants (same path, different query-param shapes). Use the Swagger UI for the exact request/response schema of any specific path. ## Common query params - List endpoints accept `limit`, `offset`, and resource-specific filters. - CDR endpoints accept `start-date` / `end-date` (and `domain`, `user` scoping via the nested path). Always bound CDR queries — they can be large. ## Provisioning flow (new customer domain) 1. `POST /domains` — creates the domain; dial plan auto-generates. 2. `POST /domains/{domain}/users` — one per extension. 3. `POST /domains/{domain}/users/{user}/devices` or `POST /domains/{domain}/phones` — SIP devices (MAC provisioning). 4. `POST /domains/{domain}/phonenumbers` — attach DIDs, route to users. 5. Configure `answerrules` as needed. ## History - 2026-04-20: API researched (session log `session-logs/2026-04-20-session.md`, "OITVOIP / NetSapiens API Research"). Platform identified, auth shapes documented. **No API key created yet** — provisioning the key + storing it in `msp-tools/oitvoip.sops.yaml` was the open TODO. - 2026-06-02: `packetdial` skill created wrapping the v2 API (read-by-default, gated writes). Confirmed `voip.` is portal-only and `pbx.` is the API host.