AIRGAP Docs

API Reference

Table of contents

The AIRGAP public API. REST over HTTPS, plus a server-sent-events stream. JSON in, JSON out. Versioned at /api/v1/.

The endpoints documented here are the stable public contract. Anything not documented here is internal and may change without notice.


Authentication

Every request must carry a bearer token in the Authorization header:

Authorization: Bearer airgap_<64-hex-chars>

Mint a token at airgap.finance/settingsAPI Tokens → New Token. The plaintext token is shown once at creation time: copy it then. Tokens are stored hashed (SHA-256); the plaintext never round-trips again. Format: the literal prefix airgap_ followed by 32 random bytes hex-encoded (71 characters total).

Tokens are scoped to your user. Rate limits are per token, enforced in a sliding window that holds across our servers (you can't multiply your quota by hitting different instances). The ceilings are tiered by endpoint cost:

SurfaceEndpointsLimit
ReadsGET /api/v1/agents, GET /api/v1/signals, GET /api/v1/signals/:id90 / min per token
WritesPOST /api/v1/agents/:id/pause, .../resume30 / min per token
Stream connectsGET /api/v1/stream20 / min per token
Bad / missing authany v1 endpoint10 / min per IP

These ceilings sit well above normal use: a consumer polling once a second never approaches the read limit, and control actions are far rarer. They exist to cut runaway loops and abuse, not to pace a real integration. If you have a genuine high-volume need, get in touch rather than working around the limit. When you hit one:

  • Status 429.
  • Response: {"error": "Rate limit exceeded. Retry after the delay indicated by Retry-After.", "code": "RATE_LIMITED"}.
  • Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (seconds until the window resets), Retry-After.

Revoke a token any time from the same Settings panel. Once revoked, the next request with that token returns 401 Unauthorized.


GET /api/v1/agents

List your agents. Returns a compact summary suitable for sidebars and dashboards.

Method. GET Path. /api/v1/agents Auth. Required. Query params. None. Request body. None.

Response 200.

{
  "agents": [
    {
      "id": "uuid",
      "name": "string",
      "type": "x | farcaster | launch-scanner | wallet-watcher | account-stalker | contract-monitor | custom | discord | telegram",
      "status": "idle | running | paused | error",
      "intensity": "light | standard | aggressive",
      "signal_count": 0,
      "last_run": "2026-05-10T12:34:56.000Z | null"
    }
  ]
}

Agents are returned newest-first (by created_at).

Errors. 401 (missing/invalid token), 429 (rate limited).

Example.

curl -H "Authorization: Bearer airgap_xxxxxxxx" \
  https://airgap.finance/api/v1/agents

POST /api/v1/agents/:id/pause

Pause a running agent. The worker stops polling on the next tick. Idempotent: pausing an already-paused agent is a no-op.

Method. POST Path. /api/v1/agents/{id}/pause Auth. Required. Path params. id: the agent UUID. Query params. None. Request body. None.

Response 200. The updated agent summary.

{
  "agent": {
    "id": "uuid",
    "name": "string",
    "type": "...",
    "status": "paused",
    "intensity": "...",
    "signal_count": 0,
    "last_run": "..."
  }
}

Errors.

  • 401: missing/invalid token.
  • 404: agent not found, or not owned by you.
  • 429: rate limited.
  • 501: agent type is telegram. Telegram user-account agents require a self-hosted process; pause/resume via the API is not supported. Use other agent types or contact support.

Example.

curl -X POST \
  -H "Authorization: Bearer airgap_xxxxxxxx" \
  https://airgap.finance/api/v1/agents/AGENT_ID/pause

POST /api/v1/agents/:id/resume

Resume a paused agent. Re-runs the access check at resume time: if the agent has no active x402 authorization headroom or your token balance dropped below threshold for a holder-gated resource, the resume is rejected. On success, status moves to running and any prior error_message is cleared.

Method. POST Path. /api/v1/agents/{id}/resume Auth. Required. Path params. id: the agent UUID. Query params. None. Request body. None.

Response 200. The updated agent summary, with status: "running".

{
  "agent": {
    "id": "uuid",
    "name": "string",
    "type": "...",
    "status": "running",
    "intensity": "...",
    "signal_count": 0,
    "last_run": "..."
  }
}

Errors.

  • 401: missing/invalid token, or the user record backing the token is gone.
  • 403: access denied. No active x402 authorization for the agent, or token holding fell below the holder threshold for a holder-gated resource.
  • 404: agent not found, or not owned by you.
  • 429: rate limited.
  • 501: agent type is telegram; not supported via API (see pause).

Example.

curl -X POST \
  -H "Authorization: Bearer airgap_xxxxxxxx" \
  https://airgap.finance/api/v1/agents/AGENT_ID/resume

GET /api/v1/signals

List signals delivered to your account, newest first. Supports filtering and pagination.

Method. GET Path. /api/v1/signals Auth. Required.

Query params.

ParamTypeRequiredNotes
agent_idUUIDoptionalRestrict to one agent.
agent_typestringoptionalRestrict to one agent type (e.g. wallet-watcher).
min_confidencenumber 0–1optionalFloor on AI confidence. Coerced from string.
limitint 1–100optionalPage size. Default 50.
offsetint ≥ 0optionalPage offset. Default 0.
afterISO 8601 datetimeoptionalOnly signals with created_at >= after.

Invalid params return 400 with a zod-formatted details object.

Request body. None.

Response 200.

{
  "signals": [
    {
      "id": "uuid",
      "user_id": "uuid",
      "agent_id": "uuid",
      "agent_type": "wallet-watcher",
      "title": "string",
      "body": "string",
      "confidence": 0.82,
      "source_url": "https://...",
      "source_platform": "string",
      "rich_data": { },
      "enrichments": { },
      "token_ca": "0x... | null",
      "created_at": "2026-05-10T12:34:56.000Z"
    }
  ],
  "total": 1234,
  "has_more": true
}

rich_data is shaped by agent_type: see the Signal*RichData types in the source for narrowed shapes (Telegram, X, Farcaster, Launch Scanner, Wallet Watcher, Account Stalker, Contract Monitor, Custom).

total is the exact count for the current filter set; has_more is true when total > offset + signals.length.

Errors.

  • 400: invalid query. Body: {"error": "Invalid query", "details": { ... }}.
  • 401: missing/invalid token.
  • 429: rate limited.
  • 500: query failed. Body: {"error": "Query failed"}.

Example.

curl -G \
  -H "Authorization: Bearer airgap_xxxxxxxx" \
  --data-urlencode "agent_type=wallet-watcher" \
  --data-urlencode "min_confidence=0.7" \
  --data-urlencode "limit=20" \
  https://airgap.finance/api/v1/signals

GET /api/v1/signals/:id

Fetch one signal in full. Returns every column on the row, including read/dismissed flags and the full rich_data/enrichments blobs.

Method. GET Path. /api/v1/signals/{id} Auth. Required. Path params. id: the signal UUID. Query params. None. Request body. None.

Response 200.

{
  "signal": {
    "id": "uuid",
    "user_id": "uuid | null",
    "agent_id": "uuid | null",
    "agent_type": "...",
    "title": "string",
    "body": "string",
    "confidence": 0.82,
    "source_url": "...",
    "source_platform": "...",
    "rich_data": { },
    "enrichments": { },
    "read": false,
    "dismissed": false,
    "token_ca": "0x... | null",
    "created_at": "..."
  }
}

Errors.

  • 401: missing/invalid token.
  • 404: signal not found, or not owned by you.
  • 429: rate limited.

Example.

curl -H "Authorization: Bearer airgap_xxxxxxxx" \
  https://airgap.finance/api/v1/signals/SIGNAL_ID

GET /api/v1/stream

Server-Sent Events stream of new signals for the authenticated user, pushed as they are created.

Method. GET Path. /api/v1/stream Auth. Required. Connect attempts are rate limited at 20 / min per token (see the table under Authentication): plenty for reconnects, a hard cap on connection churn. Query params. None. Request body. None.

Response. 200 with Content-Type: text/event-stream. The stream stays open until the client disconnects or the server reaches its connection lifetime (see Reconnection).

Event shape.

  • On connect, the server sends a single SSE comment to flush headers and prove the connection is live:
    : connected
    
  • Every 20 seconds, a heartbeat comment keeps proxies from killing the connection:
    : ping
    
  • For each new signal inserted into your account, the server emits a named event:
    event: signal
    data: { ...signal row JSON... }
    
    The data payload is the raw row from the signals table: same shape as the object returned by GET /api/v1/signals/:id under signal, minus the wrapper.

Concurrency. Connects are capped at 20/min per token and channels are scoped per token, so a second token from the same user gets its own independent stream. One stream per process is the intended pattern: don't fan out dozens.

Connection lifetime. The server holds a stream for up to ~5 minutes, then closes it cleanly and tears down its channel. This is by design (it recycles the serverless instance); a standard EventSource client reconnects automatically, so you see one continuous feed. The reconnect counts against the 20/min connect limit, which is far above one reconnect every five minutes.

Reconnection. The server does not emit id: fields, so Last-Event-ID is not supported. If your client disconnects (or the server reaches its lifetime), reconnect with a fresh GET; backfill any missed signals by hitting GET /api/v1/signals?after=<lastEventTimestamp> before re-subscribing. The 20-second : ping heartbeat is the canonical liveness signal: if you go more than ~45 seconds without one, close the connection and reconnect.

Testing with Postman. Postman closes long-lived SSE responses after roughly a minute regardless of server activity, so a stream that cuts there is the client, not the server. For sustained tests use curl -N, a real EventSource client, or your own HTTP client without a response timeout cap.

Errors.

  • 401: missing/invalid token. Returned as a plain Unauthorized body, not JSON.
  • 503: the service is temporarily unavailable. Retry shortly.

Example.

curl -N \
  -H "Authorization: Bearer airgap_xxxxxxxx" \
  -H "Accept: text/event-stream" \
  https://airgap.finance/api/v1/stream

-N disables curl's output buffering so events appear in real time.