Skip to content

Config Platform - API Contract Specification

Overview

This document provides concrete API contracts for the Config Platform REST and gRPC APIs. Use this specification to generate client code, integration adapters, and API gateway configurations.

Base URL: /api/v1
API Version: v1 (semantic versioning in x-api-version header)
Authentication: OAuth2/OIDC JWT Bearer tokens with scopes: ecs.read, ecs.write, ecs.admin

REST API Endpoints

Config Sets

Create Config Set

POST /api/v1/config-sets
Authorization: Bearer {token}
Idempotency-Key: {uuid}
Content-Type: application/json

{
  "name": "string",
  "appId": "string",
  "environment": "dev|staging|prod",
  "labels": ["string"],
  "description": "string"
}

Response: 201 Created

{
  "id": "uuid",
  "name": "string",
  "appId": "string",
  "environment": "string",
  "labels": ["string"],
  "status": "draft|published",
  "createdAt": "2024-01-01T00:00:00Z",
  "updatedAt": "2024-01-01T00:00:00Z",
  "etag": "string"
}

List Config Sets

GET /api/v1/config-sets?cursor={cursor}&limit=50&filter=labels:eq:production&sort=createdAt:desc
Authorization: Bearer {token}

Response: 200 OK

{
  "items": [
    {
      "id": "uuid",
      "name": "string",
      "appId": "string",
      "status": "string",
      "etag": "string"
    }
  ],
  "nextCursor": "string",
  "hasMore": true
}

Get Config Set

GET /api/v1/config-sets/{setId}
Authorization: Bearer {token}
If-None-Match: {etag}

Response: 200 OK or 304 Not Modified

{
  "id": "uuid",
  "name": "string",
  "appId": "string",
  "environment": "string",
  "labels": ["string"],
  "status": "string",
  "createdAt": "2024-01-01T00:00:00Z",
  "updatedAt": "2024-01-01T00:00:00Z",
  "etag": "string"
}

Config Items

Upsert Config Item

PUT /api/v1/config-sets/{setId}/items/{key}
Authorization: Bearer {token}
If-Match: {etag}
Content-Type: application/json

{
  "value": "any",
  "contentType": "application/json|text/plain|application/yaml",
  "isSecret": false,
  "labels": ["string"],
  "description": "string"
}

Response: 200 OK or 201 Created

{
  "key": "string",
  "value": "any",
  "contentType": "string",
  "isSecret": false,
  "labels": ["string"],
  "version": 1,
  "etag": "string",
  "updatedAt": "2024-01-01T00:00:00Z"
}

Get Config Item

GET /api/v1/config-sets/{setId}/items/{key}
Authorization: Bearer {token}
If-None-Match: {etag}

Response: 200 OK or 304 Not Modified

{
  "key": "string",
  "value": "any",
  "contentType": "string",
  "isSecret": false,
  "version": 1,
  "etag": "string"
}

Resolve Configuration

GET /api/v1/configs/{path}:resolve?version=latest&env=prod&service={serviceId}&instance={instanceId}
Authorization: Bearer {token}
If-None-Match: {etag}

Response: 200 OK or 304 Not Modified

{
  "path": "string",
  "value": "any",
  "version": "string",
  "etag": "string",
  "resolvedAt": "2024-01-01T00:00:00Z",
  "sources": [
    {
      "scope": "global|edition|tenant|service|instance",
      "version": "string",
      "key": "string"
    }
  ]
}

Snapshots and Versions

Create Snapshot

POST /api/v1/config-sets/{setId}/snapshots
Authorization: Bearer {token}
Content-Type: application/json

{
  "note": "string",
  "labels": ["string"]
}

Response: 201 Created

{
  "id": "uuid",
  "configSetId": "uuid",
  "version": "string",
  "hash": "string",
  "createdBy": "string",
  "createdAt": "2024-01-01T00:00:00Z",
  "note": "string"
}

Get Snapshot Content

GET /api/v1/config-sets/{setId}/snapshots/{snapId}/content?format=json|yaml
Authorization: Bearer {token}
Accept: application/json|application/yaml

Response: 200 OK

{
  "version": "string",
  "items": {
    "key1": "value1",
    "key2": "value2"
  },
  "metadata": {
    "createdAt": "2024-01-01T00:00:00Z",
    "createdBy": "string"
  }
}

Compute Diff

POST /api/v1/config-sets/{setId}/diff
Authorization: Bearer {token}
Content-Type: application/json

{
  "fromVersion": "string",
  "toVersion": "string"
}

Response: 200 OK

{
  "added": ["key1", "key2"],
  "removed": ["key3"],
  "modified": [
    {
      "key": "key4",
      "oldValue": "old",
      "newValue": "new"
    }
  ]
}

Deployments

Deploy Snapshot

