Contact us
v1.0.0
OpenAPI 3.1.0

Umoja Portal API

Public REST API for the Umoja satellite operations portal. All endpoints live under /api/v1; authenticate with an OAuth2 client_credentials token and pass it as Authorization: Bearer <token>.

Getting a token — ask Remos to provision an API client (client_id / client_secret) for your group, then exchange them directly with Keycloak:

curl -X POST https://umoja.remosspace.com:8443/realms/expedite/protocol/openid-connect/token \
  -d grant_type=client_credentials \
  -d client_id=<your-client-id> \
  -d client_secret=<your-client-secret>

The response's access_token is your bearer for every call below. If your group already has portal access, you can also mint a short-lived test token yourself from Profile → API access without ever handling the secret.


Realtime & Data Integration

Three ways to get pass data out of Umoja programmatically:

  1. portal-link: a small client that exposes the live pass as local TCP ports for a Mission Control System (Yamcs, COSMOS/OpenC3, Gpredict, scripts).
  2. The realtime WebSocket: /ws/realtime, the same live feed the portal itself consumes.
  3. The mission-data API: list and download recordings after a pass.

All three authenticate with your Keycloak token and respect the same group and booking-window rules as the portal.

portal-link opens local TCP ports on your machine so an MCS can exchange telemetry and telecommands with the hub during a pass. It speaks the hub's TCP bridge protocol and handles sign-in for you.

Quick start

./portal-link \
  --hub bridge.umoja.remosspace.com:10100 \
  --station <your-station-id> \
  --booking <booking-uuid> \
  --kc-url https://umoja.remosspace.com:8443/realms/expedite

On first run it prints a URL to open in your browser to sign in. After sign-in:

  1. The client connects to the hub over TLS.
  2. A local TM server starts on localhost:10025.
  3. A local TC server starts on localhost:10026.

Omit --booking to get an interactive picker of your active/upcoming passes.

Point your MCS at the local ports

  • TM data source: localhost:10025 (TCP)
  • TC data sink: localhost:10026 (TCP)

CLI reference

Usage: portal-link [flags]

Required:
  --hub             Hub TCP bridge address (e.g. bridge.umoja.remosspace.com:10100)
  --kc-url          Keycloak realm URL

Optional:
  --api-url         Hub HTTP API URL (auto-derived from --hub if unset)
  --station         Ground station client ID (e.g. umoja)
  --booking         Booking UUID (interactive picker if omitted)
  --streams         Comma-separated streams (default: tm,tc)
                    Valid: tm,tc,antenna,doppler,ephemeris,command
  --tm-port         Local TM port (default: 10025)
  --tc-port         Local TC port (default: 10026)
  --doppler-port    Local doppler port (default: 10027)
  --ephemeris-port  Local ephemeris port (default: 10028)
  --antenna-port    Local antenna port (default: 10029)
  --command-port    Local command-echo port (default: 10030)
  --client-id       Keycloak client ID (default: portal-link)
  --tls             Enable TLS to hub (default: true)
  --insecure        Skip TLS certificate verification (testing only)
  --verbose         Enable debug logging
  --version         Show version

Streams and ports

Each requested stream is exposed on its own local TCP port. Multiple tools may connect to the same port at once. Every connection gets a copy of the stream.

Stream Default port Direction Purpose
tm 10025 hub → MCS Telemetry frames from the satellite.
tc 10026 MCS → hub Telecommand frames to the satellite.
doppler 10027 hub → MCS Doppler shift updates during the pass.
ephemeris 10028 hub → MCS Predicted trajectory for the pass.
antenna 10029 hub → MCS Antenna pointing telemetry (az/el).
command 10030 hub → MCS Echo of telecommands as they leave the radio.

Request more than the default tm,tc by listing them:

./portal-link \
  --hub bridge.umoja.remosspace.com:10100 \
  --station <your-station-id> --booking <booking-uuid> \
  --kc-url https://umoja.remosspace.com:8443/realms/expedite \
  --streams tm,tc,antenna,doppler,ephemeris,command

MCS configuration examples

Yamcs
dataLinks:
  - name: remos-tm
    class: org.yamcs.tctm.TcpTmDataLink
    host: localhost
    port: 10025
    stream: tm_realtime
  - name: remos-tc
    class: org.yamcs.tctm.TcpTcDataLink
    host: localhost
    port: 10026
    stream: tc_realtime
COSMOS (OpenC3)
INTERFACE REMOS_TM_INT tcpip_client_interface.rb localhost 10025 10025 10.0 nil
INTERFACE REMOS_TC_INT tcpip_client_interface.rb localhost 10026 10026 10.0 nil
Custom script (Python)
import socket

# Receive TM
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("localhost", 10025))
while True:
    data = sock.recv(4096)
    if not data:
        break
    process_telemetry(data)
