📘 Public beta · Endpoints are stable; OpenAPI specs and SDKs ship monthly. See changelog →
Products
Orchestration
Service connections

Service connections

A service connection is your org's registered handle for a sibling Quantum Elixir product (or any external HTTP service). It stores the base URL + an encrypted API key, and lets workflow nodes call sibling endpoints by slug rather than URL.

Why connect explicitly?

Three reasons:

  1. Encryption at rest. Storing the sibling API key in the workflow spec would expose it on every read of the workflow. Service connections store the key encrypted under AES-256-GCM with a per-org KEK; the workflow spec just references service: "aml".
  2. Health checks. The connection record tracks last-healthy timestamp; ops can see at a glance which sibling is unreachable.
  3. Catalog discovery. Once a service is connected, you get programmatic access to its endpoint catalog via GET /api/catalog/services.

List connections

GET/api/service-connections
Auth · API keyScope · service-connections:read
{
  "data": {
    "connections": [
      {
        "id": "scn_01HXY...",
        "serviceSlug": "identity",
        "baseUrl": "https://identity.quantumelixir.tech",
        "active": true,
        "lastHealthyAt": "2026-05-24T08:14:22Z",
        "createdAt": "...",
        "lastUsedAt": "..."
      }
    ]
  }
}

Register or upsert

POST/api/service-connections
Auth · API keyScope · service-connections:write
{
  "serviceSlug": "aml",
  "baseUrl": "https://aml.quantumelixir.tech",
  "apiKey": "aml_a8f3c92e..."
}

If a connection for that serviceSlug already exists, this upserts — updates baseUrl + apiKey atomically. Useful for rotation: rotate the sibling key in the sibling's dashboard, then upsert here.

Response (201):

{
  "data": {
    "connection": {
      "id": "scn_01HXY...",
      "serviceSlug": "aml",
      "baseUrl": "...",
      "active": true,
      "apiKeyHash": "sha256:...",
      "createdAt": "..."
    }
  }
}

The API key is never returned again. To rotate, call upsert with the new value.

Allowed serviceSlug values

  • identity
  • aml
  • anti-fraud
  • document-intelligence
  • bank-statement
  • ai-automation

For external (non-Quantum-Elixir) services, use custom:<your-slug>:

{
  "serviceSlug": "custom:core-banking",
  "baseUrl": "https://core-banking.bank-xyz.internal",
  "apiKey": "your-internal-api-key"
}

External services use the same apiKey shape as siblings — the key is sent as Authorization: Bearer <apiKey> on each outbound call. If your internal service expects a different scheme (Basic, custom header, mTLS), put a thin adapter in front of it that translates Bearer into your scheme — or open an issue and we'll prioritize a richer authScheme field. Workflow nodes referencing custom services work the same: service: "custom:core-banking".

Catalog discovery

GET/api/catalog/services
Auth · API keyScope · service-connections:read

Returns every connected sibling's endpoint catalog:

{
  "data": {
    "catalog": [
      {
        "slug": "identity",
        "name": "Identity Platform",
        "baseUrl": "https://identity.quantumelixir.tech",
        "endpoints": [
          {
            "method": "POST",
            "path": "/api/identity/document/ktp/capture",
            "name": "KTP capture",
            "inputSchema":  { /* JSON Schema for body */ },
            "outputSchema": { /* JSON Schema for response */ },
            "scope": "verifications:view"
          },
          {
            "method": "POST",
            "path": "/api/identity/face/liveness",
            ...
          }
        ]
      }
    ]
  }
}

The dashboard's visual workflow builder reads this to surface auto-complete + schema validation when you wire up service_call nodes. Hand-written workflows can reference it for the same reason — your code can validate against the JSON Schemas to catch typos before publishing.

Health-check semantics

lastHealthyAt is updated every time:

  • A workflow service_call to this connection returns 2xx.
  • An explicit health-check request fires (background sweep every 60s).

If lastHealthyAt falls more than 5 minutes behind now, the connection is marked unhealthy: true in the response (dashboard shows a red dot). Workflows still attempt calls — health is informational, not gating.

To force a check:

POST /api/service-connections/{id}/test

Returns { ok: true, latencyMs } or { ok: false, error }.

Rotating keys

Sibling product API keys should be rotated quarterly. Workflow:

  1. In the sibling product's dashboard, issue a new key with the same scopes.
  2. POST /api/service-connections with the new key (upsert, same slug).
  3. Wait for in-flight workflow steps to complete (use the workflow dashboard).
  4. Revoke the old key in the sibling's dashboard.

Because connections store the key encrypted-at-rest with a per-org KEK and never expose it again, you can't accidentally leak the key by reading the connection record back.

Never put a sibling API key in the workflow spec

Always use service: "<slug>" and let the connection provide the key. Putting apiKey: "..." directly in a service_call node is supported but strongly discouraged — the key ends up in plaintext in spec exports and audit-log payloads.

Audit

Connection mutations are immutable-logged:

  • create
  • upsert (with hash of old/new key for forensic compare without exposing values)
  • delete

Export from the dashboard's audit log when due-diligence requires it.