Substratal Apps Substratal Apps Hub specs
GitHub

Help Developers

Developers

API quickstart

Base URL: https://api.substratalapps.com. There's no version in the path. Every request sends Substratal-Version: 1.

1. Check the API is up

curl -s https://api.substratalapps.com/health
# {"status":"ok"}

2. Call an endpoint with an API key

export SUBSTRATAL_KEY=satk_test_…   # a test-mode key: everything it writes is invisible to live credentials

curl -s 'https://api.substratalapps.com/users?limit=10' \
  -H 'Substratal-Version: 1' \
  -H "Authorization: Bearer $SUBSTRATAL_KEY"

Leave out the header and you get:

{ "error": { "code": "api_version_required", "message": "This request requires a Substratal-Version header.", "details": { "supported": ["1"] } } }

3. Page through a list

Lists take limit (1–100, default 25) and cursor, and return page.next_cursor and page.has_more:

curl -s "https://api.substratalapps.com/audit-events?limit=100&cursor=$NEXT" -H 'Substratal-Version: 1' -H "Authorization: Bearer $SUBSTRATAL_KEY"

4. Create something safely

Send an Idempotency-Key on creates. Retrying with the same key returns the original result instead of a duplicate. It's required when granting access:

curl -s https://api.substratalapps.com/users/usr_01JAG…/entitlements \
  -H 'Substratal-Version: 1' -H "Authorization: Bearer $SUBSTRATAL_KEY" \
  -H 'Content-Type: application/json' -H 'Idempotency-Key: order-10443' \
  -d '{"application_id":"app_timetrack","source":"purchase"}'

5. Update without losing someone else's change

Every single-resource GET returns an ETag. Send it back as If-Match on PATCH or DELETE. If someone changed the resource in between, you get 409 version_conflict instead of silently overwriting their change.

Errors

Every error has the same shape: {"error": {"code", "message", "details"}}. Branch on code, never on message. Field errors arrive in details.fields. A 429 carries Retry-After. Every response carries X-Request-Id, so quote it when you ask for help.

Generated clients

Generate a client from the OpenAPI document rather than writing HTTP by hand:

  • Swift: swift-openapi-generator. Add Substratal-Version in a ClientMiddleware, so no call site has to pass it.
  • TypeScript: openapi-typescript + openapi-fetch. Set the header in the client's default headers.
  • Kotlin, C#, others: any OpenAPI 3.1 generator. Configure the header globally.

Generators often emit closed enums. Configure them, or post-process the spec, so an unknown enum value never fails a whole response. The API adds enum values without a version bump.

// Swift: one middleware sends the version on every request.
struct APIVersionMiddleware: ClientMiddleware {
    func intercept(_ request: HTTPRequest, body: HTTPBody?, baseURL: URL, operationID: String,
                   next: (HTTPRequest, HTTPBody?, URL) async throws -> (HTTPResponse, HTTPBody?)) async throws -> (HTTPResponse, HTTPBody?) {
        var request = request
        request.headerFields[HTTPField.Name("Substratal-Version")!] = "1"
        return try await next(request, body, baseURL)
    }
}
// TypeScript (openapi-fetch)
import createClient from "openapi-fetch";
import type { paths } from "./substratal";
export const api = createClient<paths>({
  baseUrl: "https://api.substratalapps.com",
  headers: { "Substratal-Version": "1" },
});

Last updated 2026-10-23 · Built 2026-10-25 from a9ef3be · API 1.0.0-draft.13 · Substratal-Version: 1