POST /api/v1/deployments
Authorization: Bearer {token}
Idempotency-Key: {uuid}
Content-Type: application/json

{
  "snapshotId": "uuid",
  "environment": "dev|staging|prod",
  "targetLabels": ["string"]
}

Response: 202 Accepted

{
  "id": "uuid",
  "snapshotId": "uuid",
  "environment": "string",
  "status": "pending|in-progress|completed|failed",
  "startedAt": "2024-01-01T00:00:00Z"
}

Policies

Validate Configuration

POST /api/v1/policies/validate
Authorization: Bearer {token}
Content-Type: application/json

{
  "configSetId": "uuid",
  "draft": {
    "key": "value"
  },
  "context": {
    "tenantId": "uuid",
    "editionId": "string",
    "environment": "string"
  }
}

Response: 200 OK

{
  "valid": true,
  "errors": [
    {
      "key": "string",
      "rule": "string",
      "message": "string",
      "severity": "error|warning"
    }
  ],
  "warnings": []
}

gRPC Services

ResolveService

Service Definition:

service ResolveService {
  rpc Resolve(ConfigResolveRequest) returns (ConfigResolveResponse);
  rpc ResolveStream(ConfigResolveRequest) returns (stream ConfigResolveResponse);
}

message ConfigResolveRequest {
  string path = 1;
  string version = 2;
  string environment = 3;
  string service_id = 4;
  string instance_id = 5;
  string etag = 6;
}

message ConfigResolveResponse {
  string path = 1;
  bytes value = 2;
  string version = 3;
  string etag = 4;
  repeated ConfigSource sources = 5;
  google.protobuf.Timestamp resolved_at = 6;
}

message ConfigSource {
  string scope = 1;
  string version = 2;
  string key = 3;
}

RefreshChannel

Service Definition:

service RefreshChannel {
  rpc Subscribe(SubscriptionRequest) returns (stream RefreshEvent);
}

message SubscriptionRequest {
  string tenant_id = 1;
  string config_set_id = 2;
  repeated string paths = 3;
}

message RefreshEvent {
  string event_id = 1;
  string config_set_id = 2;
  repeated string paths = 3;
  string version = 4;
  string etag = 5;
  google.protobuf.Timestamp timestamp = 6;
}

Error Model

All non-2xx responses follow RFC 7807 Problem Details format:

{
  "type": "https://api.connectsoft.cloud/problems/config-not-found",
  "title": "Config Not Found",
  "status": 404,
  "detail": "Config set with id 'uuid' was not found",
  "instance": "/api/v1/config-sets/uuid",
  "traceId": "string",
  "tenantId": "uuid",
  "errors": [
    {
      "field": "string",
      "message": "string"
    }
  ]
}

Common Error Codes:

  • 400 - Bad Request (validation errors)
  • 401 - Unauthorized (invalid/expired token)
  • 403 - Forbidden (insufficient scope/tenant)
  • 404 - Not Found
  • 409 - Conflict (concurrency conflict, use If-Match)
  • 429 - Too Many Requests (rate limited, includes Retry-After header)
  • 500 - Internal Server Error

Authentication and Authorization

JWT Token Claims

Required claims: - aud: Audience (ecs.api, ecs.admin) - sub: Subject (user/service ID) - tenant_id: Tenant identifier - scope: Space-separated scopes (ecs.read, ecs.write, ecs.admin) - exp: Expiration timestamp

Scope Permissions

Scope Permissions
ecs.read Read config sets, items, snapshots, resolve
ecs.write Create/update/delete config sets and items
ecs.admin All permissions + cross-tenant operations

Rate Limiting

Rate limits are tenant-scoped and edition-aware:

  • Free Edition: 100 requests/minute
  • Pro Edition: 1000 requests/minute
  • Enterprise Edition: 10000 requests/minute

Rate limit headers: - X-RateLimit-Limit: Request limit per window - X-RateLimit-Remaining: Remaining requests - X-RateLimit-Reset: Reset timestamp - Retry-After: Seconds to wait (on 429)

Idempotency

POST endpoints that create resources support idempotency via Idempotency-Key header:

POST /api/v1/config-sets
Idempotency-Key: {uuid}

Duplicate requests with the same key return the original response without side effects.

ETags and Conditional Requests

Resources include ETag header for optimistic concurrency:

  • If-None-Match: {etag} - Return 304 if unchanged
  • If-Match: {etag} - Require current etag for updates

Pagination

List endpoints use cursor-based pagination:

GET /api/v1/config-sets?cursor={cursor}&limit=50

Response includes nextCursor and hasMore fields.

Filtering and Sorting

Filtering uses query parameters:

  • filter=labels:eq:production - Equality filter
  • filter=createdAt:gte:2024-01-01 - Range filter
  • sort=createdAt:desc - Sort order

References