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/.


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. Authenticated requests are rate-limited at 60 requests per minute per token in a sliding window. Anonymous (bad-auth) requests are limited at 10 per minute per IP to keep token-guessing cheap. When you hit the limit:

  • Status 429.
  • Response: {"error": "Rate limit exceeded"}.
  • Headers: X-RateLimit-Remaining, X-RateLimit-Reset (seconds until 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 your subscription lapsed or your token balance dropped below threshold, 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. Subscription inactive, or token holding fell below the holder threshold for a holder-only tier.
  • 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.

| Param | Type | Required | Notes | | --- | --- | --- | --- | | agent_id | UUID | optional | Restrict to one agent. | | agent_type | string | optional | Restrict to one agent type (e.g. wallet-watcher). | | min_confidence | number 0–1 | optional | Floor on AI confidence. Coerced from string. | | limit | int 1–100 | optional | Page size. Default 50. | | offset | int ≥ 0 | optional | Page offset. Default 0. | | after | ISO 8601 datetime | optional | Only 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": { },
      "is_house_signal": false,
      "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": { },
    "is_house_signal": false,
    "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. Subscribes to Supabase Realtime server-side and forwards inserts as SSE.

Method. GET Path. /api/v1/stream Auth. Required. Note: this endpoint authenticates but does not apply the per-minute rate limit — it's a long-lived single connection. Query params. None. Request body. None.

Response. 200 with Content-Type: text/event-stream. The stream stays open until the client disconnects.

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. No hard limit is enforced, but channels are scoped per-token, so a second token from the same user gets its own independent stream. Don't open dozens of these — one per process is the intended pattern.

Errors.

  • 401 — missing/invalid token. Returned as a plain Unauthorized body, not JSON.
  • 503 — server misconfigured (Supabase URL or service-role key missing in the deployment).

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.