Developers
DEVELOPERS · REST API

REST API reference

Base URL api.signalraven.ai/api/v1 — versioned, read-only, JSON, scoped to your workspace. Every call needs an OAuth bearer token.

Authentication

Create an API key in the portal (app.signalraven.ai → Settings → API keys), then use the OAuth client_credentialsgrant to get a bearer token (≈30 min lifetime), and call the API with it.

# 1. Get a token
curl -s -X POST https://auth.signalraven.ai/oauth2/token \
  -d grant_type=client_credentials \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET \
  -d 'scope=read:signals read:sources read:icp'

# 2. Call the API
curl -s https://api.signalraven.ai/api/v1/signals?limit=5 \
  -H "Authorization: Bearer eyJ…"

Scopes

ScopeGrants
read:signalsList + fetch signals
read:sourcesList monitored sources + metrics
read:watchlistList watchlist posts
read:icpRead the workspace ICP profile
read:intelligenceRead Account + Person Intelligence reports
read:usageRead workspace activity counts
read:destinationsRead where signals are delivered
write:intelligenceStart new Intelligence runs (spends credits)
write:destinationsCreate + change delivery destinations

A token only works on endpoints matching its scopes; anything else returns 403 insufficient_scope. Every response carries a _meta.mode of "live" or "sample" (see approved vs. sample data).

GET /signals

List signals, newest first.

ParamTypeDefaultNotes
limitint251–100
offsetint0pagination
typestringfilter by signal type
minStrengthintstrength ≥ this (0–10)
{
  "data": [
    {
      "id": "8af4c6c7-…",
      "type": "KEYWORD_SEARCH_REACTION",
      "strength": 9,
      "person": { "company": "Globex", "linkedinUrl": "https://…", "location": "New York, US" },
      "whyItMatters": "Publicly weighing a multi-CDN migration …",
      "suggestedOpener": "Saw your note on tail-latency …",
      "talkingPoints": ["Multi-CDN failover", "Live-event QoE"],
      "createdAt": "2026-01-01T00:00:00.000Z"
    }
  ],
  "total": 866,
  "limit": 25,
  "offset": 0,
  "_meta": { "mode": "live" }
}

GET /signals/{id}

Fetch one signal by id. Returns { data, _meta }, or 404 if not found.

GET /sources

List monitored sources with activity metrics. Query period = 7, 30 (default), or all (days). Each source includes type, linkedinUrl, displayName, isActive, strengthScore, signalCount, totalPosts, qualifiedPosts.

GET /watchlist

List watchlist posts (limit 1–100, offset). Each post: sourceId, url, author, preview, relevanceScore, lifecycleStatus, createdAt.

GET /icp

The workspace’s default ICP profile: minEmployees, maxEmployees, targetIndustries, targetTitles, targetPersonas, targetSeniority, additionalContext. 404 if none configured.

GET /intelligence

The Account and Person Intelligence reports run in your workspace, newest first (limit 1–100, offset, type of all | account | person, q free-text on the name). Every row carries id, type, slug, name, status and createdAt; account rows add disposition, industry, employeeCount and committeeSize, person rows add currentTitle, currentCompany and signalsCount. Fields belonging to the other family are null, so branch on type.

GET /intelligence/accounts/{id}

One Account Intelligence report: firmographics, disposition and dispositionReason, and the buyingCommittee array. 404if the report isn’t yours or doesn’t exist.

GET /intelligence/people/{id}

One Person Intelligence snapshot: profile, aiRead (classification + reasoning), notable (hiring, openToWork, recentJobChange), the signals it surfaced, and rawActivityCount. Raw activity is returned as a count only — the same thing the product shows a human.

POST /intelligence/accounts

Start an Account Intelligence run. Body: companyUrl. Returns 202 with { id, slug, status, cached:false } when a run was queued, or 200 with cached:true when a report from the last 30 days already covered it — in which case nothing is spent. Poll GET /intelligence/accounts/{id} until status is ready.

POST /intelligence/people

Same shape for a person. Body: personUrl, optionally sourceCompanyReportId to keep the lineage from an account report.

GET /usage

Workspace activity counts: signalsThisMonth, signalsLastMonth, sourcesActive, postsMonitored, postsThisMonth, discoveryRunsToDate, plus systemStatus. Counts only — no billing figures.

GET /destinations

Where your signals are delivered, with type, name, isActive, health, consecutiveFailures and routing. Connection settings are never returned — a read scope should not hand back webhook URLs or tokens.

POST / PATCH / DELETE /destinations

Create (destinationType, name, config, optional routing), update (name, isActive, routing), or remove one. DELETE deactivates rather than hard-deleting, so delivery history survives. Creating returns 409when the plan’s destination limit is reached.

Errors

StatuserrorWhen
401unauthorizedmissing / invalid / expired token
403insufficient_scopetoken lacks the required scope
403forbiddentoken isn’t workspace-scoped
403client_deniedclient denied by SignalRaven
404not_foundresource doesn’t exist
403workspace_inactivewrites need an active workspace
409plan_destination_cap_reachedat the plan’s destination limit