# Send TC
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("localhost", 10026))
sock.sendall(bytes.fromhex("A1B2C3D4"))

Realtime WebSocket

If you'd rather consume the feed directly (the same one the portal uses), open a WebSocket per stream:

wss://<host>/ws/realtime?station_id=<client_id>&stream=<stream>[&booking_id=<uuid>]

Authenticate by sending your Keycloak bearer token as a WebSocket subprotocol:

new WebSocket(url, ["bearer", "<your-jwt>"])

Available streams: spectrum, constellation, doppler, ephemeris, antenna, telemetry, command.

Frames are binary, prefixed with a small header: a 2-byte magic (EX), a 1-byte stream tag, and the 16-byte station client ID (NUL-padded), followed by the stream payload (e.g. spectrum is 1024 × [freq, power] float32 pairs). The hub refreshes nothing for you: keep your token fresh and reconnect on drop.

Mission-data API

After a pass, list and download its recordings over REST. Send your Keycloak token as Authorization: Bearer <jwt>.

Endpoint Method Purpose
/api/data/files GET List files (filter by station_id, booking_id, status, from_ts/to_ts, search; group_by_pass).
/api/data/files/summary GET Aggregate counts and bytes.
/api/data/files/{file_id}/link POST Mint a short-lived signed download URL for one file.
/api/data/bookings/{booking_id}/link POST Mint a signed URL for the whole pass as a ZIP bundle.

Minting a link, then downloading, keeps the long-lived token off the wire:

# 1. mint a signed URL (bearer token)
curl -X POST https://umoja.remosspace.com/api/data/files/$FILE_ID/link \
  -H "Authorization: Bearer $TOKEN"
# -> { "download_url": "...", "expires_in": 900 }

# 2. download (the signed URL carries its own token)
curl -L -o capture.iq "<download_url>"

See the REST API tab for the full, interactive reference.

Session window

A live session (portal-link or WebSocket) is tied to a booking and only flows during its window:

  • Earliest: AOS − 5 minutes. Latest: LOS + 5 minutes.
  • Connect before the window opens and the hub rejects the session until the booking becomes active.
  • The session disconnects automatically once the window closes.
  • portal-admin accounts bypass the window for live diagnostics.

Troubleshooting

Symptom Cause What to do
AUTH_FAIL:invalid_token Token expired or malformed. Re-run portal-link / refresh your token.
AUTH_FAIL:no_active_booking No valid booking in the current window. Check the window under Bookings; wait for AOS − 5 min.
AUTH_FAIL:station_not_found Wrong --station. Use the client ID shown on the Stations page.
AUTH_FAIL:server_full Too many concurrent sessions. Retry shortly, or contact support.
TM stops mid-pass Network drop or pass ended. portal-link auto-reconnects until LOS + 5 min.
TC not delivered Station offline. Check the station's live state on Stations.
Server:https://umoja.remosspace.com

Production hub

Client Libraries

Satellites

List satellites visible to the caller.

Query Parameters
  • limit
    Type: integer · Limit
    min:  
    1
    max:  
    500

    Page size; max 500.

  • offset
    Type: integer · Offset
    min:  
    0

    0-based offset for pagination.

Responses
  • application/json
  • application/json
Request Example for get/api/v1/satellites
curl https://umoja.remosspace.com/api/v1/satellites \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "data": {
    "items": [
      {
        "name": "string",
        "norad_id": "string"
      }
    ],
    "pagination": {
      "limit": 1,
      "offset": 1,
      "total": 1
    }
  },
  "message": "string",
  "request_id": "string",
  "status": "ok",
  "timestamp": 1
}

Get satellite metadata from the hub TLE cache.

Path Parameters
  • norad
    Type: string · Norad
    required
Responses
  • application/json
  • application/json
  • application/json
  • application/json
Request Example for get/api/v1/satellites/{norad}
curl 'https://umoja.remosspace.com/api/v1/satellites/{norad}' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "data": {
    "name": "string",
    "norad_id": "string",
    "tle1": "string",
    "tle2": "string"
  },
  "message": "string",
  "request_id": "string",
  "status": "ok",
  "timestamp": 1
}

List Mission Templates

List this org's mission templates for a satellite.

Path Parameters
  • norad
    Type: string · Norad
    required
Responses
  • application/json
  • application/json
Request Example for get/api/v1/satellites/{norad}/mission-templates
curl 'https://umoja.remosspace.com/api/v1/satellites/{norad}/mission-templates' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
null

Create Mission Template

Create a mission template for this satellite under the caller's org.

Path Parameters
  • norad
    Type: string · Norad
    required
