API Reference Appendices
This appendix describes the key API endpoints used by the web interface, CLI, and join scripts.
Use it when you need endpoint-level details for automation, troubleshooting, or custom integrations.
For term definitions, see the Glossary. For step-by-step workflows, see:
API Surface Map
graph LR
A[CLI and Join Scripts] --> B[OAuth Device Authorization]
B --> C[Token Endpoint with DPoP]
C --> D[Join API or Signing API]
D --> E[Policy Evaluation and CA Signing]
F[Web UI] --> G[Session Auth and Admin APIs]
How authentication works on each surface:
- Browser UI uses session-based login and role-guarded routes.
- CLI and join scripts use OAuth device authorization with DPoP-bound tokens.
- Join and signing operations require both the right token scope and a valid request proof.
Device Authorization Endpoints
These endpoints support CLI and script authorization flows.
POST /oauth2/device_authorization
Starts device authorization and returns the information needed to approve the request in a browser.
Request parameters (application/x-www-form-urlencoded):
| Parameter | Required | Type | Description |
|---|---|---|---|
client_id |
Yes | string | OAuth client identifier, for example sshteam-cli. |
scope |
No | string | Requested scope: signing or join. Defaults to join. |
device_name |
No | string | Optional display name for signing-scope device records. |
Example success response (200):
{
"device_code": "dvc_2f9f6f3d7f224d3d9b9e",
"user_code": "ABCD-EFGH",
"verification_uri": "https://<hostname>/device/verify",
"verification_uri_complete": "https://<hostname>/device/verify?user_code=ABCD-EFGH&device_code=dvc_2f9f6f3d7f224d3d9b9e",
"expires_in": 600,
"interval": 5
}
POST /oauth2/token
Exchanges an approved device code for tokens, or refreshes an existing token.
Request parameters (application/x-www-form-urlencoded):
| Parameter | Required | Type | Description |
|---|---|---|---|
grant_type |
Yes | string | Device flow: urn:ietf:params:oauth:grant-type:device_code; refresh flow: refresh_token. |
device_code |
Conditional | string | Required for device-code exchange. |
refresh_token |
Conditional | string | Required for refresh-token exchange. |
client_id |
Conditional | string | Required for device-code exchange; not used by refresh-token exchange. |
DPoP (header) |
Conditional | string | Required for signing scope token exchange and refresh grant handling. |
Example success response (200):
{
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"token_type": "DPoP or Bearer",
"expires_in": 86400,
"scope": "signing or join"
}
Response notes:
token_typeisDPoPfor signing-scope flows.token_typeisBearerfor join-scope flows.
Example pending response (400):
{
"error": "authorization_pending",
"error_description": "Authorization request is still pending"
}
Join and Signing Endpoints
POST /api/v1/ssh/join
Registers a server and returns CA trust material the server can use to validate user certificates.
Headers:
| Header | Required | Description |
|---|---|---|
Authorization |
Yes | Bearer <access-token> with join scope. |
Content-Type |
Yes | application/json. |
Request body fields:
| Field | Required | Type | Description |
|---|---|---|---|
hostname |
Yes | string | Target server hostname or address. |
port |
No | number | SSH service port. Defaults to 22 if omitted. |
osFamily |
Yes | string | Target OS family, for example linux or windows. |
osVersion |
Yes | string | Target OS version string. |
sshFingerprints |
No | array |
Host-key fingerprints collected from the target server. |
name |
No | string | Friendly name shown in the administration UI. |
tags |
No | array |
Tags used for policy targeting. |
timezone |
No | string | Optional server timezone. |
serverId |
No | string | Existing server ID for update-path joins. |
Example success response (200, text/plain):
# TrustedUserCAKeys
ssh-rsa AAAA...
ssh-ed25519 AAAA...
# sshteam-server-id: srv_a18c0c2e
POST /api/v1/ssh/sign
Submits a certificate signing request from CLI flows (sign, issue, agent).
Headers:
| Header | Required | Description |
|---|---|---|
Authorization |
Yes | DPoP <access-token> with signing scope. |
DPoP |
Yes | Proof JWT bound to token and request URL/method. |
Content-Type |
Yes | application/json. |
Request body fields:
| Field | Required | Type | Description |
|---|---|---|---|
principal |
Yes | string | UNIX account requested in the certificate. |
server_fingerprint |
Yes | string | Target server host-key fingerprint. |
public_key |
Yes | string | OpenSSH-formatted public key. |
certificate_type |
No | string | ED25519 or RSA. Defaults to ED25519. |
timezone |
No | string | IANA timezone override for policy window evaluation. |
Example success response (200):
{
"certificate": "ssh-ed25519-cert-v01@openssh.com AAAA..."
}
Example deny response (403):
{
"error": "No certificate policy permits this request"
}
GET /api/v1/devices
Lists registered devices for the current user.
Headers:
| Header | Required | Description |
|---|---|---|
| Session cookie | Yes | Authenticated browser session for current user. |
Example success response (200):
[
{
"id": "dev_77f7a",
"displayName": "macbook-pro",
"createdAt": "2026-07-15T21:03:11Z",
"lastSeenAt": "2026-07-16T05:44:42Z",
"revoked": false
}
]
DELETE /api/v1/devices/{deviceId}
Revokes an existing device registration.
- Revocation takes effect immediately.
- Repeated revoke requests are treated idempotently.
Path and headers:
| Name | Required | Type | Description |
|---|---|---|---|
deviceId (path) |
Yes | string | Device identifier returned by the device list endpoint. |
| Session cookie | Yes | string | Authenticated browser session for current user. |
Example success response (204, no response body).
Error Codes and Expected HTTP Statuses
Use this table as a baseline. Exact payload fields may vary by endpoint.
| Scenario | Typical HTTP status | Notes |
|---|---|---|
| Successful device authorization start | 200 | Returns device/user codes and verification URI |
| Device polling pending approval | 400 | OAuth error body, often authorization_pending |
| Device polling denied | 400 | OAuth error body, often access_denied |
| Device code expired | 400 | OAuth error body, often expired_token |
| Missing or invalid session for protected UI route | 302 or 401/403 | Redirect to /login or auth failure |
| Invalid token or DPoP proof on protected API | 401 | Token invalid, expired, revoked, or proof mismatch |
| Authenticated but not authorized for action | 403 | Role or policy boundary denies action |
| Policy denial on sign request | 403 | Request valid but does not match allow policy |
| Resource conflict on optimistic lock update | 409 | Typical for concurrent policy updates |
| Validation error in request payload | 400 | Field format, required fields, or logical constraints fail |
How to read common status codes:
- 401 means an authentication or proof problem.
- 403 means a permission or policy boundary problem.
- 409 means a concurrency or version conflict.
Example Payloads and Curl Snippets
These snippets are for reference and controlled testing.
Start device authorization:
curl -sS -X POST "https://<hostname>/oauth2/device_authorization" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data "client_id=sshteam-cli&scope=signing"
Poll the token endpoint:
curl -sS -X POST "https://<hostname>/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data "grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=<device_code>&client_id=sshteam-cli"
Sign request with DPoP headers (illustrative):
curl -sS -X POST "https://<hostname>/api/v1/ssh/sign" \
-H "Authorization: DPoP <access_token>" \
-H "DPoP: <proof_jwt>" \
-H "Content-Type: application/json" \
-d '{
"principal": "devops",
"server_fingerprint": "SHA256:example",
"public_key": "ssh-ed25519 AAAA...",
"certificate_type": "ED25519"
}'
Revoke a device:
curl -sS -X DELETE "https://<hostname>/api/v1/devices/<deviceId>" \
-H "Cookie: JSESSIONID=<session_id>"
Treat these examples as contract guidance. Field names and optional attributes may vary with your deployed version, so check before building automation around them.
Pro Tip and Common Gotcha
Pro tip:
- For everyday work, use the
sshteamCLI commands first. Reach for these API calls only when you need deeper diagnostics or custom integrations.
Common gotcha:
- Join and signing endpoints use different auth models (
BearervsDPoP). Mixing those headers produces confusing failures that look like generic auth errors.