Skip to content

API Specification: Common Patterns

Shared conventions, response formats, and error handling for all Graph OLAP Platform APIs.


DocumentContent
api.mappings.spec.mdMapping CRUD, versions, lifecycle
api.snapshots.spec.mdSnapshot read-only APIs (CRUD disabled), progress, retry
api.instances.spec.mdInstance CRUD, lock status
api.admin-ops.spec.mdConfig, cluster, audit, favorites
api.starburst.spec.mdStarburst schema browser, SQL validation
api.wrapper.spec.mdGraph queries, algorithms
api.internal.spec.mdInternal component communication

The Control Plane exposes two categories of APIs based on consumer type and authentication model:

The product API for user-initiated operations. Used by external SDK clients.

AspectDescription
AuthenticationUser credentials (API key → X-Username, X-User-Role headers)
AuthorizationRole-based (analyst, admin, ops) + ownership checks
Network AccessIngress (SDK)
ConsumersPython SDK
Rate LimitingPer-user limits enforced
Audit LoggingFull audit trail with user identity

Endpoints: Mappings, Snapshots, Instances, Favorites, Admin/Ops, Starburst introspection

Component-to-component communication. Not for external consumption.

AspectDescription
AuthenticationKubernetes service account tokens
AuthorizationComponent identity (`X-Component: wrapper
Network AccessClusterIP only (cluster-internal)
ConsumersRyugraph Wrapper, FalkorDB Wrapper, Export Worker
Rate LimitingNone (trusted components)
Audit LoggingSystem-level logging only

Endpoints: Status updates, metrics reporting, mapping retrieval, activity recording, Starburst introspection

  1. Consumer Type: API is for user-facing clients; Internal API is for platform components
  2. Auth Model: API uses user credentials; Internal API uses service accounts
  3. Contract Stability: API requires backward compatibility; Internal API can evolve freely
  4. Trust Model: API validates user permissions; Internal API trusts component identity
┌─────────────────────────────────────────────────────────────┐
│ External │
│ │
│ Python SDK ───► Ingress ───► Control Plane (/api/*) │
│ │
├─────────────────────────────────────────────────────────────┤
│ Cluster-Internal │
│ │
│ Wrapper ───► ClusterIP ───► Control Plane (/api/internal/*) │
│ Worker ───► ClusterIP ───► Control Plane (/api/internal/*) │
│ │
└─────────────────────────────────────────────────────────────┘
# NetworkPolicy ensures internal APIs are cluster-only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: control-plane-internal
spec:
podSelector:
matchLabels:
app: control-plane
ingress:
# Internal APIs from platform components
- from:
- podSelector:
matchLabels:
component: wrapper
- podSelector:
matchLabels:
component: worker
ports:
- port: 8000
# Path: /api/internal/* (enforced at application level)

This matrix shows which platform components consume each API endpoint group.

ConsumerAuthenticationAccess Pattern
WebAppUser API Key (forwarded)All user-facing + Ops endpoints
SDKUser API KeyUser-facing endpoints only
WrapperK8s Service AccountInternal status reporting
ExporterK8s Service AccountInternal job management
Endpoint GroupEndpointsWebAppSDKWrapperExporter
Mappings12--
Snapshots8(disabled)(disabled)--
Instances11--
Favorites3--
Starburst Schema6--
Admin/Ops Config10---
Cluster Ops3---
Export Queue4---
Subtotal57493200

Note: Public snapshot CRUD endpoints are disabled. Instances are created directly from mappings via POST /instances/from-mapping. The snapshot layer operates internally.

Endpoint GroupEndpointsWebAppSDKWrapperExporter
Snapshot Status4---
Instance Status4---
Instance Config2---
Subtotal100064
Endpoint GroupEndpointsWebAppSDKInternal
Health/Status3
Graph Operations3--
Lock Status1-
Algorithms5--
Subtotal122123
CategoryTotalWebAppSDKWrapperExporter
User-Facing49493200
Internal100064
Wrapper Pod1221200
Total71514464

Note: User-facing count excludes 8 disabled snapshot endpoints.

┌─────────────────────────────────────────────────────────────────┐
│ External │
│ │
│ SDK ──────► Ingress ──────► Control Plane (/api/*) │
│ │ │
│ └──────► Ingress ──────► Wrapper Pods (queries, algorithms) │
│ │
├─────────────────────────────────────────────────────────────────┤
│ Cluster-Internal │
│ │
│ WebApp ───► ClusterIP ───► Control Plane (/api/*) │
│ Wrapper ──► ClusterIP ───► Control Plane (/api/internal/*) │
│ Exporter ─► ClusterIP ───► Control Plane (/api/internal/*) │
│ │
└─────────────────────────────────────────────────────────────────┘

All requests and responses use JSON:

Content-Type: application/json
Accept: application/json

References:

  • ADR-084: Dual Authentication Paths
  • ADR-085: Auth Middleware JWT Extraction

The platform supports two authentication paths for different use cases:

PathUse CaseFlowToken Source
API (Bearer)SDK, scripts, CI/CDDirect JWT validationPre-issued JWT
User (OAuth2)Browser accessOAuth2-proxy redirectAuth0 login
┌─────────────────┐
│ Ingress │
└────────┬────────┘
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ /api/* │ │ /jupyter/* │ │ /argocd/* │
│ │ │ │ │ │
│ Bearer Token │ │ OAuth2 Flow │ │ OAuth2 Flow │
│ (skip proxy) │ │ (via proxy) │ │ (OIDC) │
└───────────────┘ └───────────────┘ └───────────────┘

API Path: Bearer tokens skip oauth2-proxy (skipJwtBearerTokens: true). The Control Plane extracts JWT claims without re-validation because the edge proxy has already validated signature, expiry, and audience.

User Path: Browser requests flow through oauth2-proxy, which handles the OAuth2 redirect flow with Auth0.

Authorization: Bearer {api_key}

Middleware-injected headers (not client-supplied):

Section titled “Middleware-injected headers (not client-supplied):”
X-Username: {username} # Username extracted from auth token (e.g., 'alice.smith')
X-User-Role: {analyst|admin|ops} # User's role from user database

These headers are set by the authentication middleware after validating the token. Backend handlers can trust these values. Clients should NOT send X-Username or X-User-Role headers; they will be overwritten by middleware.

Roles are hierarchical: Analyst < Admin < Ops. Each higher role inherits all permissions of lower roles. See authorization.spec.md for the complete matrix.

TypeFormatExample
Resource IDInteger1, 42, 1337
UsernameStringalice.smith, bob.jones
TimestampISO 86012025-01-15T10:30:00Z
DurationISO 8601PT24H, P7D, PT30M
BooleanJSON booleantrue, false

ID Strategy:

  • User IDs: Username from authentication token (e.g., alice.smith)
  • Resource IDs: Database-generated auto-incrementing integers

All API responses include:

HeaderDescription
Content-Typeapplication/json
X-Request-IDUnique request identifier for tracing

X-Request-ID Behavior:

  • Server generates a UUID for each incoming request
  • If client provides X-Request-ID header, server uses that value instead
  • Same ID appears in response header and meta.request_id body field
  • All log entries for the request include this ID for correlation
Response Header:
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
Response Body:
{
"data": {...},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}

{
"data": {
"id": 42,
"name": "Resource Name",
...
},
"meta": {
"request_id": "req-uuid"
}
}
{
"data": [
{"id": "uuid-1", ...},
{"id": "uuid-2", ...}
],
"meta": {
"request_id": "req-uuid",
"total": 150,
"offset": 0,
"limit": 50
}
}
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable description",
"details": {
"field": "specific_field",
"reason": "additional context"
}
},
"meta": {
"request_id": "req-uuid"
}
}

All list endpoints support pagination:

ParameterTypeDefaultMaxDescription
offsetinteger0-Records to skip
limitinteger50100Records to return

Response includes:

  • meta.total: Total matching records
  • meta.offset: Current offset
  • meta.limit: Current limit
GET /api/mappings?offset=50&limit=25
Response:
{
"data": [...25 items...],
"meta": {
"total": 150,
"offset": 50,
"limit": 25
}
}

ParameterTypeDescription
ownerstringFilter by owner_username
searchstringText search on name, description
created_aftertimestampFilter by created_at >= value
created_beforetimestampFilter by created_at <= value
statusstringFilter by resource status
ParameterTypeDefaultDescription
sort_bystringcreated_atField to sort by
sort_orderstringdescasc or desc

CodeUsage
200 OKSuccessful GET, PUT, DELETE
201 CreatedSuccessful POST (resource created)
202 AcceptedAsync operation started (algorithms)
400 Bad RequestInvalid request body, validation failure
401 UnauthorizedMissing or invalid authentication
403 ForbiddenPermission denied
404 Not FoundResource not found
408 Request TimeoutQuery timeout exceeded
409 ConflictState conflict (locked, limit exceeded, dependencies)
500 Internal Server ErrorServer error
503 Service UnavailableService temporarily unavailable

CodeDescription
VALIDATION_FAILEDRequest body validation failed

Authentication/Authorization Errors (401/403)

Section titled “Authentication/Authorization Errors (401/403)”
CodeDescription
UNAUTHORIZEDMissing or invalid authentication
PERMISSION_DENIEDUser not authorized for operation
CodeDescription
RESOURCE_NOT_FOUNDGeneric resource not found
MAPPING_VERSION_NOT_FOUNDSpecific version not found
ALGORITHM_NOT_FOUNDUnknown algorithm name
EXECUTION_NOT_FOUNDUnknown execution ID
CodeDescription
RESOURCE_LOCKEDInstance locked by algorithm
CONCURRENCY_LIMIT_EXCEEDEDInstance limit reached
RESOURCE_HAS_DEPENDENCIESCannot delete (dependencies exist)
SNAPSHOT_NOT_READYCannot use non-ready snapshot
INVALID_STATEOperation invalid for current state
ALREADY_EXISTSResource already exists
CodeDescription
QUERY_TIMEOUTCypher query exceeded timeout
CodeDescription
STARBURST_ERRORStarburst connection/query error
RYUGRAPH_ERRORRyugraph operation error
FALKORDB_ERRORFalkorDB operation error
GCS_ERRORGCS read/write error
SERVICE_UNAVAILABLEMaintenance mode enabled, or dependency unreachable

503 SERVICE_UNAVAILABLE details:

Returns 503 when:

  • Maintenance mode is enabled (for resource creation requests)
  • Starburst is unreachable
  • Database is unavailable

Note: During maintenance mode, read operations (GET) continue to work. Only creation requests (POST for new resources) return 503.

Note: This is the authoritative list of error codes for the Graph OLAP Platform.


Rate limiting is not currently implemented. Future consideration:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1704067200

API versioning is not currently implemented. Future consideration:

Accept: application/vnd.graph-olap.v1+json

Or path-based:

/api/v1/mappings
/api/v2/mappings

See architectural.guardrails.md for the authoritative list.

Key sections relevant to API design:

  • API Design - JSON only, pagination, immutable fields, validation rules
  • Authentication & Authorization - Owner-based permissions, no credential exposure
  • Resource Lifecycle - Lifecycle limits, concurrency limits, cleanup order
  • Data Handling & GCS - GCS deletion order, favorites cleanup

See decision.log.md for consolidated open questions and architecture decision records.