← Back to Home

API Reference

Complete reference for SynAuth's REST API, Python SDK, and MCP tools. Base URL: https://synauth.fly.dev/api/v1

Authentication

SynAuth uses two authentication methods depending on who is making the request.

Agent requests X-API-Key

Agents authenticate with an API key in the X-API-Key header. Keys start with aa_ and are generated when you create your account in the iOS app.

curl https://synauth.fly.dev/api/v1/actions \
  -H "X-API-Key: aa_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"action_type": "communication", "title": "Send email"}'

Device requests X-Device-Id

The iOS app authenticates with a device ID in the X-Device-Id header. Device IDs start with dev_ and are assigned during device registration.

curl https://synauth.fly.dev/api/v1/pending \
  -H "X-Device-Id: dev_your_device_id"

Errors & Status Codes

All errors return JSON with a detail field describing the problem.

Code Meaning Common Causes
200SuccessRequest completed normally
400Bad RequestMissing required field, invalid enum value, malformed JSON
401UnauthorizedMissing or invalid API key / device ID
403ForbiddenInvalid TOTP code, content hash mismatch, host not in allowed list
404Not FoundAction request, credential, or rule doesn't exist
409ConflictAction already resolved, credential already exists, TOTP already configured
429Rate LimitedToo many requests. Check Retry-After header
502Bad GatewayUpstream API error during vault execution or Stripe call
// Error response format
{
  "detail": "Action request is 'approved', not pending"
}

Rate Limits

Sliding window rate limits per identity. Headers included in every response.

IdentityLimitWindow
API Key (agents)120 requests60 seconds
Device ID (iOS app)60 requests60 seconds
IP (unauthenticated)10 requests60 seconds

Response headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After (when limited).

POST /actions Agent

Create Action

Request biometric approval for an agent action. Returns immediately — the action starts as pending unless a rule auto-resolves it.

Request Body

FieldTypeRequiredDescription
action_typestringYesOne of: communication, purchase, scheduling, legal, data_access, social, system
titlestringYesHuman-readable summary (max 200 chars)
descriptionstringNoDetailed description (max 2000 chars)
risk_levelstringNolow, medium (default), high, critical
reversiblebooleanNoWhether the action can be undone (default: true)
amountfloatNoDollar amount for financial actions (must be positive)
currencystringNoISO currency code (default: USD)
recipientstringNoRecipient name or identifier (max 500 chars)
metadataobjectNoArbitrary key-value data (max 20 keys, 8KB serialized)
expires_in_secondsintNoSeconds until expiry (default: 300, range: 30–3600)
callback_urlstringNoHTTPS webhook URL for status updates

Response

{
  "id": "act_a1b2c3d4e5f6g7h8",
  "status": "pending",
  "action_type": "purchase",
  "title": "Buy office supplies",
  "content_hash": null,
  "rule_applied": null
}

If a spending limit is exceeded, the response includes a spending_limit_exceeded object with period, limit, spent, and requested amounts.

If a rule auto-resolves the action, status will be approved or denied immediately and rule_applied will contain the rule name.

SDK

from synauth import SynAuthClient

client = SynAuthClient(api_key="aa_your_key")

result = client.request_action(
    action_type="purchase",
    title="Buy office supplies",
    amount=49.99,
    risk_level="medium",
    callback_url="https://your-app.com/webhook",
)

print(result["id"])      # "act_..."
print(result["status"])  # "pending" or "approved"
GET /actions/{request_id} Agent

Get Action

Check the current status of an action request. Also triggers lazy expiration — if the action has expired, its status updates to expired on this call.

curl https://synauth.fly.dev/api/v1/actions/act_a1b2c3d4e5f6g7h8 \
  -H "X-API-Key: aa_your_key"

SDK

status = client.get_status("act_a1b2c3d4e5f6g7h8")
# Returns full action object including status, timestamps, metadata

To poll until resolution:

result = client.wait_for_result("act_a1b2c3d4e5f6g7h8")
# Blocks until approved, denied, or expired
print(result["status"])  # "approved", "denied", or "expired"
GET /actions Agent

List Actions

List this agent's action requests with optional filtering.

ParameterTypeDescription
limitintMax results (default: 50)
statusstringFilter: pending, approved, denied, expired
action_typestringFilter by action type
curl "https://synauth.fly.dev/api/v1/actions?status=approved&limit=10" \
  -H "X-API-Key: aa_your_key"

// Response
{
  "actions": [
    {
      "id": "act_a1b2c3d4e5f6g7h8",
      "action_type": "purchase",
      "title": "Buy office supplies",
      "status": "approved",
      "resolved_by": "face_id",
      "resolved_at": "2026-02-18T15:30:00Z",
      ...
    }
  ]
}
GET /agent/spending-summary Agent

Spending Summary

Check your current spending against configured limits. Returns all applicable limits with current spend, remaining budget, and utilization percentage.

curl https://synauth.fly.dev/api/v1/agent/spending-summary \
  -H "X-API-Key: aa_your_key"

