parrad API v1.0

API Reference

The parrad REST API lets you query risk scores, capability signals, and alerts for your monitored API stack — and trigger scans programmatically. All responses are JSON.

Base URL: https://parrad.app/api/v1

Authentication

Every request must include your workspace API key as the X-PARRAD-Key header. You can find your key in the dashboard under Settings → parrad API Key.

Keep your key secret. Do not commit it to version control. Use environment variables (parrad_API_KEY) in production.
Request header
X-PARRAD-Key: prd-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Response format

Successful responses are always HTTP 200 (or 201 for created resources, 202 for async jobs) with this envelope:

{
  "ok":   true,
  "data": { /* endpoint-specific payload */ },
  "meta": {
    "api_version": "1.0",
    "timestamp":   "2025-05-17T14:32:00.000Z"
  }
}

Errors

HTTP codeMeaning
401Missing or invalid X-PARRAD-Key
400Bad request — missing required field, invalid JSON
404Resource or endpoint not found
409Conflict — API already exists
500Internal server error
{
  "ok":    false,
  "error": "API not found",
  "code":  404
}

GET /apis

Returns all monitored APIs with their current risk and capability scores.

GET /api/v1/apis
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/apis
Response
{
  "ok": true,
  "data": {
    "apis": [
      {
        "id":          "hubspot-crm-v3",
        "name":        "HubSpot CRM API v3",
        "vendor":      "HubSpot",
        "criticality": "critical",
        "version":     "v3",
        "active":      true,
        "risk_score":  42,
        "risk_level":  "yellow",
        "opp_score":   28,
        "opp_level":   "low",
        "last_run":    "2025-05-17T06:00:00.000Z"
      }
    ]
  },
  "meta": { "api_version": "1.0", "timestamp": "2025-05-17T14:32:00.000Z" }
}

GET /apis/{id}

Returns a single API's configuration and full state blob.

GET /api/v1/apis/{id}
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/apis/hubspot-crm-v3
Response
{
  "ok": true,
  "data": {
    "api": {
      "id": "hubspot-crm-v3", "name": "HubSpot CRM API v3",
      "vendor": "HubSpot", "criticality": "critical",
      "changelog_url": "https://developers.hubspot.com/changelog",
      "state": {
        "risk_score": 42,
        "risk_level":  "yellow",
        "last_run":     "2025-05-17T06:00:00.000Z"
      }
    }
  },
  "meta": { "api_version": "1.0", "timestamp": "2025-05-17T14:32:00.000Z" }
}

GET /apis/{id}/risk

Returns the full risk picture for a single API: score, level, confirmed signals, velocity, and score history.

GET /api/v1/apis/{id}/risk
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/apis/hubspot-crm-v3/risk
Response
{
  "ok": true,
  "data": {
    "api_id":              "hubspot-crm-v3",
    "score":               42,
    "level":               "yellow",
    "velocity_multiplier": 1.3,
    "velocity_avg_days":   18,
    "signals": [
      {
        "signal_id":   "deprecation_announced",
        "layer":       1,
        "detected_at": "2025-05-10T09:00:00.000Z",
        "excerpt":     "v1 Lists API sunset extended to April 30, 2026",
        "source_url":  "https://developers.hubspot.com/changelog/..."
      }
    ],
    "score_history": [
      { "date": "2025-05-16", "score": 38 },
      { "date": "2025-05-17", "score": 42 }
    ],
    "last_run": "2025-05-17T06:00:00.000Z"
  },
  "meta": { "api_version": "1.0", "timestamp": "2025-05-17T14:32:00.000Z" }
}

GET /apis/{id}/opp

Returns capability signals for a single API: score, velocity, and signal list.

GET /api/v1/apis/{id}/opp
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/apis/hubspot-crm-v3/opp
Response
{
  "ok": true,
  "data": {
    "api_id":         "hubspot-crm-v3",
    "score":          55,
    "level":          "medium",
    "velocity_label": "active",
    "velocity_rate":  2.1,
    "signals": [
      {
        "signal_id": "new_integration_announced",
        "excerpt":   "HubSpot launches native AI workflow builder",
        "source_url": "https://..."
      }
    ]
  },
  "meta": { "api_version": "1.0", "timestamp": "2025-05-17T14:32:00.000Z" }
}

