Skip to main content

Developer Guide

Add AI governance to any application with ARQERA's API. This guide covers authentication, the core API, SDKs, and common patterns.

Quick Start

1. Get an API Key

  1. Log in to arqera.io
  2. Go to Settings → API Keys
  3. Click Create Key
  4. Copy the key (starts with ak_)

2. Make Your First Call

curl -X POST https://api.arqera.io/v1/governance/evaluate \
-H "Authorization: Bearer ak_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"action": "send_email",
"context": {
"to": "john@example.com",
"subject": "Meeting follow-up"
}
}'

Response:

{
"verdict": "PROCEED",
"evidence_id": "ev_abc123",
"reasoning": "Action matches policy: routine communications allowed",
"evaluated_at": "2026-04-13T10:30:00Z",
"duration_ms": 12
}

3. Act on the Verdict

const response = await fetch('https://api.arqera.io/v1/governance/evaluate', {
method: 'POST',
headers: {
'Authorization': 'Bearer ak_your_api_key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
action: 'send_email',
context: { to: 'john@example.com', subject: 'Meeting follow-up' }
})
});

const { verdict, evidence_id } = await response.json();

if (verdict === 'PROCEED') {
// Execute the action
await sendEmail(to, subject, body);
} else if (verdict === 'ESCALATE') {
// Route to human approval
await createApprovalRequest(evidence_id);
} else if (verdict === 'BLOCK') {
// Stop and log
console.log('Action blocked by governance');
}

Core Concepts

Actions

An action is anything your AI wants to do:

Action TypeExamples
send_emailSend an email
read_fileAccess a document
create_recordAdd to database
call_apiExternal API call
spend_moneyPurchase, payment

Context

Context is the details of the action. The more context, the better the evaluation:

{
"action": "send_email",
"context": {
"to": "john@example.com",
"cc": ["team@company.com"],
"subject": "Q4 Contract Discussion",
"body_preview": "I'd like to discuss the terms...",
"has_attachments": true,
"attachment_types": ["pdf"],
"sender_role": "sales_rep",
"recipient_type": "external"
}
}

Verdicts

VerdictWhat to doHTTP Status
PROCEEDExecute the action200
ESCALATEQueue for human approval200
BLOCKDo not execute200

Note: All verdicts return 200. The verdict itself tells you what to do.

Evidence

Every evaluation creates an evidence record. This is your audit trail:

{
"evidence_id": "ev_abc123",
"action": "send_email",
"verdict": "PROCEED",
"reasoning": "Policy: routine-comms matched. All laws passed.",
"laws_evaluated": [
{"law": "safety_dominance", "passed": true},
{"law": "budget_conservation", "passed": true},
{"law": "bounded_autonomy", "passed": true}
],
"timestamp": "2026-04-13T10:30:00Z",
"signature": "sha256:abc123..."
}

API Reference

POST /v1/governance/evaluate

Evaluate an action against governance policies.

Request:

{
"action": "string (required)",
"context": "object (required)",
"agent_id": "string (optional)",
"user_id": "string (optional)",
"tenant_id": "string (optional)",
"idempotency_key": "string (optional)"
}

Response:

{
"verdict": "PROCEED | ESCALATE | BLOCK",
"evidence_id": "string",
"reasoning": "string",
"evaluated_at": "ISO 8601 timestamp",
"duration_ms": "number",
"escalation_id": "string (if ESCALATE)",
"block_reason": "string (if BLOCK)"
}

POST /v1/evidence

Submit evidence for an action that bypassed real-time evaluation.

Request:

{
"action": "string",
"context": "object",
"outcome": "success | failure",
"executed_at": "ISO 8601 timestamp"
}

GET /v1/evidence/{evidence_id}

Retrieve a specific evidence record.

GET /v1/evidence

List evidence records with filters.

Query params:

  • from — Start timestamp
  • to — End timestamp
  • action — Filter by action type
  • verdict — Filter by verdict
  • limit — Max results (default 100)
  • cursor — Pagination cursor

POST /v1/approvals/{approval_id}/approve

Approve an escalated action.

POST /v1/approvals/{approval_id}/reject

Reject an escalated action.

GET /v1/approvals

List pending approvals.


SDKs

Python

pip install arqera
from arqera import ArqeraClient

client = ArqeraClient(api_key="ak_your_api_key")

