HTTP API
Deployed and supported. The fields and commands described here are stable within version v1alpha1.
The StackNGO API is a JSON REST API. The sng CLI and the console use it exclusively:
everything they do, you can do with curl or a client generated from the OpenAPI
specification.
Address and version
| Base URL | https://api.dev.stackngo.fr/v1 |
| OpenAPI 3.1 specification | https://api.dev.stackngo.fr/v1/openapi.yaml (public, no authentication) |
| Version | Everything is prefixed with /v1. There is no version header. |
| Health | GET /healthz (process alive), GET /readyz (dependencies ready), outside /v1 and without authentication |
The routes are documented one by one in Endpoints, generated from the
specification. Each route states the IAM action it requires (deploy:write,
ssm:read, …): this is what a policy must grant for the route to respond with anything
other than 403.
Two-step authentication
- Obtain an identity token (OIDC, from
https://auth.dev.stackngo.app). - Exchange it for a tenant session with
POST /v1/iam/session. - Send the session in
Authorization: Bearer <session>on all of the tenant's routes.
Details, with curl examples, are in Authentication.
A single token per request; the X-Tenant-Token header is refused (400) on every /v1
route, session opening included: the tenant session is enough.
Request conventions
- JSON body:
Content-Type: application/jsonis required onPOST,PUT, andPATCHrequests that carry a body. Otherwise415. - Unknown fields refused: a field not provided for in the body yields
400. The OpenAPI schemas are therefore exhaustive. - Size: a body is limited to 1 MB.
- Names in the path: resource names (
{name},{tenant}) are lowercase DNS labels. The tenant of a request always comes from the session, never from the path or a header; on the/v1/tenants/{tenant}/…routes,{tenant}must be the session's.
Response conventions
Content-Type: application/json.- Lists return an object that wraps the array, named after the resource:
{"secrets": […]},{"apps": […]},{"items": […]}. There is no pagination: lists are complete. - Creations respond
201, deletions204without a body, everything else200. - Errors all have the form
{"error": "message"},415and429included. See Errors. Retry-Afteraccompanies409 policies_pendingresponses (tenant just created) and429s.
Limits and timeouts
300 requests per minute per IP address on the /v1 routes; server timeouts of 30 s for reading and 60 s for writing. Details and
advice in Limits.
Idempotency
PUT /v1/secrets/{name}andPUT /v1/iam/policies/{name}create or replace: replaying the same request gives the same state.POST /v1/applythenPOST /v1/apply/completeare designed to be replayed: an identical manifest is markedunchanged.- Action
POSTs (/scale,/restart,/rotate) are not idempotent.
Generate a client
curl -fsSL https://api.dev.stackngo.fr/v1/openapi.yaml -o openapi.yaml
npx openapi-typescript openapi.yaml -o stackngo.d.ts # TypeScript
go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest -package stackngo openapi.yaml # Go
The StackNGO console is itself generated from this file.
Complete example
# 1. Identity (here: a token already obtained by the CLI)
ID_TOKEN=$(yq '.access_token' ~/.config/stackngo/config.yaml)
# 2. Tenant session
SESSION=$(curl -fsS https://api.dev.stackngo.fr/v1/iam/session \
-H "Authorization: Bearer $ID_TOKEN" -H 'Content-Type: application/json' \
-d '{"tenant":"acme"}' | jq -r .access_token)
# 3. A tenant route
curl -fsS https://api.dev.stackngo.fr/v1/secrets -H "Authorization: Bearer $SESSION"
{"secrets":[{"name":"db-password","version_id":"3","version_stages":{"current":"3"},"updated_at":"2026-10-01T09:12:44Z"}]}