SignalForge Hub API
Every SignalForge Hub exposes the same API surface. The web console, recorder clients, public radio-set players, and trusted peer hubs all talk to this hub API.
Base URL
Use the public hub URL as the base URL. API routes are under /api/v1 unless noted.
https://p7hub.projectseven.us/api/v1/health
Authentication Model
| Scope | Access |
|---|---|
| Public | Health, version, update check, recorder ingest, public player routes. |
| User session | Call browsing, radio sets, talkgroup preferences, readable sources. |
| Admin session | Users, audit logs, hub identity, federation peers, source management, call retention and archive, directory refresh. |
| Source key | Recorder upload through POST /api/call-upload. |
Authentication
Hubs advertise available sign-in methods through GET /api/v1/auth/capabilities. Online hubs with
Mailjet configured offer magic-link email codes; off-grid cells typically enable password login
and leave email delivery disabled.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/auth/capabilities | Password login, magic link, and email-delivery flags. |
| POST | /api/v1/auth/login | Email + password session (when AUTH_PASSWORD_LOGIN_ENABLED). |
| POST | /api/v1/auth/magic-link | Request a sign-in code by email (when Mailjet is configured). |
| POST | /api/v1/auth/verify-code | Complete magic-link sign-in with a 6-digit code. |
| GET | /api/v1/auth/me | Current session user. |
| POST | /api/v1/auth/logout | Revoke the session cookie or bearer token. |
Bootstrap admin credentials are set with AUTH_BOOTSTRAP_EMAIL and AUTH_BOOTSTRAP_PASSWORD in the API environment. See Off-Grid Hub Cells on the main site.
System Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/health | Basic hub health. |
| GET | /api/v1/version | Server build metadata. |
| GET | /api/v1/update-check | Compare deployed tag to the public image manifest. |
Hub Identity And Directory
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/hub/identity | Read local hub identity. Requires a user session. |
| PUT | /api/v1/hub/identity | Update hub name, URL, region, contact, and federation toggle. |
| POST | /api/v1/hub/identity/keypair | Generate the local Ed25519 hub keypair if missing. Returns only the public key. |
| POST | /api/v1/hub/directory/refresh | Fetch the configured directory feed and update local trust fields. |
| GET | /api/v1/hub/federation/status | Admin federation, trust, sharing, and peer status view. |
Federation
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/hub/invites | List hub invite tokens. |
| POST | /api/v1/hub/invites | Create a peer invite token. |
| POST | /api/v1/hub/invites/accept | Accept a remote hub invite. |
| GET | /api/v1/hub/peers | List known peer hubs. |
| POST | /api/v1/hub/peers | Connect to a remote hub with URL and invite token. |
| GET | /api/v1/federation/sources | Peer-readable shared local sources. |
| GET | /api/v1/federation/calls | Peer-readable shared local calls. Imported remote calls are not re-exported. |
Calls, Sources, And Radio Sets
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/calls | List readable calls with pagination, search, talkgroup, group, source, and radio-set filters. |
| GET | /api/v1/calls/{id}/audio | Download or play readable call audio. |
| GET | /api/v1/sources | List visible ingestion sources. |
| PUT | /api/v1/sources | Create or update a source profile. |
| POST | /api/v1/sources/{id}/keys | Generate a source upload key. |
| GET | /api/v1/radio-sets | List saved radio sets. |
| POST | /api/v1/radio-sets | Create a set by talkgroup IDs or by talkgroup_group names (selectionMode: "groups"). |
| POST | /api/v1/radio-sets/{id}/share | Generate a public player share token. |
Call Retention And Archive (Admin)
Call audio is stored in PostgreSQL. Operators configure a retention window so older calls are
exported to disk (and optionally S3-compatible object storage) before deletion. Sweeps run
automatically every six hours, or on demand from the web console Account tab.
A single Archive Now action batches through the full backlog until
remainingOld reaches zero.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/admin/calls/storage | Call count, total audio bytes, oldest/newest timestamps, retention and archive config. |
| POST | /api/v1/admin/calls/archive | Export then delete calls older than N days. dryRun: true reports the backlog without changes. Real runs batch up to 500 calls at a time and continue until empty by default (untilEmpty: true). |
Key environment variables on the api service:
| Variable | Meaning |
|---|---|
CALL_RETENTION_DAYS | Delete calls older than this many days when archiving (also drives the six-hour scheduler). |
CALL_ARCHIVE_DIR | Host path for exported day folders (mount a volume here). |
CALL_ARCHIVE_S3_URI | Optional s3://space-name/prefix destination for object storage sync. |
SPACES_ACCESS_KEY / SPACES_SECRET_KEY / SPACES_ENDPOINT | DigitalOcean Spaces credentials; entrypoint writes s3cmd config at container start. |
CALL_ARCHIVE_DELETE_LOCAL_AFTER_S3 | Remove local day folders after a successful upload. |
CALL_ARCHIVE_VACUUM_FULL | When true (default), run VACUUM FULL on calls after a sweep clears the backlog. |
Archive export layout:
/data/call-archive/
2026-06-01/
call-12345.json
call-12345.mp3
Example archive request:
POST /api/v1/admin/calls/archive
{
"olderThanDays": 14,
"dryRun": true
}
See Call Retention & Archive on the main site for operator setup notes.
Recorder Upload
Recorder clients upload calls with multipart form data to POST /api/call-upload.
Required fields are key, talkgroup, and an audio file part.
curl -F key=sk_live_REPLACE_WITH_SOURCE_KEY -F test=1 https://p7hub.projectseven.us/api/call-upload
A valid key probe returns HTTP 417 with incomplete call data: no talkgroup.
That is expected and means the key was accepted.
sf recorder check --source-key sk_live_REPLACE_WITH_SOURCE_KEY
sf recorder watch --input ./calls --source-key sk_live_REPLACE_WITH_SOURCE_KEY
sf update check
Public Player
| Method | Path | Purpose |
|---|---|---|
| GET | /public/player/{token} | Self-contained public player page. |
| GET | /public/ws/{token} | Public WebSocket with metadata and base64 audio in one message. |
| GET | /public/last-call/{token} | Most recent call audio for a share token. |
Custom Consoles
Authenticated operator consoles can connect to GET /ws for live hub events.
Most integrations should use the documented HTTP routes first.