POST /apis

Add a new API to your watchlist. Accepts either a domain (auto-generates id, name, vendor) or a fully specified config object with an explicit id.

FieldTypeDescription
domainstringRoot domain, e.g. stripe.com. Required if id not provided.
idstringExplicit slug, e.g. stripe-api. Required if domain not provided.
namestringHuman-readable name. Auto-derived from domain if omitted.
vendorstringVendor name. Auto-derived from domain if omitted.
criticalitystringcritical | high | medium. Default: high.
changelog_urlstringOptional. URL of the API changelog.
github_repostringOptional. e.g. stripe/stripe-node.
npm_packagestringOptional. e.g. stripe.
POST /api/v1/apis
curl
curl -X POST -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "stripe.com", "criticality": "critical" }' \
  https://parrad.app/api/v1/apis
Response — 201 Created
{
  "ok": true,
  "data": {
    "api": {
      "id": "stripe", "name": "Stripe API",
      "vendor": "Stripe", "criticality": "critical",
      "active": true, "risk_score": null, "last_run": null
    }
  },
  "meta": { "api_version": "1.0", "timestamp": "2025-05-17T14:32:00.000Z" }
}

DELETE /apis/{id}

Removes an API from the watchlist and deletes its local state file. Permanent. The response includes type indicating which storage path was used. For bundle APIs (shipped in config), config.active is set to false in Supabase before the record is removed. For user-added APIs, the row is hard-deleted.

DELETE /api/v1/apis/{id}
curl
curl -X DELETE -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/apis/stripe
Response
{
  "ok": true,
  "data": {
    "deleted": "stripe",
    "type":    "user-added"  // "bundle" | "user-added"
  },
  "meta": { "api_version": "1.0", "timestamp": "2025-05-17T14:32:00.000Z" }
}

GET /alerts

Returns the full alert feed. Alerts are logged automatically when a risk score crosses a threshold band between runs (Green / Yellow / Orange / Red at 20 / 50 / 75).

GET /api/v1/alerts
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/alerts
Response
{
  "ok": true,
  "data": {
    "total":  12,
    "unread": 3,
    "alerts": [
      {
        "id":             "m8k2a9xz",
        "api_id":         "hubspot-crm-v3",
        "api_name":       "HubSpot CRM API v3",
        "previous_level": "yellow",
        "new_level":      "orange",
        "score":          52,
        "timestamp":      "2025-05-17T06:01:00.000Z",
        "read":           false,
        "top_signal":     "deprecation_announced"
      }
    ]
  },
  "meta": { "api_version": "1.0", "timestamp": "2025-05-17T14:32:00.000Z" }
}

GET /summary

Global workspace overview: API count, average scores, level distribution, the 3 most-at-risk APIs, and the 5 most recent alerts.

GET /api/v1/summary
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/summary
Response
{
  "ok": true,
  "data": {
    "total_apis":     8,
    "avg_risk_score": 31,
    "avg_opp_score":  44,
    "level_counts":   { "green": 4, "yellow": 2, "orange": 1, "red": 1 },
    "unread_alerts":  3,
    "most_at_risk": [
      { "api_id": "hubspot-crm-v3", "score": 78, "level": "red"    },
      { "api_id": "lemlist-v2",     "score": 52, "level": "orange" },
      { "api_id": "stripe",         "score": 38, "level": "yellow" }
    ],
    "latest_alerts": [ /* same shape as GET /alerts */ ]
  },
  "meta": { "api_version": "1.0", "timestamp": "2025-05-17T14:32:00.000Z" }
}

POST /runs

Triggers a scan for 1–10 APIs asynchronously. Returns a run_id immediately (HTTP 202). api_ids is required — full-stack scans run via cron only. Optional scope controls what is collected: "risk", "opp", or "both" (default). Run records are shared with the UI trigger path — both write to the same runs/{run_id}.json store.