# Evaluate an action
result = client.evaluate(
action="send_email",
context={
"to": "john@example.com",
"subject": "Meeting follow-up"
}
)

if result.verdict == "PROCEED":
# Execute
pass
elif result.verdict == "ESCALATE":
# Queue for approval
approval_id = result.escalation_id

Full Python SDK documentation →

TypeScript

npm install @arqera/sdk
import { ArqeraClient } from '@arqera/sdk';

const client = new ArqeraClient({ apiKey: 'ak_your_api_key' });

const result = await client.evaluate({
action: 'send_email',
context: {
to: 'john@example.com',
subject: 'Meeting follow-up'
}
});

if (result.verdict === 'PROCEED') {
// Execute
} else if (result.verdict === 'ESCALATE') {
// Queue for approval
}

Full TypeScript SDK documentation →


Common Patterns

Governed Agent

Wrap your AI agent to evaluate every action:

class GovernedAgent:
def __init__(self, arqera_client, agent):
self.governance = arqera_client
self.agent = agent

async def execute(self, action, context):
# Evaluate first
result = await self.governance.evaluate(action, context)

if result.verdict == "PROCEED":
return await self.agent.execute(action, context)
elif result.verdict == "ESCALATE":
return {"status": "pending_approval", "id": result.escalation_id}
else:
return {"status": "blocked", "reason": result.block_reason}

Governed Chatbot

Add governance to LLM function calls:

async def handle_function_call(function_name, arguments):
# Evaluate the function call
result = await arqera.evaluate(
action=f"function_call:{function_name}",
context=arguments
)

if result.verdict == "PROCEED":
return await execute_function(function_name, arguments)
elif result.verdict == "ESCALATE":
return f"This action needs approval. Reference: {result.escalation_id}"
else:
return f"I can't do that. Reason: {result.block_reason}"

Batch Evaluation

Evaluate multiple actions at once:

results = await client.evaluate_batch([
{"action": "send_email", "context": {...}},
{"action": "read_file", "context": {...}},
{"action": "create_record", "context": {...}}
])

for result in results:
print(f"{result.action}: {result.verdict}")

Async Evidence

For high-throughput scenarios, submit evidence asynchronously:

# Execute first, submit evidence after
await execute_action(action, context)

# Submit evidence (non-blocking)
await client.submit_evidence(
action=action,
context=context,
outcome="success"
)

Webhooks

Get notified when things happen:

# In your webhook handler
@app.post("/webhooks/arqera")
async def handle_webhook(request):
event = request.json()

if event["type"] == "approval.created":
# Notify the right person
await notify_approver(event["data"]["approval_id"])

elif event["type"] == "approval.resolved":
# Continue the workflow
if event["data"]["resolution"] == "approved":
await resume_action(event["data"]["evidence_id"])

Configure webhooks at Settings → Webhooks in the dashboard.


Rate Limits

PlanEvaluations/secondEvidence records/day
Free1010,000
Personal50100,000
Pro100Unlimited
Team200Unlimited
EnterpriseCustomUnlimited

Rate limit headers:

  • X-RateLimit-Limit — Your limit
  • X-RateLimit-Remaining — Remaining calls
  • X-RateLimit-Reset — Reset timestamp

Error Handling

try:
result = await client.evaluate(action, context)
except ArqeraAuthError:
# Invalid API key
except ArqeraRateLimitError as e:
# Wait and retry
await asyncio.sleep(e.retry_after)
except ArqeraServerError:
# Temporary issue, retry with backoff

HTTP status codes:

CodeMeaning
200Success (check verdict)
400Bad request (invalid input)
401Unauthorized (bad API key)
429Rate limited
500Server error

Testing

Sandbox Mode

Use the sandbox for testing without affecting production data:

curl -X POST https://sandbox.api.arqera.io/v1/governance/evaluate \
-H "Authorization: Bearer ak_sandbox_your_key" \
...

Test Policies

Create test policies in the dashboard under Governance → Test Policies.

Mock Responses

The SDK supports mock mode:

client = ArqeraClient(api_key="ak_test", mock=True)
client.set_mock_verdict("PROCEED") # All evaluations return PROCEED

Security

  • API keys are tenant-scoped. One key per environment.
  • TLS 1.3 for all connections
  • Evidence is signed with SHA-256
  • Audit logs are immutable

Rotate keys regularly: Settings → API Keys → Rotate.


Next Steps


Questions? developers@arqera.io