Help 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. AddSubstratal-Versionin aClientMiddleware, so no call site has to pass it. - TypeScript:
openapi-typescript+openapi-fetch. Set the header in the client's defaultheaders. - 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" },
});