FieldTypeDescription
run_idstringUnique identifier, format runs-{epoch_ms}
statusstringqueuedrunningdone | error | orphaned
started_atstringISO timestamp
finished_atstring | nullISO timestamp, null until terminal state
last_heartbeat_atstringUpdated ~every 5 s while scan is running; use to detect stalls
trigger_sourcestring"api_v1" | "ui" | "cron"
api_idsstring[]APIs included in this run
scopestring"risk" | "opp" | "both"
errorstring | nullError message, only set when status = "error"
_reusedboolPresent and true when a live identical scan was reused (dedup window: 15 s)
POST /api/v1/runs
curl — trigger scan for two APIs
curl -X POST -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "api_ids": ["hubspot-crm-v3", "stripe-api"], "scope": "risk" }' \
  https://parrad.app/api/v1/runs
Response — 202 Accepted
{
  "ok": true,
  "data": {
    "run_id":             "runs-1747486320000",
    "status":             "queued",
    "started_at":         "2026-05-23T10:00:00.000Z",
    "finished_at":        null,
    "last_heartbeat_at":  "2026-05-23T10:00:00.000Z",
    "trigger_source":     "api_v1",
    "api_ids":            ["hubspot-crm-v3", "stripe-api"],
    "scope":              "risk",
    "error":              null
  },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:00:00.000Z" }
}
Error — 400 (api_ids missing or > 10)
{
  "ok": false,
  "error": "{"error":"api_ids_required","detail":"Specify between 1 and 10 api_ids. Full-stack scans run via cron only."}",
  "code": 400
}
Idempotency: Send an Idempotency-Key header (any string) to deduplicate rapid triggers within a 60-second window. Separately, if a single-API scan is already running with a heartbeat <15 s old, the response includes _reused: true and returns the existing run.

GET /runs/{run_id}

Poll run status. Status transitions: queuedrunningdone | error. A run with status = "running" and last_heartbeat_at older than 15 s is returned as "orphaned" (worker died). When done, the response merges current state for each scanned API.

GET /api/v1/runs/{run_id}
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/runs/runs-1747486320000
Response — running
{
  "ok": true,
  "data": {
    "run_id":            "runs-1747486320000",
    "status":            "running",
    "started_at":        "2026-05-23T10:00:00.000Z",
    "finished_at":       null,
    "last_heartbeat_at": "2026-05-23T10:01:15.000Z",
    "trigger_source":    "api_v1",
    "api_ids":           ["hubspot-crm-v3"],
    "scope":             "risk",
    "error":             null
  },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:01:20.000Z" }
}
Response — done
{
  "ok": true,
  "data": {
    "run_id":            "runs-1747486320000",
    "status":            "done",
    "started_at":        "2026-05-23T10:00:00.000Z",
    "finished_at":       "2026-05-23T10:02:30.000Z",
    "last_heartbeat_at": "2026-05-23T10:02:28.000Z",
    "trigger_source":    "api_v1",
    "scope":             "risk",
    "apis": [
      {
        "api_id":      "hubspot-crm-v3",
        "name":        "HubSpot CRM API v3",
        "risk_score":  42,
        "risk_level":  "yellow",
        "opp_score":   18,
        "opp_level":   "low",
        "signals":     [/* sigSlice — see GET /signals for shape */],
        "opp_signals": [],
        "last_run":    "2026-05-23T10:02:00.000Z"
      }
    ]
  },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:02:35.000Z" }
}
Note: API state in apis[] reflects the current merged state, not a point-in-time snapshot. ?at= is reserved for future historical snapshots.

GET /apis/{id}/sources

Manage custom signal sources attached to a specific API. Sources are URL probes (RSS feeds, HTML changelogs, GitHub repos, npm packages) that the scanner reads on each run. Source type is auto-detected from the URL at write time.

GET /api/v1/apis/{id}/sources
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/apis/cargo-api/sources
Response
{
  "ok": true,
  "data": {
    "sources": [
      {
        "id":   "rss-a1b2c3",
        "type": "rss",
        "url":  "https://docs.getcargo.ai/changelog.rss",
        "config": {
          "discovery_validated":  true,
          "discovery_confidence": 0.97
        }
      }
    ]
  },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:00:00.000Z" }
}

Add a source

POST /api/v1/apis/{id}/sources
curl
curl -X POST -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "type": "rss", "url": "https://docs.getcargo.ai/changelog.rss" }' \
  https://parrad.app/api/v1/apis/cargo-api/sources
Response — 201 Created
{
  "ok": true,
  "data": { "sources": [ /* updated sources array */ ] },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:00:00.000Z" }
}
Returns 422 if the URL probe disagrees with the declared type (e.g., passing type: "rss" for an HTML page). Returns 409 if the source already exists.

