OpenAPI Specification

The complete Platform API contract as an OpenAPI 3.1 document, for developers building Applications against Substratal Apps. Browse it in the Interactive Reference, download it, or generate a client from it.

  1. At a glance
  2. Get the document
  3. What the document covers
  4. Authentication in the spec
  5. Reading an operation
    1. Vendor extensions
  6. Generate a client
  7. Validate your integration
  8. Versioning and change policy

At a glance

   
Format OpenAPI 3.1.0, YAML
Document openapi.yaml
Interactive view Interactive Reference (Swagger UI)
Base URL https://api.substratalapps.com/v1
Authentication Authorization: Bearer <token>: a user access token or an API Key (bearerAuth)
Content type application/json; charset=utf-8
Operations 118, every one with a stable operationId
Schemas 109 reusable components under components.schemas

The prose pages under API Reference are authoritative. openapi.yaml is kept in sync with them by hand, and the implementation’s contract tests validate every response against it, so a drift between the two fails CI rather than reaching a client. See Conventions → Machine-readable.

Get the document

curl -O https://compositecode.github.io/substratalapps.com/openapi.yaml

Every published version of this site keeps its own copy of openapi.yaml, so you can pin a client to the spec it was generated from. See Versions.

What the document covers

Operations are grouped by tag, and each tag matches an API Reference page that explains the rules behind it.

Tag Operations Prose reference
Meta 1 Conventions → Health check
Auth 21 Auth
Users 10 Users
Profiles 4 Profiles
Settings 4 Settings
Applications 4 Applications
Entitlements 6 Entitlements
Roles & Permissions 10 Roles & Permissions
Organizations 11 Organizations
Tenancy 6 Tenancy
Billing 7 Billing
Audit 2 Audit
Webhooks 9 Webhooks
Notifications 17 Push Notifications
API Keys 6 API Keys

Notifications are specified, not built yet. The 17 operations under the Notifications tag are the target contract for Push Notifications. Until the reference implementation builds them, each one returns 501 not_implemented, so build clients against the shapes, not against a live server.

Some endpoints live outside /v1 and aren’t in the document’s paths, because they follow external standards instead:

  • GET /.well-known/jwks.json and GET /.well-known/openid-configuration: signing keys and discovery, for verifying app tokens with any standard JWT library. See Auth → Signing keys.
  • /mcp: the MCP Server, generated from this document’s operationIds.

Authentication in the spec

The document declares one security scheme, bearerAuth (HTTP bearer), and applies it globally. The few operations that need no credential, such as GET /health, signup, and login, override it with security: [].

The same header carries both credential types:

Credential Looks like Used by Get one from
User access token A JWT A person, or a client acting for them POST /auth/login, /auth/signup, /auth/mfa/verify, or /auth/token/refresh
API Key satk_live_… or satk_test_… Your backend, service to service API Keys

Some operations accept only one of the two. For example, every Billing operation returns 400 user_token_required when called with an API Key. The operation’s description says so, and its prose page lists the permission it requires.

A satk_test_… key works against the same base URL but only ever sees test-mode records. There’s no separate sandbox host. See Conventions → Authentication.

Reading an operation

Each operation in the document follows the same conventions, so you only need to learn them once:

  • operationId is stable and namespaced (entitlements.grant, users.get). Generated clients use it for method names, and the MCP server uses it for tool names. Renaming one counts as a breaking change.
  • Errors all share one shape ({"error": {"code", "message", "details"}}, with the request id in the X-Request-Id header) through the shared responses BadRequest, Unauthorized, Forbidden, NotFound, Conflict, Unprocessable, TooManyRequests, and ServiceUnavailable. Branch on error.code, never on message. See Conventions → Errors.
  • Pagination uses the shared Limit and Cursor parameters (limit 1–100, default 25). See Conventions → Pagination.
  • Idempotency. Operations that create something take the Idempotency-Key header. Some require it, and their descriptions say which. See Conventions → Idempotency.
  • Concurrency. Single-resource responses carry an ETag header. Send it back as If-Match on PATCH to avoid overwriting someone else’s change. See Conventions → Concurrency.
  • IDs are typed by prefix (usr_, ent_, org_, …). Most are a prefix plus a ULID. Application and Role ids are derived (app_<slug>, role_<slug>_<name>). See Conventions → IDs.
  • me can stand in for your own User id in any /users/{id} path. See Conventions → Addressing yourself.

Vendor extensions

Extension Values Meaning
x-substratal-destructive always | conditional The operation destroys or irreversibly changes data. It’s rejected for API Keys created with restrict_destructive: true, and the MCP server marks it destructiveHint. See API Keys → Agent keys.
x-substratal-destructive-when a description of the request With conditional, only requests matching this are destructive. The rest of the operation stays callable with a restricted key.
x-substratal-phase mvp, phase2, phase3 The Roadmap phase the operation ships in.

Code generators ignore these extensions, so they’re safe to leave in.

Generate a client

Any OpenAPI 3.1 tool works. A few common ones:

# TypeScript types, with no runtime dependency
npx openapi-typescript https://compositecode.github.io/substratalapps.com/openapi.yaml -o substratal.d.ts

# Go: types and a client (this is what the reference implementation uses for its server side)
go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest \
  -generate types,client -package substratal openapi.yaml > substratal.gen.go

# Any of 50+ languages
npx @openapitools/openapi-generator-cli generate -i openapi.yaml -g python -o ./substratal-python

To explore by hand, import openapi.yaml into Postman, Insomnia, Bruno, or any other tool that reads OpenAPI. The Interactive Reference also has “Try it out” built in, against a local devserver.

Validate your integration

  • Lint the document, or your copy of it. Run npx @redocly/cli lint openapi.yaml or npx @stoplight/spectral-cli lint openapi.yaml.
  • Check your mocks against it. npx @stoplight/prism-cli mock openapi.yaml serves a mock API from the document, so you can build a client before you have credentials.
  • Run the reference implementation locally. The repository’s devserver serves every route at http://localhost:8080/v1 with no AWS account. See the repository’s README → Local development. The Interactive Reference can send requests to it once its origin is allowed. See the steps at the top of the Interactive Reference.

Versioning and change policy

The /v1 prefix in the base URL is the API version. info.version in the document (currently a 1.0.0-draft pre-release) is the version of the document itself.

  • Additive changes (new optional fields, new operations, new enum values) ship without a new prefix. Write clients to ignore fields they don’t know, and to handle enum values they don’t recognize.
  • Breaking changes get a new prefix and a deprecation window for the old one. See Non-Functional Requirements → Versioning.
  • Every change is recorded in the Changelog.

Table of contents


Back to top

Substratal Apps Platform API — living specification. This site is the system of record; see git history for how it has changed over time.

This site uses Just the Docs, a documentation theme for Jekyll.