Grandstream Phones
The Grandstream adapter is beta-tier . It communicates with Grandstream GXP/GXV SIP desk phones over CGI/HTTP using challenge-response authentication and browser-style cookies. The adapter includes credentials encrypted at rest, an SSRF guard on phone IP, role gates on factory-reset and firmware push, and HMAC-signed provisioning.
What works
Section titled “What works”| Feature | Details |
|---|---|
| Fleet view | Per-phone: model, firmware, MAC, registration status |
| Templated XML provisioning | Account, codec order, BLF keys, DND, time zone, NTP, VLAN tag |
| Bulk reboot | Trigger a reboot across all selected phones (site_admin+ required) |
| Per-phone factory-reset | Wipe a single phone to factory defaults (voip.manage_phones required) |
| Firmware push | Push a firmware URL to a phone or fleet (site_admin+ required) |
| Per-phone status polling | SIP registration state, IP address, uptime |
GXV touchscreen-only video features and DECT base-station + handset roaming orchestration (beyond single-base provisioning) are not supported.
Authentication model
Section titled “Authentication model”Grandstream phones use a challenge-response authentication scheme:
- FreeSDN posts
sha256_hex(username)to/cgi-bin/access; the phone returns atoken. - FreeSDN hashes
sha256_hex(password + token)and posts it to/cgi-bin/dologinalong with the username. - The phone sets
session-identityandsession-rolecookies at login time (Set-Cookieon the/cgi-bin/dologinresponse). aiohttp carries these automatically for all subsequent/cgi-bin/api-*calls - cookies are the primary authentication mechanism andinclude_sid=Falseis the default. Additionally,/cgi-bin/config_getand/cgi-bin/config_updaterequire thesid(also returned in the login JSON body) appended as a query parameter (?sid=<sid>); those two endpoints passinclude_sid=Trueexplicitly. TheCookieJar(unsafe=True)is required because aiohttp’s default cookie jar rejects cookies from IP-literal hosts.
The adapter uses aiohttp with CookieJar(unsafe=True) because Grandstream phones are addressed by IP, and the standard cookie jar rejects cookies for IP-literal hosts.
Passwords are stored encrypted at rest. They are never written to logs.
Prerequisites
Section titled “Prerequisites”- Grandstream phone running firmware 1.0.7.x or later (GXP21xx/GXP16xx series tested)
- HTTP access from the FreeSDN host to the phone management port (default 80)
- A phone admin account (default username:
admin) - Source-IP allowlist: the FreeSDN host IP must be in the phone’s Access Control list, or requests must carry a valid HMAC signature
Adding phones
Section titled “Adding phones”Single phone
Section titled “Single phone”# Add the phonecurl -X POST https://freesdn.example.com/api/v1/voip/phones \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>" \ -H "Content-Type: application/json" \ -d '{ "name": "Reception", "ip_address": "10.0.1.101", "mac_address": "00:0b:82:ab:cd:ef", "vendor": "grandstream", "site_id": "<site-uuid>" }'
# Store the admin password on the phone record (encrypted at rest in the phone's settings)curl -X PUT https://freesdn.example.com/api/v1/voip/phones/<phone-uuid>/credentials \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>" \ -H "Content-Type: application/json" \ -d '{ "username": "admin", "password": "admin123" }'Fleet discovery
Section titled “Fleet discovery”If phones are on the same subnet as the FreeSDN agent, use the discovery scan to find all Grandstream devices:
curl -X POST https://freesdn.example.com/api/v1/voip/discovery/scan \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>" \ -H "Content-Type: application/json" \ -d '{ "subnet": "10.0.1.0/24", "scan_type": "http" }'Discovered phones appear in VoIP → Discovery for manual adoption.
Provisioning
Section titled “Provisioning”FreeSDN uses templated XML provisioning - you define a provisioning template (account parameters, codec order, BLF keys, etc.) and push it to one or more phones.
Create a template
Section titled “Create a template”curl -X POST https://freesdn.example.com/api/v1/voip/templates/ \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>" \ -H "Content-Type: application/json" \ -d '{ "name": "Standard Office", "vendor": "grandstream", "site_id": "<site-uuid>", "sip_settings": { "sip_server": "pbx.example.com", "sip_port": 5060, "codec_order": ["PCMU", "PCMA", "G722"] }, "network_settings": { "ntp_server": "pool.ntp.org", "time_zone": "US/Eastern" }, "feature_settings": { "dnd_enabled": false } }'Apply a template to a phone
Section titled “Apply a template to a phone”curl -X POST https://freesdn.example.com/api/v1/voip/phones/<phone-uuid>/provision \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>" \ -H "Content-Type: application/json" \ -d '{"force": false, "reboot_after": true}'The adapter renders the template to Grandstream P-code XML, extracts the P-value map, and writes it to the phone via HTTP PUT to /cgi-bin/config_update (POST returns 501 on modern GXP/GRP firmware). The phone reboots to apply the new config.
Bulk operations
Section titled “Bulk operations”Bulk reboot
Section titled “Bulk reboot”curl -X POST https://freesdn.example.com/api/v1/voip/fleet/bulk/reboot \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>" \ -H "Content-Type: application/json" \ -d '{"phone_ids": ["<uuid-1>", "<uuid-2>"]}'Per-phone factory-reset
Section titled “Per-phone factory-reset”Requires the voip.manage_phones permission. Factory-reset is a per-phone operation - there is no bulk fleet endpoint for factory-reset.
curl -X POST https://freesdn.example.com/api/v1/voip/phones/<phone-uuid>/factory-reset \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>"Firmware push
Section titled “Firmware push”Requires site_admin role. Provide the target firmware version string; FreeSDN schedules the upgrade across the selected phones via the fleet bulk endpoint:
curl -X POST https://freesdn.example.com/api/v1/voip/fleet/bulk/firmware \ -H "Cookie: freesdn_access=<token>" \ -H "X-CSRF-Token: <csrf>" \ -H "Content-Type: application/json" \ -d '{ "phone_ids": ["<uuid-1>"], "target_version": "1.0.11.16" }'P-code configuration
Section titled “P-code configuration”Grandstream phones use a numeric P-code system for all settings. FreeSDN’s provisioning templates map human-readable fields to P-codes. If you need to set a P-code that is not in the template schema, use the raw_pconfig field:
{ "raw_pconfig": { "P271": "1", "P272": "3" }}Values in raw_pconfig are merged with the rendered template before being pushed to the phone.
Frontend pages
Section titled “Frontend pages”- PhonesListPage - fleet overview with registration status badges
- PhoneDetailPage - per-phone detail, provisioning, BLF configuration
- FleetDashboardPage - aggregate fleet health
- TemplatesPage - provisioning template management
- FirmwarePage (VoIP) - firmware push history
- DiscoveryPage (VoIP) - newly discovered phones awaiting adoption
Backend route prefix
Section titled “Backend route prefix”/api/v1/voip/phones/*- phone CRUD + lifecycle/api/v1/voip/templates/*- provisioning template management/api/v1/voip/provisioning/cfg{MAC}.xml- phone-pull config serving. Access is restricted: the source IP must be in the site’s configured subnet(s), or the request URL must include a valid HMAC signature (?sig=<hex>). No JWT/cookie auth is required (phones pull directly), but unauthenticated external requests from unlisted IPs return 404./api/v1/voip/fleet/bulk/*- bulk reboot + firmware push/api/v1/voip/firmware/*- firmware tracking + compliance/api/v1/voip/discovery/*- scan triggers
Adapter tier
Section titled “Adapter tier”| CB | RR | TS | SSRF | RG |
|---|---|---|---|---|
| ✓ | ✓ | ✓ | ✓ | ✓ |
Dual-gate (DG) is not applicable - phone configuration writes are applied immediately (not staged), because the phone itself is the authority. Every write is logged in the FreeSDN audit trail.