Edit a source

PATCH /api/v1/apis/{id}/sources/{sid}
curl
curl -X PATCH -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://docs.getcargo.ai/feed.rss" }' \
  https://parrad.app/api/v1/apis/cargo-api/sources/rss-a1b2c3
Response
{ "ok": true, "data": { "sources": [ /* updated */ ] }, "meta": { /* ... */ } }

Remove a source

DELETE /api/v1/apis/{id}/sources/{sid}
curl
curl -X DELETE -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/apis/cargo-api/sources/rss-a1b2c3
Response
{
  "ok": true,
  "data": {
    "deleted": "rss-a1b2c3",
    "sources": [ /* remaining active sources */ ]
  },
  "meta": { /* ... */ }
}
Soft-delete: sets deleted_at on the record. The scanner skips soft-deleted sources; historical signals already collected are preserved.

GET /apis/{id}/downstream

Returns the dependency footprint of a single API: which APIs depend on it (direct and indirect), which APIs it depends on, and all associated risk/capability impacts.

GET /api/v1/apis/{id}/downstream
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/apis/linkedin-api/downstream
Response
{
  "ok": true,
  "data": {
    "api_id": "linkedin-api",
    "name":   "LinkedIn API",
    "direct_dependents":   [{ "api_id": "cargo-api", "name": "Cargo API" }],
    "indirect_dependents": [],
    "depends_on": [],
    "risk_impacts":        [ /* impact objects — see GET /downstream/risks */ ],
    "capability_impacts": [],
    "edges":               [ /* edge objects — see GET /downstream */ ]
  },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:00:00.000Z" }
}

GET /signals

Returns all collected risk signals across your stack. Primary source is the collected_signals Supabase table (persisted across cold starts); falls back to in-memory state files when the table is empty. Signals are sorted newest-first.

Query paramDescription
levelFilter by API risk level: green | yellow | orange | red
layerFilter by signal layer: 14
signal_idExact signal identifier, e.g. deprecation_announced
api_nameCase-insensitive substring match on API name
sinceISO date — only signals detected after this date
untilISO date — only signals detected before this date
GET /api/v1/signals
curl — all orange/red signals
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  "https://parrad.app/api/v1/signals?level=orange"
Response
{
  "ok": true,
  "data": {
    "signals": [
      {
        "signal_id":   "deprecation_announced",
        "layer":       1,
        "weight":      5,
        "excerpt":     "v1 Lists API sunset extended to April 30, 2026",
        "source_url":  "https://developers.hubspot.com/changelog/...",
        "signal_date": "2025-05-10T09:00:00.000Z",
        "detected_at": "2025-05-10T09:00:00.000Z",
        "api_id":      "hubspot-crm-v3",
        "api_name":    "HubSpot CRM API v3",
        "vendor":      "HubSpot",
        "level":       "orange"
      }
    ],
    "total": 1
  },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:00:00.000Z" }
}

GET /alerts/unread

Shortcut for unread alerts only. Returns the same alert shape as GET /alerts but pre-filtered to read: false and returned as a flat array (not paginated). Enriched with situation, action, and urgency from AI insights.

GET /api/v1/alerts/unread
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/alerts/unread
Response — array of unread alert objects
{
  "ok": true,
  "data": [
    {
      "id":             "m8k2a9xz",
      "api_id":         "hubspot-crm-v3",
      "api_name":       "HubSpot CRM API v3",
      "previous_level": "yellow",
      "new_level":      "orange",
      "score":          52,
      "timestamp":      "2025-05-17T06:01:00.000Z",
      "read":           false,
      "situation":     "HubSpot v1 Lists API sunset announced for April 2026.",
      "action":         "Migrate to v3 Lists API before Q1 2026.",
      "urgency":        "medium",
      "changelog_url":  "https://developers.hubspot.com/changelog",
      "docs_url":       null
    }
  ],
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:00:00.000Z" }
}

GET /categories

Returns all API categories with their tool counts. Includes categories defined in app_config even if no APIs are currently assigned to them (count: 0).

