Developers

API Reference

REST/HTTPS endpoints for cognitive offloading — governance evaluation, intent declaration, and shadow mode onboarding.

  • Base URL: https://api.gaas.is/v1
  • Protocol: HTTPS + JSON
  • Auth: X-API-Key header
POST /v1/intents?mode=shadow
Example
dec_fc6260d0b92249e9affa00cf2c1f0031
send → email-service · cx-agent-7
APPROVE MODIFIED
Request
action
communicate · send
summary
Send welcome email to new user
Decision
risk
0.09 · low
policies
18 evaluated · 15 passed · 0 failed
conditions
3 applied
latency
19 ms
audit
aud_74e92a023217…
pipeline_mode: shadowNothing enforced in shadow mode
On this page

Authentication

All API requests (except onboarding) require an API key passed in the X-API-Key header:

header
X-API-Key: gsk_...

You receive an API key when you create a shadow deployment via the onboarding endpoint. API keys are shown once at creation and cannot be retrieved later.

Shadow Mode

Shadow mode evaluates every action without enforcement. Actions are validated, enriched, checked against your policies, and audited — but never blocked. The deliberation stage is skipped in shadow mode, and shadow calls do not count toward your quota. Use it to observe what governance reveals about your agents before activating enforcement.

To use shadow mode, append ?mode=shadow to any intent submission. The response carries "pipeline_mode": "shadow" to confirm.

Verdicts

Every governance decision returns one of four verdicts in decision.verdict:

VerdictMeaningTypical Latency
approveAction is safe to execute as declared< 100ms
approve_modifiedAction approved with modifications or conditions (returned in modifications and conditions)< 100ms
escalateAction requires human review before execution< 100ms, or ~40–60s if deliberated
blockAction denied — policy violation or high risk< 100ms

Create Shadow Deployment

POST /v1/onboarding/quickstart

Register a new organization and receive API credentials for a 14-day free shadow deployment. No credit card required. No authentication needed for this endpoint. The key it returns is an admin key for the new organization.

Request Body

FieldTypeRequiredDescription
email string REQUIRED Contact email for the organization
org_name string REQUIRED Organization name
description string REQUIRED Description of what your AI agents do
Example Request
curl -X POST https://api.gaas.is/v1/onboarding/quickstart \
  -H "Content-Type: application/json" \
  -d '{
    "email": "engineer@acme.com",
    "org_name": "Acme Corp",
    "description": "Process customer refunds, generate compliance reports"
  }'
Example Response (201)
{
  "membrane_id": "mem_3f9a1c7e2b4d",
  "membrane_status": "shadow",
  "org_id": "org_acme_corp",
  "api_key": "gsk_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6",
  "key_id": "key_5e8d2c1a9b7f4e3d",
  "quickstart_snippet": "curl -X POST https://api.gaas.is/v1/intents?mode=shadow \\\n  -H \"X-API-Key: gsk_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"intent\": {...}}'",
  "next_steps": [
    "Submit intents with ?mode=shadow using your API key",
    "Review shadow stats: GET /v1/membranes/mem_3f9a1c7e2b4d/shadow/stats",
    "Activate when ready: POST /v1/membranes/mem_3f9a1c7e2b4d/activate",
    "View your active default policies: GET /v1/policy-authoring/policies",
    "Add a brand safety or fact-checking policy: POST /v1/policy-authoring/generate"
  ]
}

Response Codes

201 Created 413 Content Too Large 422 Validation Failed 429 Rate Limited

Declare Intent

POST /v1/intents

Submit an agent's intended action through the full five-stage governance pipeline. The pipeline evaluates the intent, enriches context, applies policies, triggers deliberation if needed, and returns an audited decision.

Query Parameters

ParamTypeDefaultDescription
mode string live live = full pipeline with enforcement (default). shadow = evaluate and audit without enforcement; skips the deliberation stage and does not count toward your quota. test = full pipeline, recorded with pipeline_mode: "test".

Request Body

The body wraps one intent declaration in an intent object. The table lists the required fields and the most-used optional ones; the full IntentSubmission schema is in the live OpenAPI spec at api.gaas.is/openapi.json. The Python and TypeScript SDKs build this object for you.