// Response
{
  "agent_id": "my-agent",
  "summaries": [
    {
      "limit_id": "lim_abc123",
      "period": "daily",
      "limit": 100.00,
      "spent": 42.50,
      "remaining": 57.50,
      "utilization": 0.425
    }
  ]
}

SDK

summary = client.get_spending_summary()
for s in summary["summaries"]:
    print(f"{s['period']}: ${s['remaining']:.2f} remaining")
GET /vault/services Agent

List Vault Services

Discover what credentials are available in the vault. Agents see service names and allowed hosts — never the credentials themselves.

curl https://synauth.fly.dev/api/v1/vault/services \
  -H "X-API-Key: aa_your_key"

// Response
{
  "services": [
    {
      "service_name": "openai",
      "auth_type": "bearer",
      "allowed_hosts": ["api.openai.com"],
      "description": "OpenAI API key"
    }
  ]
}
POST /vault/execute/{request_id} Agent

Execute API Call

Execute a credentialed API call after biometric approval. The agent describes the call; SynAuth injects the credential and makes the request. The agent never sees the raw credential.

This is a two-step process:

1
Create action with vault metadata — include vault_execute: true, service_name, method, url, and optional headers/body in the metadata field.
2
After approval, call vault/execute — SynAuth retrieves the credential, validates the URL against allowed hosts, injects the auth header, and makes the HTTP call. Single-use: each approved action can only be executed once.

Step 1: Create the action

curl https://synauth.fly.dev/api/v1/actions \
  -H "X-API-Key: aa_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "action_type": "system",
    "title": "Call OpenAI API",
    "risk_level": "medium",
    "metadata": {
      "vault_execute": true,
      "service_name": "openai",
      "method": "POST",
      "url": "https://api.openai.com/v1/chat/completions",
      "body": "{\"model\": \"gpt-4\", \"messages\": [{\"role\": \"user\", \"content\": \"Hello\"}]}"
    }
  }'

Step 2: Execute after approval

curl -X POST https://synauth.fly.dev/api/v1/vault/execute/act_a1b2c3d4 \
  -H "X-API-Key: aa_your_key"

// Response: proxied response from the upstream API
{
  "status_code": 200,
  "headers": {"content-type": "application/json", ...},
  "body": "{\"id\": \"chatcmpl-...\", ...}"
}

WYSIWYS Verification

For vault execution requests, SynAuth computes a content hash of the execution parameters. The iOS app verifies this hash during approval — ensuring what you saw is what gets executed. If the parameters are tampered with after approval, execution fails with a 403 Content hash mismatch error.

SDK

result = client.execute_api_call(
    service_name="openai",
    method="POST",
    url="https://api.openai.com/v1/chat/completions",
    body='{"model": "gpt-4", "messages": [{"role": "user", "content": "Hello"}]}'
)
# Handles the full flow: create action → wait for approval → execute

Manage Credentials

Store, list, and delete API credentials in the vault. All credentials are encrypted at rest. Only accessible from the iOS app (device auth).

POST /vault/credentials Device

Store Credential

FieldTypeRequiredDescription
service_namestringYesUnique name for this service (e.g., openai)
credential_valuestringYesThe raw credential (encrypted at rest)
auth_typestringNobearer (default), api_key, basic, custom
auth_headerstringNoHeader name (default: Authorization)
allowed_hostsstring[]YesHostnames where this credential can be sent (e.g., ["api.openai.com"])
descriptionstringNoHuman-readable description
GET /vault/credentials Device

List Credentials

Returns service names, auth types, allowed hosts, and descriptions. Credential values are never returned.

DELETE /vault/credentials/{credential_id} Device

Delete Credential

Permanently removes a stored credential from the vault.

Approve & Deny Actions

Resolve pending action requests from the iOS app.

POST /actions/{request_id}/approve Device

Approve

FieldTypeRequiredDescription
totp_codestringNoTOTP code for authenticator-based approval
content_hashstringConditionalRequired for vault execution requests (WYSIWYS verification)
payment_methodstringNoapple_pay for Apple Pay purchases
signaturestringNoECDSA-P256 signature for cryptographic device auth
timestampstringNoISO8601 timestamp included in signed payload

Verification methods (in resolved_by): face_id (default), totp, apple_pay.

POST /actions/{request_id}/deny Device

Deny

FieldTypeRequiredDescription
reasonstringNoWhy the action was denied (max 500 chars)
signaturestringNoECDSA-P256 signature
timestampstringNoISO8601 timestamp
GET /pending Device

Get Pending Actions

List all pending action requests for this device's account. Sorted by risk level (critical first), then by creation time.

// Response
{
  "requests": [
    {
      "id": "act_a1b2c3d4e5f6g7h8",
      "action_type": "purchase",
      "title": "Buy office supplies",
      "risk_level": "medium",
      "amount_cents": 4999,
      "status": "pending",
      "created_at": "2026-02-18T15:00:00Z",
      "expires_at": "2026-02-18T15:05:00Z"
    }
  ]
}

Rules Engine

Auto-approve or auto-deny actions based on configurable rules. Rules are evaluated in order — first match wins.

POST /rules Device

Create Rule

