Open5GS NMS

April 24, 2026 · View on GitHub

Complete reference for all HTTP REST endpoints exposed by the backend on port 3001 (proxied through nginx on port 8888 at /api/*).

All endpoints except /api/health, POST /api/auth/login require a valid session cookie (nms_session). Unauthenticated requests return 401.

Base URL (via nginx proxy): http://YOUR_SERVER:8888/api
Base URL (direct backend): http://YOUR_SERVER:3001/api


Table of Contents

  1. Health
  2. Authentication
  3. Users
  4. Configuration
  5. Services
  6. Subscribers
  7. Interface Status
  8. Backup & Restore
  9. Auto-Config
  10. SUCI Keys
  11. Audit Log
  12. Docker
  13. WebSocket

Health

GET /api/health

Public endpoint. No authentication required. Used by Docker healthcheck.

Response 200

{
  "status": "ok",
  "timestamp": "2026-04-22T22:00:00.000Z",
  "wsConnections": 3
}

Authentication

POST /api/auth/login

Public endpoint. Rate-limited to 10 attempts per 15 minutes per IP (failed attempts only).

Request body

{
  "username": "admin",
  "password": "your-password"
}

Response 200 — Sets nms_session HttpOnly cookie.

{
  "success": true,
  "data": {
    "user": {
      "id": "abc123",
      "username": "admin",
      "role": "admin"
    }
  }
}

Response 401

{ "success": false, "error": "Invalid username or password" }

Response 429

{ "success": false, "error": "Too many login attempts, please try again later" }

POST /api/auth/logout

Requires valid session. Invalidates the session and clears the cookie.

Response 200

{ "success": true }

GET /api/auth/me

Returns the currently authenticated user.

Response 200

{
  "success": true,
  "data": {
    "user": {
      "id": "abc123",
      "username": "admin",
      "role": "admin"
    }
  }
}

Users

GET /api/users

List all NMS users.

Response 200

{
  "success": true,
  "data": {
    "users": [
      {
        "id": "abc123",
        "username": "admin",
        "role": "admin",
        "createdAt": "2026-04-01T00:00:00.000Z",
        "lastLoginAt": "2026-04-22T22:00:00.000Z"
      }
    ]
  }
}

POST /api/users

Create a new NMS user.

Request body

{
  "username": "operator1",
  "password": "SecurePass123!"
}

Response 201

{
  "success": true,
  "data": {
    "user": { "id": "def456", "username": "operator1", "role": "admin" }
  }
}

Response 409 — Username already exists.

Response 400 — Weak password or invalid username.


PUT /api/users/:id/password

Change a user's password.

URL params: id — user ID string

Request body

{ "password": "NewSecurePass456!" }

Response 200

{ "success": true }

Response 404 — User not found.

Response 400 — Weak password.


DELETE /api/users/:id

Delete a user. Cannot delete yourself or the last remaining user.

URL params: id — user ID string

Response 200

{ "success": true }

Response 400 — Cannot delete self / cannot delete last user.

Response 404 — User not found.


Configuration

Valid service names for all config endpoints:

nrf scp amf smf upf ausf udm udr pcf nssf bsf mme hss pcrf sgwc sgwu


GET /api/config

Load all 16 NF configurations at once.

Response 200

{
  "success": true,
  "data": {
    "nrf": { ... },
    "amf": { ... },
    "smf": { ... },
    "..." : "..."
  }
}

GET /api/config/:service

Load a single NF configuration.

URL params: service — one of the 16 valid service names

Response 200

{
  "success": true,
  "data": {
    "sbi": { "server": [{ "address": "10.0.1.155", "port": 7777 }] },
    "ngap": { "server": [{ "address": "10.0.1.155" }] },
    "rawYaml": { ... }
  }
}

Response 400 — Invalid service name.


POST /api/config/validate

Validate all current on-disk configurations against Zod schemas without applying anything.

Response 200

{
  "success": true,
  "data": {
    "valid": true,
    "errors": []
  }
}

POST /api/config/apply

Apply configuration changes to all NF YAML files. Automatically backs up current config first, restarts affected services in dependency order, and rolls back on failure. Also regenerates and live-reloads prometheus.yml.

Request body — Full configs object (same shape as GET /api/config response data)

{
  "amf": { "rawYaml": { ... } },
  "smf": { "rawYaml": { ... } },
  "..."
}

Response 200

{
  "success": true,
  "data": {
    "success": true,
    "rollback": false,
    "backupCreated": "/etc/open5gs/backups/backup-2026-04-22T22-00-00",
    "restartedServices": ["open5gs-amfd", "open5gs-smfd"],
    "errors": [],
    "prometheusReloaded": true,
    "prometheusReloadError": null
  }
}

GET /api/config/topology/graph

Returns node data for all 16 NFs including their addresses, ports, and current active status. Used by the topology page.

Response 200

{
  "success": true,
  "data": {
    "nodes": [
      { "id": "amf", "address": "10.0.1.155", "port": 7777, "active": true },
      { "id": "smf", "address": "10.0.1.155", "port": 7777, "active": true }
    ],
    "edges": []
  }
}

POST /api/config/sync-sd

Sync a Slice Differentiator (SD) value across SMF config and all matching subscriber slices in MongoDB.

Request body

{
  "sd": "000001",
  "sst": 1
}

Response 200

{
  "success": true,
  "data": {
    "smfUpdated": true,
    "subscribersUpdated": 12
  }
}

Response 400 — SD value is required.


Services

Valid service names: nrf scp amf smf upf ausf udm udr pcf nssf bsf mme hss pcrf sgwc sgwu

Valid actions for single service: start stop restart enable disable

Valid actions for bulk: start stop restart


GET /api/services

Get status of all 16 Open5GS services.

Response 200

{
  "success": true,
  "data": [
    {
      "name": "amf",
      "active": true,
      "enabled": true,
      "description": "open5gs-amfd.service",
      "subState": "running"
    }
  ]
}

GET /api/services/:name

Get status of a single service.

URL params: name — service name

Response 200

{
  "success": true,
  "data": {
    "name": "amf",
    "active": true,
    "enabled": true,
    "subState": "running"
  }
}

Response 400 — Invalid service name.


POST /api/services/:name/:action

Execute an action on a single service.

URL params: name — service name, action — one of start stop restart enable disable

Response 200

{ "success": true, "message": "Service amf restarted successfully" }

Response 400 — Invalid service name or action.


POST /api/services/all/:action

Execute an action across all 16 services at once.

URL params: action — one of start stop restart

Response 200

{
  "success": true,
  "results": [
    { "service": "nrf", "success": true, "message": "Started" },
    { "service": "amf", "success": true, "message": "Started" }
  ]
}

Subscribers

GET /api/subscribers

List subscribers with optional pagination and search.

Query params

ParamTypeDefaultDescription
skipnumber0Records to skip (pagination offset)
limitnumber50Max records to return
searchstringFilter by IMSI (partial match)

Response 200

{
  "subscribers": [
    {
      "imsi": "999700000053555",
      "msisdn": [],
      "imeisv": [],
      "slice": [...]
    }
  ],
  "total": 42
}

GET /api/subscribers/ip-assignments

Get the current static IP assignments for all subscribers that have one configured.

Response 200

{
  "success": true,
  "data": [
    { "imsi": "999700000053555", "ipv4": "10.45.0.100" }
  ]
}

GET /api/subscribers/:imsi

Get a single subscriber by IMSI.

URL params: imsi — 15-digit IMSI string

Response 200 — Full subscriber document from MongoDB.

Response 404 — Subscriber not found.


POST /api/subscribers

Create a new subscriber.

Request body — Open5GS subscriber document

{
  "imsi": "999700000053556",
  "security": {
    "k": "465B5CE8B199B49FAA5F0A2EE238A6BC",
    "opc": "E8ED289DEBA952E4283B54E88E6183CA",
    "amf": "8000"
  },
  "slice": [
    {
      "sst": 1,
      "session": [
        {
          "name": "internet",
          "type": 3,
          "qos": { "index": 9, "arp": { "priority_level": 8 } },
          "ambr": { "downlink": { "value": 1, "unit": 3 }, "uplink": { "value": 1, "unit": 3 } }
        }
      ]
    }
  ]
}

Response 201

{ "message": "Created" }

Response 400 — Validation error (e.g. duplicate IMSI, invalid key format).


PUT /api/subscribers/:imsi

Update an existing subscriber.

URL params: imsi — 15-digit IMSI string

Request body — Same shape as POST /api/subscribers

Response 200

{ "message": "Updated" }

Response 400 — Validation error.


DELETE /api/subscribers/:imsi

Delete a subscriber.

URL params: imsi — 15-digit IMSI string

Response 200

{ "message": "Deleted" }

POST /api/subscribers/auto-assign-ips

Auto-assign sequential static IPv4 addresses from the session pool to all subscribers that do not already have one.

Response 200

{
  "success": true,
  "data": {
    "assigned": 5,
    "skipped": 12,
    "errors": []
  }
}

Interface Status

GET /api/interface-status

Returns live RAN interface status and active UE sessions. Runs netstat (N2, S1-MME), conntrack (S1-U, 4G sessions), and tshark (N3, 5G sessions) on the host.

Response 200

{
  "s1mme": {
    "active": true,
    "connectedEnodebs": ["10.0.1.100", "10.0.1.101", "10.0.1.102"]
  },
  "s1u": {
    "active": true,
    "connectedEnodebs": ["10.0.1.100", "10.0.1.101"]
  },
  "n2": {
    "active": true,
    "connectedGnodebs": ["10.0.1.48", "172.16.1.67"]
  },
  "n3": {
    "active": true,
    "connectedGnodebs": ["172.16.1.67"]
  },
  "activeUEs4G": [
    { "ip": "10.45.0.100", "imsi": "999700000053555" }
  ],
  "activeUEs5G": [
    { "ip": "10.45.0.102", "imsi": "999702959493689" }
  ]
}

Detection methods:

FieldMethodBound to
s1mmenetstat -an, SCTP port 36412MME IP from mme.yaml
s1uconntrack UDP/2152, dst=<sgwu-ip>SGW-U IP from sgwu.yaml
n2netstat -an, SCTP port 38412AMF NGAP IP from amf.yaml
n3tshark UDP/2152, host <upf-ip>UPF GTP-U IP from upf.yaml
activeUEs4Gconntrack + MongoDB correlationSession pool subnets
activeUEs5Gtshark inner GTP + MongoDB correlationUPF GTP-U IP from upf.yaml

Backup & Restore

GET /api/backup/list

List all available backups.

Response 200

{
  "mongoBackups": [
    { "name": "mongo-backup-2026-04-22T22-00-00", "timestamp": "2026-04-22T22:00:00.000Z", "type": "mongodb" }
  ],
  "configBackups": [
    { "name": "config-backup-2026-04-22T22-00-00", "timestamp": "2026-04-22T22:00:00.000Z", "type": "config" }
  ]
}

POST /api/backup/config

Create a manual config backup of all 16 YAML files immediately.

Response 200

{ "success": true, "backupName": "config-backup-2026-04-22T22-00-00" }

POST /api/backup/mongo

Create a manual MongoDB backup using mongodump.

Response 200

{ "success": true, "backupName": "mongo-backup-2026-04-22T22-00-00" }

POST /api/backup/restore/config

Restore all config files from a named config backup.

Request body

{ "backupName": "config-backup-2026-04-22T22-00-00" }

Response 200

{ "success": true, "restoredFiles": ["amf.yaml", "smf.yaml", "..."] }

POST /api/backup/restore/mongo

Restore MongoDB subscriber database from a named backup using mongorestore.

Request body

{ "backupName": "mongo-backup-2026-04-22T22-00-00" }

Response 200

{ "success": true }

POST /api/backup/restore/both

Restore both config and MongoDB in one operation.

Request body

{
  "configBackupName": "config-backup-2026-04-22T22-00-00",
  "mongoBackupName": "mongo-backup-2026-04-22T22-00-00"
}

Response 200

{ "success": true, "configRestored": true, "mongoRestored": true, "errors": [] }

POST /api/backup/restore/selective

Restore only specific NF config files from a backup, leaving others untouched.

Request body

{
  "backupName": "config-backup-2026-04-22T22-00-00",
  "services": ["amf", "smf", "upf"]
}

Response 200

{ "success": true, "restored": ["amf.yaml", "smf.yaml", "upf.yaml"], "errors": {} }

POST /api/backup/diff

Get a unified diff showing what changed between a backup and the current on-disk config.

Request body

{ "backupName": "config-backup-2026-04-22T22-00-00" }

Response 200

{
  "success": true,
  "diffs": {
    "amf": "--- amf.yaml\n+++ amf.yaml\n@@ -10,7 +10,7 @@\n ...",
    "smf": ""
  }
}

GET /api/backup/last-config

Get the name of the most recent config backup (useful for the auto-rollback display).

Response 200

{ "backupName": "config-backup-2026-04-22T22-00-00" }

GET /api/backup/settings

Get current backup retention settings.

Response 200

{ "configBackupsToKeep": 10, "mongoBackupsToKeep": 5 }

PUT /api/backup/settings

Update backup retention settings and immediately apply cleanup.

Request body

{ "configBackupsToKeep": 10, "mongoBackupsToKeep": 5 }

Response 200

{ "configBackupsToKeep": 10, "mongoBackupsToKeep": 5 }

Response 400 — Retention values must be at least 1.


POST /api/backup/cleanup

Manually trigger backup cleanup to enforce retention limits.

Request body (optional — defaults used if omitted)

{ "configBackupsToKeep": 10, "mongoBackupsToKeep": 5 }

Response 200

{ "success": true }

POST /api/backup/restore-defaults

Restore all 16 NF YAML configs to factory defaults (bundled with the NMS). Creates a backup of the current state first.

Response 200

{
  "success": true,
  "message": "Factory defaults restored. Backup created at /etc/open5gs/backups/...",
  "backupCreated": "/etc/open5gs/backups/pre-restore-defaults-2026-04-22T22-00-00"
}

Auto-Config

POST /api/auto-config/preview

Generate a YAML diff preview of what the auto-config wizard would change without actually applying anything.

Request body

{
  "plmn4g": [{ "mcc": "999", "mnc": "70", "mme_gid": 2, "mme_code": 1, "tac": 1 }],
  "plmn5g": [{ "mcc": "999", "mnc": "70", "tac": 1 }],
  "s1mmeIP": "10.0.1.175",
  "sgwuGtpIP": "10.0.1.175",
  "amfNgapIP": "10.0.1.155",
  "upfGtpIP": "10.0.1.155",
  "sessionPoolIPv4Subnet": "10.45.0.0/16",
  "sessionPoolIPv4Gateway": "10.45.0.1",
  "sessionPoolIPv6Subnet": "2001:db8:cafe::/48",
  "sessionPoolIPv6Gateway": "2001:db8:cafe::1",
  "configureNAT": false
}

Response 200

{
  "success": true,
  "diffs": {
    "mme": "--- mme.yaml\n+++ mme.yaml\n@@ ...",
    "amf": "--- amf.yaml\n+++ amf.yaml\n@@ ...",
    "sgwu": "",
    "upf": "",
    "smf": ""
  }
}

POST /api/auto-config/apply

Apply the auto-configuration wizard. Backs up current configs, writes MME, SGW-U, AMF, UPF, SMF YAML files, optionally configures NAT (persistent via netfilter-persistent save), then restarts affected services.

Request body — Same shape as POST /api/auto-config/preview plus optional NAT fields:

{
  "...": "...(same as preview)...",
  "configureNAT": true,
  "natInterface": "ogstun"
}

Response 200

{
  "success": true,
  "message": "Auto-configuration applied successfully. Services restarted.",
  "backupCreated": "/etc/open5gs/backups/pre-autoconfig-2026-04-22T22-00-00",
  "updatedFiles": ["mme.yaml", "sgwu.yaml", "amf.yaml", "upf.yaml", "smf.yaml"]
}

SUCI Keys

GET /api/suci/keys

List all SUCI home network public/private keypairs.

Response 200

[
  {
    "id": 1,
    "scheme": 1,
    "publicKey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650",
    "privateKey": "c80949f13ebe...",
    "createdAt": "2026-04-22T22:00:00.000Z"
  }
]

GET /api/suci/next-id

Get the next available PKI ID (0–255).

Response 200

{ "nextId": 2 }

POST /api/suci/keys

Generate a new SUCI keypair. Scheme 1 = Profile A (X25519), Scheme 2 = Profile B (secp256r1). Also updates UDM config with the new public key.

Request body

{ "id": 1, "scheme": 1 }

Response 200

{
  "id": 1,
  "scheme": 1,
  "publicKey": "5a8d38864820197c3394b92613b20b91633cbd897119273bf8e4a6f4eec0a650",
  "privateKey": "c80949f13ebe...",
  "createdAt": "2026-04-22T22:00:00.000Z"
}

Response 400 — Missing fields or invalid scheme.


PUT /api/suci/keys/:id

Regenerate an existing keypair (deletes old, creates new with same ID). Also updates UDM config.

URL params: id — PKI ID number

Request body

{ "scheme": 1 }

Response 200 — Same shape as POST /api/suci/keys.


DELETE /api/suci/keys/:id

Delete a SUCI keypair. Optionally deletes the key file from disk.

URL params: id — PKI ID number

Query params: deleteFile=true — also remove the key file from disk

Response 200

{ "success": true, "id": 1, "deletedFile": true }

Audit Log

GET /api/audit

Retrieve audit log entries with optional pagination and action filtering.

Query params

ParamTypeDefaultDescription
skipnumber0Records to skip
limitnumber100Max records to return
actionstringFilter by action type (see below)

Valid action values: config_apply config_load service_action subscriber_create subscriber_update subscriber_delete backup_create backup_restore suci_generate suci_delete auto_config

Response 200

{
  "entries": [
    {
      "id": "abc123",
      "timestamp": "2026-04-22T22:00:00.000Z",
      "user": "admin",
      "action": "config_apply",
      "target": "amf",
      "details": "AMF configuration applied",
      "success": true
    }
  ],
  "total": 156
}

Docker

GET /api/docker/containers

List all NMS Docker containers (backend, frontend, nginx).

Response 200

{
  "success": true,
  "containers": [
    "open5gs-nms-backend",
    "open5gs-nms-frontend",
    "open5gs-nms-nginx"
  ]
}

GET /api/docker/logs/:container

Get recent log lines from a specific NMS container.

URL params: container — container name (e.g. open5gs-nms-backend)

Query params: limit — number of lines to return (default 100)

Response 200

{
  "success": true,
  "logs": [
    {
      "timestamp": "2026-04-22T22:00:00.000Z",
      "level": "info",
      "message": "HTTP server started",
      "container": "open5gs-nms-backend"
    }
  ]
}

WebSocket

The WebSocket server runs on port 3002 (proxied via nginx at ws://YOUR_SERVER:8888/ws).

Used for real-time log streaming and service status updates. Authenticate before connecting — the WebSocket connection shares the same session cookie as HTTP.

Service Status Broadcasts

The server broadcasts service status updates every 5 seconds to all connected clients automatically. No subscription message needed.

Received message

{
  "type": "service_status_update",
  "payload": {
    "statuses": {
      "amf": { "name": "amf", "active": true, "enabled": true, "subState": "running" },
      "smf": { "name": "smf", "active": true, "enabled": true, "subState": "running" }
    }
  }
}

Log Streaming — Subscribe

Send this message to start streaming logs from Open5GS services or Docker containers.

Sent message

{
  "type": "subscribe_logs",
  "source": "open5gs",
  "services": ["amf", "smf", "upf"]
}

Or for Docker container logs:

{
  "type": "subscribe_logs",
  "source": "docker",
  "services": ["open5gs-nms-backend", "open5gs-nms-nginx"]
}

Use "services": [] or omit to subscribe to all services/containers for that source.


Log Streaming — Received

Received message

{
  "type": "log_entry",
  "payload": {
    "timestamp": "2026-04-22T22:00:00.000Z",
    "level": "info",
    "service": "amf",
    "message": "gNB-N2 accepted[addr:172.16.1.67]",
    "source": "open5gs"
  }
}

Log Streaming — Unsubscribe

Sent message

{
  "type": "unsubscribe_logs",
  "source": "open5gs"
}

Error Responses

All endpoints return a consistent error shape on failure:

{ "success": false, "error": "Human-readable error message" }
StatusMeaning
400Bad request — missing or invalid parameters
401Unauthenticated — no valid session cookie
404Resource not found
409Conflict — e.g. duplicate username
429Rate limited — too many login attempts
500Internal server error

Quick Reference

MethodPathAuthDescription
GET/api/healthNoHealth check
POST/api/auth/loginNoLogin
POST/api/auth/logoutYesLogout
GET/api/auth/meYesCurrent user
GET/api/usersYesList users
POST/api/usersYesCreate user
PUT/api/users/:id/passwordYesChange password
DELETE/api/users/:idYesDelete user
GET/api/configYesLoad all configs
GET/api/config/:serviceYesLoad one config
POST/api/config/validateYesValidate configs
POST/api/config/applyYesApply configs
GET/api/config/topology/graphYesTopology node data
POST/api/config/sync-sdYesSync SD value
GET/api/servicesYesAll service statuses
GET/api/services/:nameYesOne service status
POST/api/services/:name/:actionYesService action
POST/api/services/all/:actionYesBulk service action
GET/api/subscribersYesList subscribers
GET/api/subscribers/ip-assignmentsYesIP assignments
GET/api/subscribers/:imsiYesGet subscriber
POST/api/subscribersYesCreate subscriber
PUT/api/subscribers/:imsiYesUpdate subscriber
DELETE/api/subscribers/:imsiYesDelete subscriber
POST/api/subscribers/auto-assign-ipsYesAuto-assign IPs
GET/api/interface-statusYesRAN + UE status
GET/api/backup/listYesList backups
POST/api/backup/configYesCreate config backup
POST/api/backup/mongoYesCreate MongoDB backup
POST/api/backup/restore/configYesRestore config
POST/api/backup/restore/mongoYesRestore MongoDB
POST/api/backup/restore/bothYesRestore both
POST/api/backup/restore/selectiveYesRestore selected NFs
POST/api/backup/diffYesConfig diff vs backup
GET/api/backup/last-configYesLatest backup name
GET/api/backup/settingsYesRetention settings
PUT/api/backup/settingsYesUpdate retention
POST/api/backup/cleanupYesTrigger cleanup
POST/api/backup/restore-defaultsYesFactory defaults
POST/api/auto-config/previewYesPreview auto-config
POST/api/auto-config/applyYesApply auto-config
GET/api/suci/keysYesList SUCI keys
GET/api/suci/next-idYesNext PKI ID
POST/api/suci/keysYesGenerate key
PUT/api/suci/keys/:idYesRegenerate key
DELETE/api/suci/keys/:idYesDelete key
GET/api/auditYesAudit log entries
GET/api/docker/containersYesList containers
GET/api/docker/logs/:containerYesContainer logs
WSws://host:8888/wsYesLog stream + status