FieldTypeRequiredDescription
intent.agent.id string REQUIRED Your identifier for the agent (max 256 characters)
intent.agent.framework string OPTIONAL langchain, autogen, crewai, openai_assistants, anthropic_tools, semantic_kernel, haystack, or custom (default)
intent.action.type string REQUIRED communicate, transact, access, control, publish, recommend, or modify
intent.action.verb string REQUIRED What the agent does, e.g. send, transfer (max 128 characters)
intent.action.target.type string REQUIRED person, system, account, device, platform, record, dataset, endpoint, or resource
intent.action.target.identifier string REQUIRED What the action is directed at (max 512 characters)
intent.action.target.sensitivity string OPTIONAL public (default), internal, confidential, or regulated. Must be a level the agent is authorized for, otherwise the request returns 422
intent.action.payload.summary string REQUIRED Plain-language description of the action (10–1,000 characters)
intent.action.payload.content object REQUIRED The action's content (any key-value pairs)
intent.action.payload.estimated_impact.reversible boolean REQUIRED Whether the action can be undone
intent.action.payload.estimated_impact object REQUIRED Also accepts financial_exposure_usd (default 0), audience_size (default 1), regulatory_domains, data_categories
intent.context_provided object OPTIONAL Context you choose to share: session_id, conversation_id, user_state, environment, custom
intent.governance_request object OPTIONAL urgency (routine default, elevated, critical), max_latency_ms (default 500, range 50–30,000), fallback_on_timeout (escalate default, block, allow_with_flag), require_deliberation, require_human_review
intent.idempotency_key string OPTIONAL Resubmitting the same key returns the original decision (max 128 characters)
Example Request
curl -X POST "https://api.gaas.is/v1/intents?mode=shadow" \
  -H "X-API-Key: gsk_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6" \
  -H "Content-Type: application/json" \
  -d '{
    "intent": {
      "agent": {
        "id": "cx-agent-7"
      },
      "action": {
        "type": "communicate",
        "verb": "send",
        "target": {
          "type": "endpoint",
          "identifier": "email-service"
        },
        "payload": {
          "summary": "Send welcome email to new user",
          "content": {
            "template": "welcome_series"
          },
          "estimated_impact": {
            "reversible": true
          }
        }
      }
    }
  }'
Example Response (200 — approve_modified, abridged)
{
  "decision": {
    "id": "dec_fc6260d0b92249e9affa00cf2c1f0031",
    "intent_id": "int_7eea996deb65482cb75a4ca019ca4942",
    "timestamp": "2026-09-24T21:07:01.130742Z",
    "verdict": "approve_modified",
    "verdict_reason": "Conditionally approved — modifications required.",
    "modifications": [],
    "conditions": [
      "Conditional policy applied: pol_tcpa_consent_001",
      "Conditional policy applied: pol_tcpa_dnc_001",
      "Conditional policy applied: pol_tcpa_revocation_001"
    ],
    "risk_assessment": {
      "score": 0.09,
      "classification": "low"
    },
    "governance_metadata": {
      "pipeline_stages_executed": [
        "intent_validation",
        "context_enrichment",
        "policy_evaluation",
        "deliberation"
      ],
      "deliberation_triggered": false,
      "policies_evaluated": 18,
      "policies_passed": 15,
      "policies_warned": 0,
      "policies_failed": 0,
      "context_contradictions": 0,
      "pipeline_latency_ms": 19
    },
    "audit_ref": "aud_74e92a02321745f085e90656795c6e81",
    "pipeline_latency_ms": 19,
    "pipeline_mode": "shadow"
  }
}
Example Response (200 — block, abridged; a $250,000 irreversible transfer to a new external account)
{
  "decision": {
    "id": "dec_eca60dea69294eebb1cfffc557833eac",
    "verdict": "block",
    "verdict_reason": "Blocked by policy evaluation.",
    "risk_assessment": {
      "score": 0.652,
      "classification": "high"
    },
    "governance_metadata": {
      "policies_evaluated": 14,
      "policies_failed": 1,
      "context_contradictions": 1,
      "deliberation_triggered": false
    },
    "reasoning": {
      "blocking_policy_tier": 1,
      "blocking_policies": [
        "pol_t1_002"
      ],
      "blocking_policy_reasons": [
        "pol_t1_002: All 1 condition(s) met — policy triggers with verdict fail."
      ]
    },
    "audit_ref": "aud_fd91dc03a5e24cbd81223b9cecec50ef",
    "pipeline_mode": "shadow"
  }
}
Example Response (422 — semantic validation; target sensitivity above what the agent is authorized for)
{
  "error": {
    "code": "semantic_validation_error",
    "message": "1 semantic check(s) failed",
    "details": [
      {
        "check": "sv_005_sensitivity_capability",
        "message": "Agent not authorized for 'regulated' sensitivity (allowed: ['public']).",
        "target_sensitivity": "regulated",
        "agent_allowed": [
          "public"
        ]
      }
    ],
    "request_id": "f261b3005ad243dcbfa26e387332feab"
  }
}

Response Codes

200 Decision Returned 401 Unauthorized 402 Quota Exceeded 422 Validation Failed 429 Rate Limited

Verify a decision

Every live decision carries a governance_proof_token: a token signed by GaaS (ECDSA P-256, SHA-256) and anchored to the decision’s audit record. Shadow and test decisions carry none. Anyone can check a token, with no API key, and each organization’s audit records are timestamped daily in Bitcoin.

