This document describes the REST API endpoints available in the zap.stream core streaming server.
The API uses NIP-98 (Nostr HTTP Auth) for authentication. All protected endpoints require an Authorization header with
the following format:
Authorization: Nostr <base64-encoded-nostr-event>
The Nostr event must:
- Be of kind 27235 (NIP-98 HTTP Auth)
- Have a valid signature
- Include appropriate
methodandurltags matching the request - Have a recent timestamp (within acceptable time window)
All API endpoints are prefixed with /api/v1/
GET /api/v1/time
Authentication: Not required
Response:
{
"time": 1640995200000
}Description: Returns the current server time as a Unix timestamp in milliseconds. Useful for client synchronization and NIP-98 authentication timestamp validation.
GET /api/v1/account
Authentication: Required
Response:
{
"endpoints": [
{
"name": "string",
"url": "string",
"key": "string",
"capabilities": [
"string"
],
"cost": {
"unit": "string",
"rate": 0.0
}
}
],
"balance": 0,
"tos": {
"accepted": false,
"link": "string"
},
"forwards": [
{
"id": 0,
"name": "string"
}
],
"details": {
"title": "string",
"summary": "string",
"image": "string",
"tags": [
"string"
],
"content_warning": "string",
"goal": "string"
},
"has_nwc": false
}Description: Returns comprehensive account information including streaming endpoints with the primary stream key ( which creates a new Nostr event for each stream), account balance, terms of service status, RTMP forward destinations, stream details, and NWC (Nostr Wallet Connect) configuration.
Response Fields (GET):
has_nwc: Boolean indicating whether NWC is configured for this account
PATCH /api/v1/account
Authentication: Required
Request Body:
{
"accept_tos": true,
"nwc": "nostr+walletconnect://...",
"remove_nwc": false
}Response:
{}Description: Updates account settings, including accepting terms of service and configuring NWC (Nostr Wallet Connect) for automated withdrawals.
NWC Configuration:
nwc(optional): A Nostr Wallet Connect URI string in the formatnostr+walletconnect://...- When provided, the server will validate the NWC connection and ensure it has
pay_invoicepermissions - The NWC URI should be obtained from a compatible Nostr wallet that supports the NWC protocol
- When provided, the server will validate the NWC connection and ensure it has
remove_nwc(optional): Boolean flag to remove the currently configured NWC connection- Set to
trueto disconnect and remove the current NWC configuration - Cannot be used simultaneously with the
nwcparameter
- Set to
- NWC allows for automated withdrawal processing through connected Nostr wallet applications
GET /api/v1/topup?amount=<amount>
Authentication: Required
Query Parameters:
amount(required): Amount to top up in millisatoshi
Response:
{
"pr": "string"
}Description: Generates a Lightning Network payment request for adding funds to the account balance. Returns a payment request (invoice) that can be paid to credit the account.
POST /api/v1/withdraw?invoice=<payment_request>
Authentication: Required
Query Parameters:
invoice(required): Lightning Network payment request to pay
Response:
{
"fee": 0,
"preimage": "string"
}Description: Withdraws funds from the account balance by paying a Lightning Network invoice. Returns the fee charged and payment preimage on success.
PATCH /api/v1/event
Authentication: Required
Request Body:
{
"id": "string",
"title": "string",
"summary": "string",
"image": "string",
"tags": [
"string"
],
"content_warning": "string",
"goal": "string"
}Response:
{}Description: Updates stream event metadata such as title, description, image, tags, content warnings, and goals.
POST /api/v1/forward
Authentication: Required
Request Body:
{
"name": "string",
"target": "string"
}Response:
{
"id": 0
}Description: Creates a new RTMP forward destination. RTMP forwards allow streaming to multiple platforms simultaneously by forwarding the stream to external RTMP endpoints (e.g., YouTube, Twitch, etc.).
DELETE /api/v1/forward/{id}
Authentication: Required
Path Parameters:
id: Forward ID to delete
Response:
{}Description: Removes an RTMP forward destination by ID.
PATCH /api/v1/forward/{id}
Authentication: Required
Path Parameters:
id: Forward ID to delete
Request Body:
{
"disabled": true
}Response:
{}Description: Update an RTMP forward destination by ID.
GET /api/v1/history
Authentication: Required
Response:
{
"items": [
{
"created": 1704067200,
"type": 0,
"amount": 1000.5,
"desc": "Lightning top-up"
},
{
"created": 1704153600,
"type": 1,
"amount": 250.0,
"desc": "Stream: My Live Stream"
},
{
"created": 1704240000,
"type": 1,
"amount": 50.0,
"desc": "Withdrawal"
}
],
"page": 0,
"page_size": 50
}Description: Returns paginated transaction history for the account including payments, withdrawals, and streaming costs.
Response Fields:
created: Unix timestamp when the transaction occurredtype: Transaction type -0for credits (payments received, top-ups, admin credits, zaps),1for debits ( withdrawals, streaming costs)amount: Transaction amount in satoshis (sats)desc: Description of the transaction - may include stream titles, "Withdrawal", "Admin Credit", or Nostr zap content
GET /api/v1/keys
Authentication: Required
Response:
[
{
"id": 0,
"key": "string",
"created": 0,
"expires": 0,
"stream_id": "string"
}
]Description: Returns all additional stream keys for the account. These are separate from the primary stream key ( returned in account info) and are used for fixed stream events, planned streams, or 24/7 streams with pre-defined Nostr events.
POST /api/v1/keys
Authentication: Required
Request Body:
{
"event": {
"title": "string",
"summary": "string",
"image": "string",
"tags": [
"string"
],
"content_warning": "string",
"goal": "string"
},
"expires": "2024-01-01T00:00:00Z"
}Response:
{
"key": "string",
"event": "string"
}Description: Creates an additional stream key with pre-defined event metadata and optional expiration time. Unlike the primary stream key (which creates a new Nostr event each time), these keys are tied to a specific Nostr event and are ideal for planned streams, scheduled events, or 24/7 streaming scenarios.
DELETE /api/v1/stream/{id}
Authentication: Required
Path Parameters:
id: Stream ID (UUID) to delete
Response:
{}Description: Deletes a stream. Users can only delete their own streams. Also publishes a Nostr deletion event if the stream has an associated Nostr event.
GET /.well-known/lnurlp/{name}
Authentication: Not required
Path Parameters:
name: User pubkey (hex encoded)
Response:
{
"callback": "https://example.com/api/v1/zap/{pubkey}",
"maxSendable": 1000000000,
"minSendable": 1000,
"tag": "payRequest",
"metadata": "[[\"text/plain\", \"Zap for {pubkey}\"]]",
"commentAllowed": null,
"allowsNostr": true,
"nostrPubkey": "server_pubkey_here"
}Description: LNURL pay endpoint for Lightning Address support. Returns payment parameters for zapping a user.
GET /api/v1/zap/{pubkey}
Authentication: Not required
Path Parameters:
pubkey: Target user's pubkey (hex encoded)
Query Parameters:
amount(required): Amount to zap in millisatoshinostr(optional): Base64-encoded Nostr zap request event
Response:
{
"pr": "lnbc..."
}Description: Handles the LNURL pay callback. Creates a Lightning invoice for zapping the specified user. Supports Nostr zap requests for proper zap attribution.
WS /api/v1/ws
Protocol: WebSocket
Description: Provides real-time streaming metrics via WebSocket connection for both streamer dashboards and admin interfaces. Supports role-based access control with different metric types based on user permissions.
WebSocket authentication uses NIP-98 (Nostr HTTP Auth) via JSON messages after connection establishment. The token should be a base64-encoded NIP-98 event (without the "Authorization: Nostr " prefix).
{
"type": "Auth",
"data": {
"token": "base64_encoded_nip98_event_here"
}
}NIP-98 Event Requirements:
- Event kind: 27235 (NIP-98 HTTP Auth)
- Valid signature and timestamp (within 120 seconds)
- URL tag:
ws://yourserver.com/api/v1/ws(WebSocket URL) - Method tag:
GET - The event's pubkey determines user permissions (admin status checked via database)
{
"type": "AuthResponse",
"data": {
"success": true,
"is_admin": true,
"pubkey": "npub1..."
}
}or for regular users:
{
"type": "AuthResponse",
"data": {
"success": true,
"is_admin": false,
"pubkey": "npub1..."
}
}{
"type": "SubscribeStream",
"data": {
"stream_id": "stream_123"
}
}Authorization: Authenticated users can subscribe to stream metrics. Regular users can only access their own streams, while admins can access any stream.
{
"type": "SubscribeOverall",
"data": null
}Authorization: Admin access required.
{
"type": "StreamMetrics",
"data": {
"stream_id": "stream_123",
"started_at": "2024-01-01T12:00:00Z",
"last_segment_time": "2024-01-01T13:00:00Z",
"viewers": 42,
"average_fps": 30.0,
"target_fps": 30.0,
"frame_count": 108000,
"endpoint_name": "Standard",
"input_resolution": "1920x1080",
"ip_address": "192.168.1.100",
"ingress_name": "RTMP",
"endpoint_stats": {
"RTMP": {
"name": "RTMP",
"bitrate": 2500000
},
"HLS": {
"name": "HLS",
"bitrate": 2400000
}
}
}
}Description: Real-time metrics for individual streams containing pipeline performance data and viewer counts.
Broadcast automatically when metrics are updated for subscribed streams. The endpoint_stats field contains
per-endpoint bitrate information for all active ingress and egress endpoints.
{
"type": "NodeMetrics",
"data": {
"node_name": "zsc-node-01",
"cpu": 0.65,
"memory_used": 2147483648,
"memory_total": 8589934592,
"uptime": 86400
}
}Description: Individual node performance metrics broadcast every 5 seconds for subscribed admin clients. Each streaming node reports its own system metrics including CPU usage (as a ratio from 0.0 to 1.0), memory usage in bytes, and uptime. Available only to admin users. Clients can aggregate data from multiple nodes to compute system-wide statistics.
{
"type": "Error",
"data": {
"message": "Authentication required"
}
}interface EndpointStats {
name: string;
bitrate: number;
}
interface ActiveStreamInfo {
node_name: string;
stream_id: string;
started_at: string;
last_segment_time: string;
viewers: number;
average_fps: number;
target_fps: number;
frame_count: number;
endpoint_name: string;
input_resolution: string;
ip_address: string;
ingress_name: string;
endpoint_stats: Record<string, EndpointStats>;
}
interface NodeInfo {
node_name: string;
cpu: number;
memory_used: number;
memory_total: number;
uptime: number;
}
type MetricMessage =
| { type: "Auth"; data: { token: string } }
| { type: "SubscribeStream"; data: { stream_id: string } }
| { type: "SubscribeOverall"; data: null }
| { type: "StreamMetrics"; data: ActiveStreamInfo }
| { type: "NodeMetrics"; data: NodeInfo }
| { type: "AuthResponse"; data: { success: boolean; is_admin: boolean; pubkey: string } }
| { type: "Error"; data: { message: string } };Authentication Flow:
Client → { type: "Auth", data: { token: "base64_nip98_event" } }
Server → { type: "AuthResponse", data: { success: true, is_admin: false, pubkey: "npub1..." } }
Stream Subscription (Users):
Client → { type: "SubscribeStream", data: { stream_id: "stream_123" } }
Server → { type: "StreamMetrics", data: { ... ActiveStreamInfo } } (on updates)
Overall Metrics Subscription (Admins Only):
Client → { type: "SubscribeOverall", data: null }
Server → { type: "NodeMetrics", data: { ... NodeInfo } } (every 5 seconds)
Server → { type: "StreamMetrics", data: { ... ActiveStreamInfo } } (on updates)
Error Responses:
Server → { type: "Error", data: { message: "Authentication required" } }
Server → { type: "Error", data: { message: "Access denied: You can only access your own streams" } }
Server → { type: "Error", data: { message: "Admin access required for overall metrics" } }
- Automatic Reconnection: Clients should implement automatic reconnection with exponential backoff
- Heartbeat: The server sends node metrics every 5 seconds; clients can detect disconnection if no messages received for 10+ seconds
- Error Handling: Always handle
Errormessage types and display appropriate user feedback
- Node Metrics: Broadcast every 5 seconds for subscribed admin clients
- Stream Metrics: Broadcast in real-time when stream metrics are updated
- Stream Ownership: Regular users can only access their own streams; admins can access any stream
- Each client connection can subscribe to multiple streams (admin) or specific streams they own (regular users)
- No additional rate limiting is currently implemented for WebSocket connections
All endpoints return appropriate HTTP status codes:
200 OK- Successful request400 Bad Request- Invalid request parameters or body401 Unauthorized- Missing or invalid authentication404 Not Found- Resource not found500 Internal Server Error- Server error
Error responses include a JSON body with error details where applicable.
The API may implement rate limiting based on account balance and usage patterns. Specific limits are not documented but will be enforced server-side.
The API includes CORS headers allowing cross-origin requests from web applications:
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: HEAD, GET, PATCH, DELETE, POST, OPTIONS
All API endpoints expect and return application/json content type unless otherwise specified.