GET /api/v1/categories
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/categories
Response
{
  "ok": true,
  "data": {
    "categories": [
      { "name": "AI",  "count": 3 },
      { "name": "CRM", "count": 1 },
      { "name": "Data pipeline", "count": 0 }
    ]
  },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:00:00.000Z" }
}
count: 0 entries are categories configured in app_config with no tools currently assigned. They appear so callers can render a complete category filter UI.

GET /downstream

Returns the full dependency graph: raw edges (directed, confidence-scored) and all propagation impacts. Filterable by source, target, confidence tier, dependency type, and date range.

Query paramDescription
srcFilter edges by source API id
targetFilter edges by target (affected) API id
confidence_tierhigh (≥0.8) | medium | low
dependency_typee.g. integration, data, auth
since / untilISO date — filter by discovered_at
GET /api/v1/downstream
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/downstream
Response
{
  "ok": true,
  "data": {
    "total_edges": 56,
    "last_run":    "2026-05-29T10:40:22.000Z",
    "edges": [
      {
        "source_api_id":  "cargo-api",
        "affected_api_id":"github-api",
        "dependency_type":"integration",
        "confidence":     1,
        "urgency":        "medium",
        "evidence":       "Cargo workspace: 1 workflow via github connector",
        "discovered_at":  "2026-05-29T10:40:22.000Z",
        "last_checked":   "2026-05-29T10:40:22.000Z"
      }
    ],
    "impacts": [ /* see GET /downstream/risks for shape */ ]
  },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:00:00.000Z" }
}

GET /downstream/risks

Risk propagation impacts only. Each impact describes how a risk signal in one API cascades to a dependent API. The prop_id field (root_api:dependent_api:kind) is used to call the /actions/fix endpoint.

GET /api/v1/downstream/risks
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/downstream/risks
Response
{
  "ok": true,
  "data": {
    "total":    6,
    "last_run": "2026-05-29T10:40:22.000Z",
    "impacts": [
      {
        "kind":         "risk",
        "root_api":     "linkedin-api",
        "root_api_name":"LinkedIn API",
        "dependent_api":"cargo-api",
        "urgency":      "high",
        "impact":       "Breaking field change in LinkedIn enrichProfile response",
        "evidence":     "hasDarkUgc field added — affects 3 Cargo workflows",
        "generated_at": "2026-05-29T10:40:22.000Z",
        "keys_needed":  ["CARGO_API_KEY"],
        "keys_present": ["CARGO_API_KEY"],
        "keys_missing": []
      }
    ]
  },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:00:00.000Z" }
}
Build the prop_id path param for action endpoints as {root_api}:{dependent_api}:{kind}, e.g. linkedin-api:cargo-api:risk.

GET /downstream/capabilities

Capability propagation impacts — same shape as GET /downstream/risks but kind: "opportunity" (enum rename in progress; use /opportunities as an alias URL during transition). Use prop_id to call /actions/build.

GET /api/v1/downstream/capabilities
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/downstream/capabilities
Response
{ "ok": true, "data": { "impacts": [ /* same shape as /risks, kind="opportunity" */ ], "total": 2, "last_run": "..." }, "meta": { /* ... */ } }

GET /downstream/graph

Full dependency graph with risk levels attached to each node. Useful for building visual graph UIs.

GET /api/v1/downstream/graph
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/downstream/graph
Response
{
  "ok": true,
  "data": {
    "last_run": "2026-05-29T10:40:22.000Z",
    "nodes": [
      {
        "id":         "linkedin-api",
        "name":       "LinkedIn API",
        "vendor":     "LinkedIn",
        "risk_level": "orange",
        "risk_score": 62,
        "opp_level":  null
      }
    ],
    "edges":   [ /* same shape as GET /downstream */ ],
    "impacts": [ /* same shape as GET /downstream/risks */ ]
  },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:00:00.000Z" }
}

POST /downstream/risks/{prop_id}/actions/fix

Generates a step-by-step Cargo fix plan for a risk propagation. First call streams a text/event-stream response as the plan is built by Claude. Subsequent calls within the cache TTL return a plain JSON response with from_cache: true — no streaming.

prop_id format: {root_api}:{dependent_api}:{kind} — URL-encode the colons if needed.

POST /api/v1/downstream/risks/{prop_id}/actions/fix
curl — first call (streaming)
curl -sN -X POST -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/downstream/risks/linkedin-api:cargo-api:risk/actions/fix
SSE event stream
Content-Type: text/event-stream