GET /v1/verify/proof/{token_id}

Public: no API key. Checks that GaaS signed the token, that its contents are unchanged, and that the audit record it is anchored to still matches its hash. Returns the signed token, so it can also be checked offline with the published key. Returns 404 when no token has this id.

Example Request
curl https://api.gaas.is/v1/verify/proof/{token_id}

Response Fields

FieldTypeDescription
validbooleanThe token checks out
signature_validbooleanGaaS’s signature matches the token’s contents
chain_integritybooleanThe audit record still matches the hash in the token
verdict, agent_id, org_id, issued_atstringWhat the token attests
key_idstringThe signing key, as listed at /.well-known/gaas-audit-keys.json
tokenobjectThe signed token, for checking offline
errorstringWhy a check failed, when one did
GET /.well-known/gaas-audit-keys.json

Public: GaaS’s signing keys as a JSON Web Key Set (EC, curve P-256, algorithm ES256), each with its kid and a PEM copy. Served from https://api.gaas.is. The list is empty when signing is off.

GET /v1/audit/timestamps

Your organization’s daily public timestamps, newest day first, with the recipe for the digest. Once a day, each organization’s audit records are combined into one digest and stamped into Bitcoin through the free, public OpenTimestamps service. Any key of your organization can read them (viewer and up). limit: 1–366, default 90.

Download one day’s proof and inspect it
curl -s -o 2026-09-27.hashes.ots https://api.gaas.is/v1/audit/timestamps/2026-09-27/ots \
  -H "X-API-Key: $VIEWER_API_KEY"
ots info 2026-09-27.hashes.ots

The day’s proof comes from GET /v1/audit/timestamps/{day}/ots, with the digest in the X-GaaS-Digest header; a day without a stamp, or another organization’s day, is a 404. Check it with the official ots client, without trusting GaaS: how to check a proof.

Connect your own systems

Give GaaS one HTTPS address of your own. When your agent proposes an action, GaaS asks that address for the facts its policies check, and decides with them. You answer from your own systems, in one small JSON shape, for any of six categories: environmental, entity_state, regulatory, organizational, identity and security. Set it up in the dashboard under Settings → Connected systems (an organization admin signed in with two-factor), or with the API below.

PUT /v1/context-endpoint

Create or replace your organization’s context endpoint. Needs an admin key; an operator or admin key can read it with GET, and DELETE removes it (decisions go on without it at once). The first save returns the signing_secret, once: store it, because your endpoint uses it to check that requests come from GaaS.

Request Body

FieldTypeRequiredDescription
urlstringREQUIREDHTTPS address GaaS sends a signed POST to (port 443)
categoriesarrayREQUIREDOne to six of the categories above
auth_typestringOPTIONALnone (default), bearer or header: how GaaS sends the secret your endpoint expects
auth_header_namestringOPTIONALThe header name, for auth_type: header
auth_secretstringOPTIONALWrite-only; never returned. Omit it to keep the saved secret
timeout_msintegerOPTIONAL1,500 by default, 200–3,000
enabledbooleanOPTIONALtrue needs a passing test on these settings; changing the address, categories, secret or time limit switches it off until you test again
Example: save, test, then switch on
curl -X PUT https://api.gaas.is/v1/context-endpoint \
  -H "X-API-Key: $ADMIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://context.example.com/gaas", "categories": ["environmental", "security"], "auth_type": "bearer", "auth_secret": "…"}'

curl -X POST https://api.gaas.is/v1/context-endpoint/test -H "X-API-Key: $ADMIN_API_KEY"

curl -X PUT https://api.gaas.is/v1/context-endpoint \
  -H "X-API-Key: $ADMIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://context.example.com/gaas", "categories": ["environmental", "security"], "auth_type": "bearer", "enabled": true}'

POST /v1/context-endpoint/test sends a signed test request and reports each category (at most 6 a minute). POST /v1/context-endpoint/rotate-signing-secret returns a new signing secret, once, used from the next request.

Each request is signed: X-GaaS-Signature is v1= and the hex HMAC-SHA256 of <timestamp>.<raw body> with your signing secret, and X-GaaS-Timestamp carries the timestamp; reject one more than five minutes from your clock. GaaS makes one attempt per decision within your time limit, with no retries. If it fails, the decision still happens, and the categories it should have answered count as missing context, which is risk. Try it in shadow mode first (?mode=shadow): GaaS calls your endpoint and records what it would decide, without enforcing anything. The request and response formats, and every fact the built-in policies read: the context endpoint guide.

SDK Integration

Use a GaaS SDK instead of calling the REST API directly:

Python

Install (Python 3.11+)
pip install gaas-sdk
header
from gaas_sdk import GaaSClient, build_intent, ActionType, TargetType