Body·
required
application/json
  • name
    Type: string · Name
    min length:  
    1
    max length:  
    120
    required
  • description
    Type: string · Description
    max length:  
    500
    nullable
  • is_default
    Type: boolean · Is Default
  • mission_config
    Type: object · Mission Config
Responses
  • application/json
  • application/json
Request Example for post/api/v1/satellites/{norad}/mission-templates
curl 'https://umoja.remosspace.com/api/v1/satellites/{norad}/mission-templates' \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "description": "",
  "is_default": false,
  "mission_config": {
    "additionalProperty": "anything"
  },
  "name": ""
}'
null

Delete Mission Template

Path Parameters
  • norad
    Type: string · Norad
    required
  • template_id
    Type: string · Template Id
    required
Responses
  • application/json
  • application/json
Request Example for delete/api/v1/satellites/{norad}/mission-templates/{template_id}
curl 'https://umoja.remosspace.com/api/v1/satellites/{norad}/mission-templates/{template_id}' \
  --request DELETE \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
null

Update Mission Template

Path Parameters
  • norad
    Type: string · Norad
    required
  • template_id
    Type: string · Template Id
    required
Body·
required
application/json
  • description
    Type: string · Description
    max length:  
    500
    nullable
  • is_default
    Type: boolean · Is Defaultnullable
  • mission_config
    Type: object · Mission Confignullable
  • name
    Type: string · Name
    min length:  
    1
    max length:  
    120
    nullable
Responses
  • application/json
  • application/json
Request Example for patch/api/v1/satellites/{norad}/mission-templates/{template_id}
curl 'https://umoja.remosspace.com/api/v1/satellites/{norad}/mission-templates/{template_id}' \
  --request PATCH \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "description": "",
  "is_default": true,
  "mission_config": {
    "additionalProperty": "anything"
  },
  "name": ""
}'
null

Set Default Mission Template

Path Parameters
  • norad
    Type: string · Norad
    required
  • template_id
    Type: string · Template Id
    required
Responses
  • application/json
  • application/json
Request Example for post/api/v1/satellites/{norad}/mission-templates/{template_id}/default
curl 'https://umoja.remosspace.com/api/v1/satellites/{norad}/mission-templates/{template_id}/default' \
  --request POST \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
null

List Station Template Assignments

List this org's (station -> template) assignments for a satellite.

Path Parameters
  • norad
    Type: string · Norad
    required
Responses
  • application/json
  • application/json
Request Example for get/api/v1/satellites/{norad}/station-template-assignments
curl 'https://umoja.remosspace.com/api/v1/satellites/{norad}/station-template-assignments' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
null

Put Station Template Assignments

Assign a mission template to one or more stations for this satellite.

Conflict = a listed station is already pinned to a DIFFERENT template under this org. Returns 409 with the conflicting rows unless force=True, in which case all listed stations are overwritten to template_id.

Path Parameters
  • norad
    Type: string · Norad
    required
Body·
required
application/json
  • template_id
    Type: string · Template Id
    required
  • force
    Type: boolean · Force
  • station_client_ids
    Type: array string[] · Station Client Ids
Responses
  • application/json
  • application/json
Request Example for put/api/v1/satellites/{norad}/station-template-assignments
curl 'https://umoja.remosspace.com/api/v1/satellites/{norad}/station-template-assignments' \
  --request PUT \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "force": false,
  "station_client_ids": [
    ""
  ],
  "template_id": ""
}'
null

Delete Station Template Assignment

Path Parameters
  • norad
    Type: string · Norad
    required
  • station_client_id
    Type: string · Station Client Id
    required
Responses
  • application/json
  • application/json
Request Example for delete/api/v1/satellites/{norad}/station-template-assignments/{station_client_id}
curl 'https://umoja.remosspace.com/api/v1/satellites/{norad}/station-template-assignments/{station_client_id}' \
  --request DELETE \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
null

Get the current TLE for a satellite (override or agent-fetched).

Return TLE for the satellite.

Priority: hub-side override (if any) > agent-fetched value via /track/gettle. The override is set via POST /api/v1/satellites/{norad}/tle. Station-scoped overrides are not surfaced here yet; a global override (station_id IS NULL) takes precedence whenever present.

Path Parameters
  • norad
    Type: string · Norad
    required
Responses
  • application/json
  • application/json
Request Example for get/api/v1/satellites/{norad}/tle
curl 'https://umoja.remosspace.com/api/v1/satellites/{norad}/tle' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN'
{
  "data": {
    "meta": {
      "additionalProperty": "anything"
    },
    "norad_id": "string",
    "set_at": "string",
    "source": "override",
    "tle1": "string",
    "tle2": "string"
  },
  "message": "string",
  "request_id": "string",
  "status": "ok",
  "timestamp": 1
}

Transceiver (Collapsed)

Models