Static Talkgroups API — quick start
- Connect the hotspot to TGIF using its normal TGIF Hotspot Security Key.
- Create a device-scoped token from Static TG API Tokens.
- Create the token for the exact DMR ID / ESSID used by the hotspot on TGIF.
- Send HTTPS requests to
https://api.tgif.network/v1/static-talkgroups/{device}.
- Read once at startup or on manual refresh; write only when configuration changes.
curl -sS \
-H "Authorization: Bearer <YOUR_STATIC_TG_API_TOKEN>" \
https://api.tgif.network/v1/static-talkgroups/123456701
Replace 123456701 with the exact hotspot/repeater DMR ID presented to TGIF. Never publish or log the bearer token.
Authentication and device scope
Bearer token
Every request uses an HTTP Authorization header:
Authorization: Bearer <YOUR_STATIC_TG_API_TOKEN>
The full secret is shown only once when the token is created. TGIF stores only a cryptographic hash of the secret.
Runtime authorization
A valid token is not enough by itself. TGIF also verifies that the requested device matches the token, is currently connected, belongs to the token account and is using TGIF secure hotspot authentication.
A token created for one ESSID cannot control another ESSID. A token for 123456701 cannot be used against 123456702.
Endpoint and data model
https://api.tgif.network/v1/static-talkgroups/{device_dmr_id}
{device_dmr_id} must contain 6 to 9 decimal digits. Static memberships are represented as a timeslot and talkgroup pair.
{"slot":2,"talkgroup":31665}
Talkgroup values must be between 1 and 16,777,215 and must not be a TGIF reserved/special destination. TG 9, 777, 4000, 9990 and 31000 are protected.
HTTP methods
| Method | Purpose | Request body |
GET | Read current Static TG state and supported timeslots. | None |
POST | Add one membership. Duplicate adds are idempotent. | {"slot":2,"talkgroup":31665} |
DELETE | Remove one membership. Removing an absent membership is idempotent. | {"slot":2,"talkgroup":31665} |
PUT | Replace the complete membership set atomically. | {"memberships":[...]} |
OPTIONS | Return the allowed methods. | None |
Read state
curl -sS \
-H "Accept: application/json" \
-H "Authorization: Bearer <YOUR_STATIC_TG_API_TOKEN>" \
https://api.tgif.network/v1/static-talkgroups/123456701
Add TG 31665 on TS2
curl -sS -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_STATIC_TG_API_TOKEN>" \
--data '{"slot":2,"talkgroup":31665}' \
https://api.tgif.network/v1/static-talkgroups/123456701
Delete TG 31665 on TS2
curl -sS -X DELETE \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_STATIC_TG_API_TOKEN>" \
--data '{"slot":2,"talkgroup":31665}' \
https://api.tgif.network/v1/static-talkgroups/123456701
Replace the complete set
curl -sS -X PUT \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_STATIC_TG_API_TOKEN>" \
--data '{"memberships":[{"slot":2,"talkgroup":31665}]}' \
https://api.tgif.network/v1/static-talkgroups/123456701
Write bodies must use Content-Type: application/json and are limited to 4096 bytes.
Response examples
GET success
{
"ok": true,
"device": 123456701,
"enabled": true,
"limit": 5,
"supports_ts1": false,
"supports_ts2": true,
"memberships": [
{"slot": 2, "talkgroup": 31665}
],
"request_id": "..."
}
Mutation success
{
"ok": true,
"device": 123456701,
"changed": true,
"runtime_seen": true,
"memberships": [
{"slot": 2, "talkgroup": 31665}
],
"request_id": "..."
}
changed:false means the requested state already existed, so no membership write or CallMGR reload was required. runtime_seen:true means a live CallMGR subscriber saw the reload notification.
HTTP status and error handling
| HTTP | Meaning | Client action |
| 400 | Invalid device, JSON or request structure. | Fix the request; do not retry unchanged input. |
| 401 | Missing, malformed, revoked or invalid API credential. | Check or recreate the credential. |
| 403 | Account/device ownership, secure-hotspot or policy authorization failed. | Check device scope and TGIF secure connection. |
| 409 | Device offline, Static TG disabled or concurrent update conflict. | Refresh state later; do not spin. |
| 413 | Request body too large. | Reduce the request. |
| 415 | Write request is not application/json. | Send the correct Content-Type. |
| 422 | Unsupported slot, reserved/invalid TG, duplicate entry or membership limit. | Correct the requested configuration. |
| 426 | HTTPS is required. | Use the HTTPS API URL. |
| 429 | Rate limited at the HTTP edge. | Stop and retry later with backoff/jitter. |
| 503 | Backing-store/runtime state could not be safely read or written. | Back off and retry later. |
JSON errors contain ok:false, an error code and a request_id for support correlation.
Required client behaviour
Continuous polling is not supported. The API is an event-driven configuration interface, not a live telemetry feed.
- Perform one GET when the client starts or its configuration UI opens.
- Cache the returned state locally.
- Send POST, DELETE or PUT only when the user changes configuration.
- Allow an explicit manual refresh to perform another GET.
- On network failure, 429 or 503, use delayed exponential backoff with random jitter if an automatic retry is necessary.
Do not use a synchronized fixed retry timer, daemon loop or short polling interval.
Hotspot integration
Pi-Star / WPSD
Linux-based hotspots can use the TGIF one-shot helper and store client settings in /etc/tgif-static-api.conf.
The configuration contains the production API URL, the device-scoped API credential and the exact DMR ID / ESSID used by the hotspot.
Protect that file with restrictive permissions. The reference helper supports list, add and del operations and deliberately does not run a polling loop.
openSPOT
openSPOT can use TGIF Static Talkgroups while securely connected, but it is an appliance and does not use the Pi-Star filesystem/helper setup.
Create the API credential for the exact openSPOT DMR ID / ESSID shown to TGIF. Manage Static TG state through TGIF Self-Care or an external application.
Do not put the Static TG API credential into the normal openSPOT TGIF/DMR network password field.
TGIF implementation behaviour
These behaviours are part of the production-safety contract and are useful when designing integrations:
- The API uses the same Static TG membership storage as TGIF Self-Care; it does not create a second configuration database.
- Reads and writes use bounded exact Redis keys; the API hot path does not use Redis KEYS or SCAN.
- Membership sets are bounded and account/network limits remain authoritative.
- PUT, duplicate POST and absent DELETE operations are idempotent.
- A genuine change publishes one reload notification for the affected hotspot only.
- An unchanged request performs no membership mutation and does not publish a CallMGR reload.
- The client never needs to restart CallMGR or reconnect the whole network after a Static TG change.
Reserved destinations
TG 9, 777, 4000, 9990 and 31000 are protected from Static TG configuration. Validation is enforced server-side.
Security guidance for application developers
- Treat the API credential as a password and never include it in URLs.
- Do not write credentials to application logs, crash reports or analytics.
- Use protected credential storage when the platform provides it.
- Provide a clear credential-revocation path to users.
- Never commit live credentials into source control or example configuration.