API Conventions
The complete API contract for SeptemCore: CRUD verb dictionary, action endpoints, response envelopes, cursor pagination, Idempotency-Key, UUID v7, ISO 8601, and rate limit headers.
All public REST endpoints served at https://api.septemcore.com/v1/
follow a single, consistent contract. This page is the normative
reference. Deviating from it in a module or platform service is a
build-blocking lint error.
Base URL
https://api.septemcore.com/v1/{resource}All requests require a bearer token unless the endpoint is documented as unauthenticated:
Authorization: Bearer <access_token>
Content-Type: application/json1. CRUD Verb Dictionary
The platform uses exactly five CRUD verbs and their fixed HTTP
method mappings. Synonyms (get, fetch, find, remove, put,
add) are prohibited at every layer — URL, service method, SDK export.
| Operation | Service method | HTTP method | URL pattern |
|---|---|---|---|
| Create | createEntity() | POST | /v1/{entities} |
| Retrieve one | retrieveEntity() | GET | /v1/{entities}/:id |
| List | listEntities() | GET | /v1/{entities} |
| Update (partial) | updateEntity() | PATCH | /v1/{entities}/:id |
| Delete | deleteEntity() | DELETE | /v1/{entities}/:id |
PUT is prohibited. All updates are partial (PATCH). PUT implies
full resource replacement, which is unsafe in a multi-tenant environment
where clients may not have read access to all fields.
2. Action Endpoints
Not every operation maps to a CRUD verb. Operations with side effects that are not resource creation use the action endpoint pattern:
POST /v1/{entities}/:id/{action}| Type | Pattern | Examples |
|---|---|---|
| Action on resource | POST /v1/{entities}/:id/{action} | /wallets/:id/credit, /files/:id/restore, /notifications/:id/retry |
| Standalone action | POST /v1/{domain}/{action} | /auth/login, /auth/logout, /auth/refresh |
Rules:
- Always
POST— actions have side effects. - Return
200 OKfor synchronous results,202 Acceptedfor async operations (the caller should poll or listen on Event Bus). - Never use
GETfor actions —GETmust be free of side effects. - Never mix CRUD and action on the same URL.
OIDC/OAuth 2.1 exceptions (names defined by the protocol standard):
GET /.well-known/openid-configuration
GET /.well-known/jwks.json
POST /v1/oauth/token
GET /v1/oauth/authorize
POST /v1/oauth/revoke
POST /v1/oauth/introspect
GET /v1/oauth/userinfo3. Response Envelopes
Single resource
A single-resource response is a bare object — no wrapper:
// GET https://api.septemcore.com/v1/users/usr_01j9p3kx2e00000000000000
// 200 OK
{
"id": "01j9p3kx2e00000000000000",
"email": "[email protected]",
"name": "Alice",
"tenant_id": "01j9p3kz5f00000000000000",
"created_at": "2026-04-15T09:12:00Z",
"deleted_at": null
}List resource
A list response uses a data array and a meta pagination object:
// GET https://api.septemcore.com/v1/users?page_size=20
// 200 OK
{
"data": [
{ "id": "01j9p3kx2e00000000000000", "email": "[email protected]" },
{ "id": "01j9p3m1aa00000000000000", "email": "[email protected]" }
],
"next_page_token": "eyJpZCI6IjAxajlwM20xYWEwMDAwMDAwMDAwMDAwMCJ9"
}Rules:
dataanderrorsnever appear in the same response body.nullfields are included in responses, not omitted.- All field names use
snake_case.
4. Field Naming
| Convention | Format | Example |
|---|---|---|
| All JSON fields | snake_case | tenant_id, created_at, is_active |
| All dates / timestamps | ISO 8601 UTC | "2026-04-15T09:12:00Z" |
| All resource IDs | UUID v7 string | "01j9p3kx2e00000000000000" |
| Soft-delete marker | deleted_at (nullable) | null or "2026-05-01T00:00:00Z" |
| TTL values | Seconds (integer) | 3600 |
UUID v7 is time-sortable (k-sorted). IDs are lexicographically
ordered by creation time, enabling index-friendly cursor pagination
without a separate created_at sort column.
Dates without a timezone designator (2026-04-15T09:12:00) are rejected
at the OpenAPI validation layer with 400 Bad Request.
5. Cursor Pagination
All list endpoints strictly follow the AIP-158 pagination standard. Offset / page-based pagination is prohibited (it is unstable under concurrent writes and suffers from the N-scan performance problem).
Request
GET /v1/users?page_size=20&page_token=eyJpZCI6Ii4uLiJ9| Parameter | Type | Required | Default | Max |
|---|---|---|---|---|
page_size | integer | No | 20 | 100 |
page_token | string | No | — | — |
If page_size exceeds the maximum, the server silently clamps it to 100.
Response
{
"data": [...],
"next_page_token": "eyJpZCI6IjAxajlwM20xYWEwMDAwMDAwMDAwMDAwMCJ9"
}The next_page_token value is an opaque base64 string. Clients must treat
it as a black box — parsing its content or constructing it manually is
unsupported and will break across versions.
- An empty string or omitted
next_page_tokenmeans you have reached the end of the list. has_moreis prohibited in responses (AIP-158 canonical form).totalCount,totalPages,limit, andoffsetare prohibited in list APIs. The only exception isGET /v1/{entities}/count— a dedicated admin endpoint that returns{ "count": N }for dashboard widgets.
6. Idempotency
Mutating POST requests on financial and critical paths require an
Idempotency-Key header:
POST https://api.septemcore.com/v1/wallets/01j9.../credit
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{ "amount": 5000, "currency": "USD", "description": "Top-up" }Endpoints requiring Idempotency-Key
| Service | Endpoints |
|---|---|
| Money | credit, debit, transfer, hold, confirm, cancel, reversal |
| Auth | register, invite |
| Notify | send, batch |
| Files | upload |
| Module Registry | POST /modules |
Behaviour
| Situation | Response |
|---|---|
| First call — succeeds | Execute, cache response 24 h, return 200/201. |
| Repeat call — same key | Return cached original response + Idempotency-Replayed: true header. |
| First call — fails (4xx/5xx) | Not cached. Retry with same key re-executes the operation. |
| Missing key (mutating POST) | 400 Bad Request — Idempotency-Key header is required. |
| Key is not UUID v4 | 400 Bad Request — Idempotency-Key must be UUID v4. |
Financial idempotency keys are persisted in PostgreSQL (source of truth) plus a Valkey fast-path cache. Non-financial keys are Valkey-only. Both have a 24-hour TTL.
7. API Versioning
All endpoints are versioned by URL path:
/v1/{resource} — current stable
/v2/{resource} — next major (when breaking changes are necessary)Deprecation lifecycle
| Stage | Action |
|---|---|
| Deprecation notice | Responses include Deprecation: true and Sunset: 2027-04-15T00:00:00Z headers. Endpoint documentation is marked deprecated. |
| Grace period | Minimum 6 months. Both versions are live simultaneously. |
| Sunset | After the sunset date, the old version returns 410 Gone (RFC 9457 type: ".../problems/api-version-sunset"). |
Modules that are still calling a deprecated version appear in the
Admin → Usage → Deprecated API report. The Event Bus emits
platform.api.deprecated with targetVersion and sunsetDate.
8. Rate Limiting
The Gateway enforces a dual-layer strategy:
| Request Type | Default Quota | Target |
|---|---|---|
| Authenticated (JWT) | 1 000 RPS / tenant | tenantId |
| Unauthenticated | 20 RPS / IP | Source IP |
| Sensitive Action | 5 / min | IP + email |
Quota Overrides
To customize limits in the Gateway environment, override the default config:
# 1. Authenticated Requests (JWT present)
GATEWAY_RATE_LIMIT_PER_TENANT=1000
# 2. Unauthenticated Requests (No JWT)
GATEWAY_RATE_LIMIT_PER_IP=20
# 3. Sensitive Action Endpoints (/auth/login, /auth/register)
GATEWAY_RATE_LIMIT_AUTH_PER_MIN=5When the quota is exceeded, the Gateway returns 429 Too Many Requests
with an RFC 9457 error body and the standard IETF rate-limit headers:
HTTP/1.1 429 Too Many Requests
RateLimit-Policy: "default";q=1000;w=1
RateLimit: "default";r=0;t=12
Retry-After: 12
Content-Type: application/problem+json
{
"type": "https://api.septemcore.com/problems/rate-limit-exceeded",
"title": "Too many requests.",
"status": 429,
"detail": "Tenant quota of 1000 RPS exceeded.",
"instance": "/v1/wallets/01j9.../credit",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}X-RateLimit-* headers are prohibited — they are a non-standard
convention. The platform uses only the IETF-standardised
RateLimit-Policy and RateLimit headers.
9. Health Endpoints
Each service exposes three health endpoints outside the versioned namespace. These are system endpoints, not API endpoints:
| Endpoint | K8s probe | Purpose |
|---|---|---|
GET /health/live | livenessProbe | Process is up. No dependency checks. |
GET /health/ready | readinessProbe | All critical dependencies (PostgreSQL, Valkey, Kafka) are reachable. Returns 503 if any fail. |
GET /health | Monitoring | Full dependency status with latency. |
// GET https://api.septemcore.com/health
{
"status": "ok",
"version": "1.0.0",
"uptime": 86400,
"dependencies": [
{ "name": "postgres", "status": "ok", "latency_ms": 2 },
{ "name": "valkey", "status": "ok", "latency_ms": 1 },
{ "name": "kafka", "status": "ok", "latency_ms": 5 }
]
}/healthcheck, /ping, /status, and /api/v1/health are prohibited
path patterns for health endpoints.
10. OpenAPI Validation
Every route is validated at two levels before reaching any business logic:
- Envoy Gateway — validates requests against the OpenAPI 3.1.0
schema (path, query params, request body schema). Invalid requests
are rejected at the edge with
400 Bad Request. - Go Gateway Service — applies business-level cross-field validation and permission checks.
Response bodies are also validated by Envoy. Fields not declared in the response schema never appear in the API response — no accidental data leakage.
All nine OpenAPI specs live under services/{name}/api/openapi.yaml and
use RFC 9457 for error schemas. The traceId field is present on
every error response for correlation with distributed traces.
Quick Reference
| Convention | Value |
|---|---|
| Base URL | https://api.septemcore.com/v1/ |
| Auth header | Authorization: Bearer <token> |
| Field naming | snake_case |
| Dates | ISO 8601 UTC (2026-04-15T09:12:00Z) |
| IDs | UUID v7 string |
| List pagination | AIP-158 (page_size, page_token, next_page_token) |
| Idempotency | Idempotency-Key: <UUID v4> header |
| Update method | PATCH only (PUT prohibited) |
| Rate limit headers | RateLimit-Policy, RateLimit, Retry-After |
| Error format | RFC 9457 application/problem+json |
| API versioning | URL path (/v1/, /v2/) |
Module Federation 2.0
How the SeptemCore UI Shell acts as a Module Federation 2.0 host, dynamically loading tenant modules as independently deployed remotes with singleton shared dependencies and Zustand cross-MFE state.
Error Handling
RFC 9457 Problem Details deep dive: application/problem+json schema, the full platform error type catalogue, gRPC-to-HTTP status mapping, batch partial success (207), and how modules define custom error types.