data: {"type":"token","text":"plan text tokens..."}

data: {"type":"token","text":"...more tokens..."}

... more token events ...

data: {
  "type": "done",
  "plan": {
    "situation": "...",
    "action":    "...",
    "steps": [
      {
        "title":        "Inspect current releases",
        "target":       "Workflows ...",
        "description":  "...",
        "cli_commands": [{ "cmd": "cargo-ai ...", "description": "..." }]
      }
    ],
    "cli_commands": [{ "cmd": "cargo-ai whoami", "description": "Verify auth" }]
  },
  "_hash": "e7108da8a233"
}
Cached response — subsequent call (plain JSON, not SSE)
{
  "ok":         true,
  "from_cache": true,
  "plan":       { /* same plan object as the done event above */ },
  "_hash":      "e7108da8a233"
}
Check GET /downstream/risks/{prop_id}/actions first to inspect has_cached_plan before streaming. Plan steps contain cli_commands that are ready to paste into a terminal or Cargo code node.

Check action cache status

GET /api/v1/downstream/risks/{prop_id}/actions
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/downstream/risks/linkedin-api:cargo-api:risk/actions
Response
{
  "ok": true,
  "data": {
    "prop_id":           "linkedin-api:cargo-api:risk",
    "available_actions": ["fix"],
    "has_cached_plan":   true,
    "cached_at":         "2026-05-23T10:00:00.000Z"
  },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:00:00.000Z" }
}

POST /downstream/capabilities/{prop_id}/actions/build

Same streaming behavior as /actions/fix but for capability propagations — generates a Cargo workflow build plan. A companion GET .../actions endpoint reports has_cached_plan.

POST /api/v1/downstream/capabilities/{prop_id}/actions/build
curl
curl -sN -X POST -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/downstream/capabilities/cargo-api:hubspot-crm-v3:opportunity/actions/build
SSE stream → same event shape as /actions/fix
data: {"type":"token","text":"..."}
...
data: {"type":"done","plan":{...},"_hash":"..."}

GET /webhooks

Manage outbound webhooks. parrad calls all enabled webhooks when a new alert is created. Max 10 webhooks per workspace. Each webhook subscribes to ["alert.created"] by default.

GET /api/v1/webhooks
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/webhooks
Response
{
  "ok": true,
  "data": {
    "webhooks": [
      {
        "id":                "wh-a1b2c3d4",
        "url":               "https://hooks.example.com/parrad",
        "label":             "My Slack webhook",
        "enabled":           true,
        "created_at":        "2026-05-01T09:00:00.000Z",
        "events_subscribed": ["alert.created"]
      }
    ]
  },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:00:00.000Z" }
}

Register a webhook

POST /api/v1/webhooks
curl
curl -X POST -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://hooks.example.com/parrad", "label": "My Slack webhook" }' \
  https://parrad.app/api/v1/webhooks
Response — 201 Created
{ "ok": true, "data": { "webhook": { /* full webhook object */ } }, "meta": { /* ... */ } }
URL must start with https://. Returns 400 if missing. label is optional; defaults to the URL.

Remove a webhook

DELETE /api/v1/webhooks/{id}
curl
curl -X DELETE -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/webhooks/wh-a1b2c3d4
Response
{ "ok": true, "data": { "deleted": "wh-a1b2c3d4" }, "meta": { /* ... */ } }
Returns 404 if the webhook id is not found.

GET /integrations

List and manage external service integrations (Cargo, Anthropic, Firecrawl). Only cargo is configurable via the API; the others are read from server environment variables.

GET /api/v1/integrations
curl
curl -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/integrations
Response
{
  "ok": true,
  "data": {
    "integrations": [
      { "provider": "cargo",     "connected": true, "connected_at": "2026-05-01T09:00:00.000Z" },
      { "provider": "anthropic", "connected": true },
      { "provider": "firecrawl", "connected": true }
    ]
  },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:00:00.000Z" }
}

Connect Cargo

POST /api/v1/integrations/cargo
curl
curl -X POST -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "api_key": "cargo_..." }' \
  https://parrad.app/api/v1/integrations/cargo