async with GaaSClient(
    "https://api.gaas.is",
    headers={"X-API-Key": "gsk_..."},
) as client:
    intent = build_intent(
        agent_id="my-agent",
        action_type=ActionType.COMMUNICATE,
        verb="send",
        target_type=TargetType.ENDPOINT,
        target_identifier="email-service",
        summary="Send welcome email to new user",
        content={"template": "welcome_series"},
    )
    response = await client.submit_intent(intent)
    print(response.data.verdict)  # approve | approve_modified | escalate | block

TypeScript

Install
npm install @governancehq/sdk
header
import { GaaSClient, buildIntent, ActionType, TargetType } from '@governancehq/sdk';

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

const intent = buildIntent({
  agentId: 'my-agent',
  actionType: ActionType.Communicate,
  verb: 'send',
  targetType: TargetType.Endpoint,
  targetIdentifier: 'email-service',
  summary: 'Send welcome email to new user',
  content: { template: 'welcome_series' },
});

const response = await client.submitIntent(intent);
console.log(response.data.verdict);

Java

Java 17 or later. The SDK, is.gaas:gaas-sdk 0.3.1, is published to GaaS’s own Maven repository, https://maven.gaas.is, not to Maven Central, so add the repository as well as the dependency. Guide: gaas.to/sdks.html#java.

Maven (pom.xml)
<repositories>
    <repository>
        <id>gaas</id>
        <url>https://maven.gaas.is</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>is.gaas</groupId>
        <artifactId>gaas-sdk</artifactId>
        <version>0.3.1</version>
    </dependency>
</dependencies>
Gradle (build.gradle)
repositories {
    mavenCentral()
    maven {
        url = 'https://maven.gaas.is'
        content { includeGroup 'is.gaas' }
    }
}

dependencies {
    implementation 'is.gaas:gaas-sdk:0.3.1'
}
Gradle Kotlin (build.gradle.kts)
repositories {
    mavenCentral()
    maven("https://maven.gaas.is") {
        content { includeGroup("is.gaas") }
    }
}

dependencies {
    implementation("is.gaas:gaas-sdk:0.3.1")
}

Framework plugins

If your agent runs on one of these frameworks, a plugin does the declaring for you. The plugins wrap the tools you give your agent: each call is checked before it runs, and a blocked action never runs. The MCP server lets any MCP-capable agent ask GaaS before it acts and look up the audit record. The Python packages need Python 3.11 or later.

FrameworkPackageInstall
LangChain and LangGraphgaas-langchainpip install gaas-langchain
CrewAIgaas-crewaipip install gaas-crewai
OpenAI Agents SDKgaas-openai-agentspip install gaas-openai-agents
Pydantic AIgaas-pydantic-aipip install gaas-pydantic-ai
Vercel AI SDK@governancehq/vercel-ainpm install @governancehq/vercel-ai
Microsoft Agent Frameworkgaas-agent-frameworkpip install "gaas-agent-framework[agent-framework]"
Any MCP-capable agent (MCP server)@governancehq/mcpnpm install @governancehq/mcp

More on how the plugins and the MCP server govern tool calls: technical specifications.

FAQ

Frequently Asked Questions

What does Shadow Mode return differently from live-enforcement mode?

Both modes return the same response structure: verdict, reasoning chain, risk score, and audit reference. In Shadow Mode the verdict shows what GaaS would have decided, but your application is expected to proceed regardless. Shadow Mode also skips the deliberation stage. Shadow vs. live is controlled per request with ?mode=shadow, or by the membrane’s lifecycle state; remove ?mode=shadow to enforce.

How are API requests rate limited?

Rate limits apply per API key and per endpoint category, not per trust tier. When a limit is exceeded the API returns 429 with Retry-After, RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers. Current limits are listed at gaas.to/rate-limits. Contact support if you need more capacity.

How should my agent handle a GaaS timeout or unavailability?

Set governance_request.fallback_on_timeout in your intent declaration: escalate (the default: hold for a human decision), block (fail-safe, recommended for production), or allow_with_flag (proceed but flag for review). If no fallback is set, the default is escalate.

Can I test against the API before integrating with a live agent?

Yes. Add ?mode=test to an intent submission: the full pipeline runs and the decision is recorded with pipeline_mode: "test", but it is not billed, creates no real escalations and carries no proof token. Test calls still count toward your quota; ?mode=shadow calls do not. Responses are structurally identical to production.

What payload size limits apply to intent declarations?

Fields have their own limits: summary 10–1,000 characters, target.identifier 512, verb 128, agent.id 256 and idempotency_key 128; a value over them returns 422. A request body over 1 MB returns 413 (payload_too_large); the SDKs do not check the size before sending, so that 413 is the signal. Pre-process large payloads (e.g., raw document content) to summaries or hashes before declaring intent.

Try it in shadow mode.

Create a shadow deployment, submit intents with ?mode=shadow, and see what governance reveals about your agents before you enforce anything.