FieldTypeRequiredDescription
namestringYesRule name (max 200 chars)
decisionstringYesauto_approve or auto_deny
action_typestringNoMatch specific action type (null = all types)
agent_idstringNoMatch specific agent (null = all agents)
risk_level_maxstringNoMax risk level for the rule to apply
amount_max_centsintNoMax amount in cents for the rule to apply
GET /rules Device

List Rules

DELETE /rules/{rule_id} Device

Delete Rule

Spending Limits

Hard spending caps per agent, per action type, per time period. Limits override auto-approve rules — even trusted agents hit the ceiling.

POST /spending-limits Device

Create Spending Limit

FieldTypeRequiredDescription
periodstringYesdaily, weekly, or monthly
amount_limitfloatYesDollar limit (must be positive)
agent_idstringNoSpecific agent (null = all agents)
action_typestringNoSpecific action type (null = all types)
GET /spending-limits Device

List Spending Limits

DELETE /spending-limits/{limit_id} Device

Delete Spending Limit

Python SDK

pip install synauth — Full-featured client with typed errors, polling, and convenience methods.

SynAuthClient

from synauth import SynAuthClient

client = SynAuthClient(api_key="aa_your_key")

# Request approval (non-blocking)
result = client.request_action(
    action_type="purchase",
    title="Order supplies",
    amount=29.99,
)

# Check status
status = client.get_status(result["id"])

# Wait for result (blocking, polls every 2s)
final = client.wait_for_result(result["id"])

# Get spending summary
summary = client.get_spending_summary()

# Get history
history = client.get_history(limit=20)

Credential Vault

Vault methods are built into SynAuthClient. The agent describes an API call; SynAuth injects stored credentials after biometric approval.

# List available vault services
services = client.list_vault_services()

# Execute a credentialed API call (full approval + execution flow)
result = client.execute_api_call(
    service_name="openai",
    method="POST",
    url="https://api.openai.com/v1/chat/completions",
    body='{"model": "gpt-4", "messages": [...]}'
)

SynPayClient

Payment-only wrapper for agents that only need purchase authorization. Simpler interface, same backend.

from synauth import SynPayClient

pay = SynPayClient(api_key="aa_your_key")

# Request a payment
request = pay.request_payment(
    amount=29.99,
    merchant="OpenAI",
    description="GPT-5 API credits",
)
result = pay.wait_for_result(request["id"])

MCP Tools

pip install synauth-mcp — Drop-in biometric approval for Claude, Cursor, and any MCP-compatible agent.

# claude_desktop_config.json
{
  "mcpServers": {
    "synauth": {
      "command": "synauth-mcp",
      "env": { "SYNAUTH_API_KEY": "aa_your_key" }
    }
  }
}

Available Tools

ToolDescription
request_approvalRequest biometric approval for any agent action. Returns request ID and initial status.
check_approvalCheck approval status by request ID.
wait_for_approvalBlock until approval is resolved (approved, denied, or expired).
get_approval_historyList past resolved requests.
get_spending_summaryCheck current spending against limits.
list_vault_servicesDiscover available vault credentials.
execute_api_callMake a credentialed API call through the vault (full approval + execution flow).

Webhooks

Receive real-time notifications when action requests are resolved. Set callback_url when creating an action.

SynAuth sends a POST to your callback URL with the following payload when an action is approved, denied, or expired:

{
  "id": "act_a1b2c3d4e5f6g7h8",
  "status": "approved",
  "action_type": "purchase",
  "title": "Buy office supplies",
  "risk_level": "medium",
  "amount": 49.99,
  "resolved_by": "face_id",
  "resolved_at": "2026-02-18T15:30:00Z",
  "created_at": "2026-02-18T15:00:00Z"
}

Requirements:

  • Callback URL must be HTTPS
  • Must not point to private/internal IP addresses
  • Failed deliveries are retried up to 5 times with exponential backoff

Action Types

Seven built-in action types for classifying agent actions.

TypeUse CaseExamples
communicationSending messagesEmail, Slack message, SMS
purchaseFinancial transactionsBuy supplies, subscribe to service, fund transfer
schedulingCalendar and bookingBook meeting, reserve restaurant, schedule flight
legalBinding agreementsSign contract, accept terms, authorize document
data_accessSensitive dataExport database, access medical records, download PII
socialPublic contentPost to Twitter, publish article, update profile
systemInfrastructureDeploy code, modify permissions, delete resources

Risk Levels

Four risk levels that determine how actions are displayed, sorted, and evaluated by rules.

LevelPriorityTypical Use
lowLowestRoutine actions. Good candidate for auto-approve rules.
mediumDefaultStandard actions that warrant human review.
highHighSensitive actions. Data access, system changes.
criticalHighestIrreversible or high-value actions. Legal, large purchases.

On the iOS app, pending requests are sorted by risk level (critical first) to surface the most important actions immediately.

Action Statuses

StatusMeaningResolved By
pendingAwaiting human decision
approvedHuman approved the actionface_id, totp, apple_pay, rule:{name}, stripe
deniedHuman denied or rule blockeduser, rule:{name}, spending_limit:{id}
expiredNo response within timeoutsystem

Start building

Free tier: 25 verified actions/month. Unlimited: $0.99/month.

pip install synauth