Authentication

API key-based authentication with multi-tenancy and org-scoped isolation.

Overview

GaaS uses API keys for authentication. Every request to the governance API (except pre-auth endpoints) must include an X-API-Key header. API keys are bound to your organization and provide automatic multi-tenant isolation across all resources.

Production vs. Open Mode: In production, you must provision an API key via the quickstart endpoint or dashboard. In local development, if no API keys are configured, GaaS runs in open mode (no authentication required).

Obtaining an API Key

The fastest way to get an API key is through the quickstart endpoint:

POST https://api.gaas.is/v1/onboarding/quickstart
Content-Type: application/json

{
  "org_name": "Your Company",
  "email": "admin@yourcompany.com",
  "description": "Brief description of your use case"
}

All three fields are required strings. The endpoint provisions an organization, generates a governance membrane in shadow mode, and returns an API key in a single call.

The response includes your API key in the api_key field:

{
  "membrane_id": "mem_abc123...",
  "membrane_status": "shadow",
  "org_id": "org_abc123",
  "api_key": "gsk_live_org_a1b2c3d4e5f6...",
  "key_id": "key_abc123",
  "quickstart_snippet": "from gaas_sdk import GaaSClient ...",
  "next_steps": [
    "Submit your first intent with POST /v1/intents",
    "Review shadow decisions in the dashboard",
    "Activate live mode when ready"
  ]
}
Store your API key securely. Treat it like a password. Never commit it to version control or expose it in client-side code. Use environment variables or a secrets manager in production.

Using Your API Key

Include your API key in the X-API-Key header on every request:

Python (gaas_sdk)

from gaas_sdk import GaaSClient

async with GaaSClient(
    "https://api.gaas.is",
    headers={"X-API-Key": "gsk_live_org_a1b2c3d4e5f6..."},
) as client:
    response = await client.submit_intent(intent)

TypeScript (@gaas/sdk)

import { GaaSClient } from '@gaas/sdk';

const client = new GaaSClient({
  baseUrl: 'https://api.gaas.is',
  headers: { 'X-API-Key': 'gsk_live_org_a1b2c3d4e5f6...' },
});

const response = await client.submitIntent(intent);

Java (com.gaas.sdk)

import com.gaas.sdk.GaaSClient;

GaaSClient client = GaaSClient.builder()
    .baseUrl("https://api.gaas.is")
    .apiKey("gsk_live_org_a1b2c3d4e5f6...")
    .build();

GaaSResponse response = client.submitIntent(intent);

cURL

curl -X POST https://api.gaas.is/v1/intents \
  -H "X-API-Key: gsk_live_org_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{"intent": {...}}'

API Key Format

GaaS API keys follow this format:

gsk_<environment>_<scope>_<random>
Component Description Example
gsk_ Prefix (GaaS Key) gsk_
environment Deployment environment live, shadow, test
scope Access scope org (most common)
random Unique identifier (32+ chars) a1b2c3d4e5f6...

Example: gsk_live_org_a1b2c3d4e5f6g7h8i9j0...


Multi-Tenancy

Every API key is bound to an organization ID (org_id). All resources—decisions, escalations, policies, audit records, webhooks, and more—are automatically scoped to your organization.

This means:

How is org_id determined? Your org_id is automatically extracted from your API key and injected into the request context. You don't need to pass it manually—it's handled transparently by the middleware.

Pre-Auth Endpoints

Some endpoints do not require authentication (marked pre-auth in the API reference). These are typically public-facing endpoints for initial onboarding, email verification, or unsubscribe flows:

All other endpoints require a valid X-API-Key header.


Key Rotation & Revocation

Atomic Rotation (Recommended)

Use the atomic rotation endpoint to create a new key and revoke the old one in a single transaction — zero risk of key leakage or misconfigured windows:

POST https://api.gaas.is/v1/api-keys/{key_id}/rotate
X-API-Key: your_current_api_key

The response includes the new key immediately. The old key is revoked atomically — no overlap window to manage.

{
  "key_id": "key_new_abc123...",
  "api_key": "gsk_live_org_newkey...",
  "expires_at": "2026-05-19T00:00:00Z",
  "rotated_from": "key_old_xyz789..."
}

Manual Rotation

Alternatively, rotate manually for more control over the transition:

  1. Generate a new key via the dashboard or POST /v1/api-keys (admin only)
  2. Update your application's environment variables to use the new key
  3. Deploy the updated configuration
  4. Revoke the old key via DELETE /v1/api-keys/{key_id}
Zero-downtime rotation: When using manual rotation, provision the new key before revoking the old one. Both keys will work during the transition period.

Key Expiration & Lifetime

API keys support automatic expiration. When a key's expires_at timestamp is reached, it is rejected at the auth layer — no manual revocation needed.

Best practice: Set up monitoring to rotate keys at least 7 days before expiry. The dashboard shows key expiration dates and sends alerts as keys approach their lifetime limit.

Best Practices


Troubleshooting

401 Unauthorized

Cause: Missing or invalid API key.

Solution: Verify that the X-API-Key header is present and matches a valid, non-revoked key.

403 Forbidden

Cause: API key is valid, but you lack permissions for the requested operation (e.g., admin-only endpoint).

Solution: Check your user role in the dashboard. Contact your organization admin to request elevated permissions.

401 Unauthorized (Expired Key)

Cause: Your API key has passed its expires_at timestamp.

Solution: Rotate the key using POST /v1/api-keys/{key_id}/rotate or provision a new key. Check the X-GaaS-Key-Expired: true response header to confirm expiration is the cause.

Open Mode Not Working

Cause: API keys or GAAS_API_KEYS env var are configured, disabling open mode.

Solution: Remove all API keys and unset GAAS_API_KEYS to enable open mode (local development only).


Related Pages