REST API Overview
The SeptemCore Platform exposes all services as REST HTTP/1.1 APIs through a single API Gateway (Envoy Gateway + Go Gateway Service). All requests flow through the Gateway — there are no direct service endpoints exposed to clients.
Base URL
https://api.septemcore.com/v1
All endpoints are versioned under /v1. A version bump to /v2 will
be communicated at least 6 months in advance per the
Versioning & Deprecation Policy.
Authentication
Every request (except public auth endpoints) requires a Bearer JWT
in the Authorization header:
Authorization: Bearer <access_token>
Tokens are obtained via:
POST /api/v1/auth/login— email + passwordPOST /api/v1/auth/login/:providerId— OAuth / wallet providerPOST /api/v1/auth/refresh— rotate access token using refresh token
Token Lifetimes
| Token | Lifetime | Notes |
|---|---|---|
| Access token (JWT) | 15 minutes | Short-lived, stateless |
| Refresh token | 7 days | Rotation on each use |
| Refresh grace window | 10 seconds | Two tabs refreshing simultaneously: both get the same new pair within the window |
| Session token (tenant selection) | 5 minutes | Issued during multi-tenant login, before select-tenant |
JWT Payload Fields
| Claim | Type | Description |
|---|---|---|
sub | UUID | User ID |
email | string | User email |
tenantId | UUID | Active tenant ID |
roles | string[] | List of role names |
iat | number | Issued-at (Unix timestamp) |
exp | number | Expires-at (Unix timestamp) |
iss | string | OIDC issuer URL |
aud | string | Audience (platform-kernel or OIDC client ID) |
Request Headers
| Header | Required | Description |
|---|---|---|
Authorization: Bearer <token> | ✅ All protected routes | JWT access token |
Content-Type: application/json | ✅ POST / PATCH body | Body format |
Idempotency-Key: <uuid> | Required for financial operations | UUID v4/v7 — prevents double-execution on retry |
Accept: application/json | Optional (default) | Response format |
Error Format — RFC 9457
All errors return Content-Type: application/problem+json following
RFC 9457 Problem Details:
{
"type": "https://api.septemcore.com/problems/insufficient-funds",
"title": "Insufficient Funds",
"status": 400,
"detail": "Available balance (12500 cents) is less than requested amount (50000 cents).",
"instance": "/api/v1/wallets/01j9pwal0000000000000001/debit",
"traceId": "4bf92f3477d54adb"
}
For validation errors, the response includes an errors array:
{
"type": "https://api.septemcore.com/problems/validation-error",
"title": "Validation Error",
"status": 400,
"detail": "The request body contains invalid fields.",
"instance": "/api/v1/users",
"traceId": "1a2b3c4d5e6f",
"errors": [
{ "field": "email", "message": "Must be a valid email address" },
{ "field": "roles", "message": "At least one role is required" }
]
}
Standard HTTP Status Codes
| Status | Meaning | Common cause |
|---|---|---|
200 OK | Success | GET, PATCH |
201 Created | Resource created | POST |
202 Accepted | Async operation queued | Batch notify, large export |
204 No Content | Success, no body | DELETE |
207 Multi-Status | Partial batch success | Bulk operations (RFC 9457 per item) |
400 Bad Request | Validation or business rule error | Invalid amount, missing field |
401 Unauthorized | Missing or expired token | Token not sent or expired |
403 Forbidden | Insufficient permissions | Role does not have required permission |
404 Not Found | Resource not found or module not installed for tenant | Unknown ID |
409 Conflict | Optimistic lock / duplicate | version mismatch, duplicate idempotencyKey |
413 Payload Too Large | Body or file exceeds limit | File > 10 MB for images |
429 Too Many Requests | Rate limit exceeded | See rate limiting below |
503 Service Unavailable | Circuit breaker open or staging limit | Try again after Retry-After |
504 Gateway Timeout | Upstream service did not respond in 5 s | Transient; retry with backoff |
Rate Limiting
The Gateway applies a hybrid two-layer token-bucket algorithm:
| Layer | Mechanism | Purpose |
|---|---|---|
| Envoy (per-instance) | Local token bucket — zero latency | Absorb burst before hitting Valkey |
| Valkey (global) | Distributed counter per-tenant | Fair enforcement across all Gateway instances |
When the rate limit is exceeded:
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/problem+json
{
"type": "https://api.septemcore.com/problems/rate-limit-exceeded",
"title": "Rate Limit Exceeded",
"status": 429,
"detail": "Request rate exceeds limit of 1000 req/min for tenant 01j9pten...",
"instance": "/api/v1/contacts",
"retryAfter": 42
}
| Parameter | Default | Override |
|---|---|---|
| Time window | 1 minute | Per Billing plan |
| Default limit | 1,000 requests / min per tenant | Admin role: 5,000 / min |
| Group key | IP / user / tenant / API key | Configurable per route |
Cursor-Based Pagination
All list endpoints use cursor-based pagination (not offset/page). Cursors are opaque base64-encoded strings — do not parse them.
Request
GET https://api.septemcore.com/v1/users?limit=20&cursor=eyJpZCI6IjAx...
| Query parameter | Default | Max | Description |
|---|---|---|---|
limit | 20 | 100 | Records per page |
cursor | — | — | Opaque cursor from previous response meta.cursor |
Response
{
"data": [
],
"meta": {
"cursor": "eyJpZCI6IjAx...",
"hasMore": true,
"total": 847
}
}
When hasMore: false, cursor is null — no more pages exist.
OpenAPI Specifications
Nine OpenAPI 3.x specifications are available — one per service. The documentation URLs dynamically follow the service's API namespace:
- Swagger UI:
/api-docs/<namespace> - Redoc:
/api-docs/<namespace>/redoc - OpenAPI JSON:
/api/v1/<namespace>/openapi.json
| Service | Namespace |
|---|---|
| IAM | iam |
| Data Layer | data |
| Money | money |
| Files | files |
| Notify | notify |
| Audit | audit |
| Events | events |
| Billing | billing |
| Gateway | gateway |
Deprecated API versions: When a version is deprecated, the Gateway saves an immutable OpenAPI snapshot to S3 (
api-specs/v{N}/openapi.yaml). Snapshots are served atGET /api-docs/v{N}and are retained indefinitely — clients still migrating can always access the old spec.
Gateway Behaviour
| Feature | Detail |
|---|---|
| Circuit breaker | 5 consecutive failures in 30 s → circuit opens for 30 s → 503 Service Unavailable |
| gRPC deadline | 5 s per upstream call (GRPC_CALL_TIMEOUT_MS). Exceeded → 504 Gateway Timeout |
| Retry | 1 retry + 100 ms backoff for idempotent GET requests. POST/PATCH/DELETE — no retry |
| OpenAPI validation | Request body validated against schema on every call. Invalid → 400 Bad Request |
| Response validation | Response body validated in dev/staging environments. Violations → 500 Internal Server Error + alert |
| Module routing | Module not installed for tenant → 404 Not Found (before downstream call, using Valkey module cache) |
| Tenant injection | tenantId from JWT is injected into every downstream request — modules cannot override it |