Response
{ "ok": true, "data": { "provider": "cargo", "connected": true }, "meta": { /* ... */ } }
The key is validated live against the Cargo API before being stored. Returns 422 if validation fails.

Disconnect Cargo

DELETE /api/v1/integrations/cargo
curl
curl -X DELETE -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/integrations/cargo
Response
{ "ok": true, "data": { "provider": "cargo", "connected": false }, "meta": { /* ... */ } }

POST /cache/invalidate

Clears the signal collection cache for a specific API (or all APIs). The next run will collect fresh signals regardless of the normal deduplication window. Useful after a manual source update or when a known change wasn't picked up.

POST /api/v1/cache/invalidate
curl — single API
curl -X POST -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "api_id": "hubspot-crm-v3" }' \
  https://parrad.app/api/v1/cache/invalidate
curl — all APIs
-d '{ "api_id": "all" }'
Response
{
  "ok": true,
  "data": {
    "invalidated": "hubspot-crm-v3",
    "message":     "signal cache cleared for hubspot-crm-v3 — next run will collect fresh"
  },
  "meta": { "api_version": "1.0", "timestamp": "2026-05-23T10:00:00.000Z" }
}

Integrate with Cargo

Use Cargo to pull parrad alerts into enrichment workflows — for example, routing high-risk API alerts to your CRM, triggering Slack messages, or auto-creating Jira tickets.

Poll alerts and filter unread

The simplest integration: poll GET /api/v1/alerts on a schedule and push the payload into a Cargo HTTP source or a Code node.

HTTP source / Code node — fetch unread alerts
curl -s -H "X-PARRAD-Key: $PARRAD_API_KEY" \
  https://parrad.app/api/v1/alerts \
  | jq '.data.alerts[] | select(.read == false)'
Example output (one unread alert)
{
  "id":             "m8k2a9xz",
  "api_id":         "hubspot-crm-v3",
  "api_name":       "HubSpot CRM API v3",
  "previous_level": "yellow",
  "new_level":      "orange",
  "score":          52,
  "timestamp":      "2025-05-17T06:01:00.000Z",
  "top_signal":     "deprecation_announced"
}
Cargo JS code node — push to HubSpot deal
// Cargo code node: for each unread alert, create a HubSpot note
const resp = await fetch('https://parrad.app/api/v1/alerts', {
  headers: { 'X-PARRAD-Key': process.env.parrad_API_KEY }
})
const { data } = await resp.json()
const unread = data.alerts.filter(a => !a.read)

return unread.map(alert => ({
  subject:  `parrad ${alert.new_level.toUpperCase()}: ${alert.api_name}`,
  body:     `Score moved ${alert.previous_level} → ${alert.new_level} (${alert.score}/100). Signal: ${alert.top_signal}`,
  api_id:   alert.api_id,
  severity: alert.new_level,
}))

Integrate with n8n

Use n8n to build automated workflows triggered by parrad data — Slack notifications, Jira issues, PagerDuty incidents, or CRM updates whenever a risk score escalates.

Webhook → trigger a parrad run

Add an HTTP Request node to trigger a full scan, then poll for results and route on severity.

n8n HTTP Request node — POST /api/v1/runs
{
  "method":  "POST",
  "url":     "https://parrad.app/api/v1/runs",
  "headers": {
    "X-PARRAD-Key":     "{{ $env.parrad_API_KEY }}",
    "Content-Type":    "application/json"
  },
  "body": {
    "api_ids": ["hubspot-crm-v3"]
  }
}
n8n HTTP Request node — poll /api/v1/alerts
{
  "method":  "GET",
  "url":     "https://parrad.app/api/v1/alerts",
  "headers": { "X-PARRAD-Key": "{{ $env.parrad_API_KEY }}" }
}
n8n IF node — route on severity
// Condition: alert is unread AND new_level is orange or red
{{ $json.data.alerts
    .filter(a => !a.read && ['orange','red'].includes(a.new_level))
    .length > 0 }}
Full workflow sketch
Schedule Trigger (daily 08:00)
  → HTTP Request: POST /api/v1/runs         // fire scan (api_ids required)
  → Wait: 120s                              // wait for run to complete
  → HTTP Request: GET /api/v1/alerts        // fetch results
  → Code node: filter unread orange + red
  → IF: any alerts found?
      YES → Slack message + Jira issue
      NO  → no-op