Query Signal Detections

Paginate the signal detections published for your workspace.

Signal detection endpoints expose published signal detections for the authenticated workspace. This includes detections produced by keyword runs and any manually added detections. All items are workspace-scoped.

This endpoint uses cursor pagination instead of the standard offset envelope. Pass the next_cursor from each response as the cursor field of the next request. To get a total count, call Count signal detections with the same filters.

POST/api/v2/workspace/signals/query

Request body

FieldTypeDescription
cursorstringOpaque pagination cursor. Pass the next_cursor from the previous response to fetch the next page. Omit for the first page.
limitintegerNumber of items to return per page (1–100). Defaults to 25.
signal_start_datestring (date-time)Return detections whose signal date is on or after this timestamp (ISO 8601 with UTC offset).
signal_end_datestring (date-time)Return detections whose signal date is on or before this timestamp (ISO 8601 with UTC offset).
detection_start_datestring (date-time)Return detections that were first seen on or after this timestamp (ISO 8601 with UTC offset).
detection_end_datestring (date-time)Return detections that were first seen on or before this timestamp (ISO 8601 with UTC offset).
agent_idintegerReturn detections produced by a specific agent. Pass the numeric agent_id returned on each detection.
signal_run_idintegerReturn detections from a specific run. Pass the numeric run id from the signal-runs endpoint. When no date filter is supplied, results default to the 90 days before the run's creation.
company_idintegerReturn detections for a specific company on your top-account list. Pass the numeric company_id from your target account list. This ID is specific to your environment. Taken at face value — a company that exists in your workspace but is not on your top-account list returns an empty page, not a 404.
company_domainstring[]Return detections for companies on your top-account list matching these domains. Native JSON array for OR filtering across multiple companies (e.g. ["acme.com", "widgets.io"]). Returns 404 when none of the supplied values resolve to a known company on your top-account list.
company_linkedin_handlestring[]Return detections for companies on your top-account list matching these LinkedIn handles. Native JSON array (e.g. ["acme-corp", "widgets-io"]). Returns 404 when none of the supplied values resolve to a known company on your top-account list.
company_linkedin_urlstring[]Return detections for companies on your top-account list matching these LinkedIn URLs. Native JSON array. Returns 404 when none of the supplied values resolve to a known company on your top-account list.

Results are sorted by detection date descending (most recent first).

Example request

curl -X POST "https://api.getsillage.com/api/v2/workspace/signals/query" \
  -H "Authorization: Bearer $SILLAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"limit":25}'
const response = await fetch('https://api.getsillage.com/api/v2/workspace/signals/query', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SILLAGE_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ limit: 25 }),
})

const result = await response.json()
import { request } from 'node:https'

const body = JSON.stringify({ limit: 25 })

const req = request(
  new URL('https://api.getsillage.com/api/v2/workspace/signals/query'),
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.SILLAGE_API_KEY}`,
      'Content-Type': 'application/json',
      'Content-Length': Buffer.byteLength(body),
    },
  },
  (res) => {
    let data = ''
    res.on('data', (chunk) => {
      data += chunk
    })
    res.on('end', () => {
      console.log(JSON.parse(data))
    })
  },
)

req.write(body)
req.end()
import os
import requests

response = requests.post(
    "https://api.getsillage.com/api/v2/workspace/signals/query",
    json={"limit": 25},
    headers={"Authorization": f"Bearer {os.environ['SILLAGE_API_KEY']}"},
    timeout=30,
)
response.raise_for_status()
result = response.json()

Example response

200 OK

{
  "data": [
    {
      "id": 101,
      "signal_type": "keywordDetection",
      "data": {
        "content_id": 5100,
        "author": {
          "type": "profile",
          "full_name": "Jane Doe",
          "headline": "VP Product at Acme",
          "linkedin_url": "https://linkedin.com/in/janedoe"
        },
        "keywords_found": ["AI"]
      },
      "detected_at": "2026-06-01T10:00:00.000Z",
      "signal_date": null,
      "lead_id": 456,
      "company_id": 789,
      "agent_id": 12,
      "source_url": "https://www.linkedin.com/feed/update/urn:li:activity:7100000000000000000/",
      "author": {
        "full_name": "Jane Doe",
        "headline": "VP Product at Acme",
        "linkedin_url": "https://linkedin.com/in/janedoe"
      },
      "excerpt": "Excited to share how AI is transforming our product roadmap this quarter."
    }
  ],
  "meta": {
    "next_cursor": "eyJkZXRlY3RlZEF0IjoiMjAyNi0wNi0wMVQxMDowMDowMC4wMDBaIiwiaWQiOjEwMX0=",
    "has_more": true
  }
}

To fetch the next page, pass next_cursor as cursor in the body of the subsequent request. When has_more is false, you have reached the last page and next_cursor will be null.

For keyword detections, data.content_id is the ID of the content the keyword matched in (usable with the Contents endpoints), and data.author describes who published it, discriminated by type: "profile" (a person — full_name, headline, linkedin_url) or "company" (a company page — company_name, linkedin_url). author is null when the publisher is unknown.

The top-level source_url, author, and excerpt fields are convenience shortcuts to the most relevant link, person, and text extract already present somewhere in data (or, for a few signal types, only reachable outside the current data shape) — read them directly instead of digging through the type-specific data payload. The top-level author is not always the same object as data.author: for detections where the source content and the person are separate concepts (e.g. a comment left by someone else on a tracked post), the top-level author follows a fixed per-type priority so it consistently points at the person behind the source content.

Detection object fields (V2SignalDetection)

FieldTypeDescription
idintegerID of the detection. Specific to your environment.
signal_typestring | nullSignal type, e.g. keywordDetection.
dataany | nullDetection payload; the shape depends on signal_type (see the sections below).
detected_atstring (date-time) | nullWhen the detection was created.
signal_datestring (date-time) | nullDate of the underlying signal event.
lead_idinteger | nullID of the associated workspace lead. For detections without their own lead (e.g. keyword matches), this is the content author's lead when known. Null when the author is unknown.
company_idinteger | nullID of the associated workspace company.
agent_idinteger | nullID of the agent that produced the detection.
source_urlstring | nullLink to the source content behind the detection (the post, interaction, or job posting). null when the detection has no public external link. Presence depends on signal_type.
authorobject | nullThe person behind the source content, when known: full_name, headline, linkedin_url (each nullable). null for company-level or authorless detections.
excerptstring | nullA short text excerpt from the source content (post text, comment, or job description). null when no text is available.

Watchlist comment detections

Detections produced by watchlist agents (signal types ending in InboundComment or OutboundComment, e.g. competitorInboundComment) use a dedicated data shape: the comment on one side (interaction), the post it was left on the other (post). Each side carries a content_id — the ID of the matching content in your workspace, usable with the Contents endpoints. content_id is null when no matching content exists in your workspace.

{
  "interaction": {
    "type": "comment",
    "content_id": 5001,
    "author": {
      "full_name": "Jane Doe",
      "linkedin_url": "https://linkedin.com/in/janedoe",
      "headline": "VP Product at Acme"
    }
  },
  "post": {
    "content_id": 5002,
    "author": {
      "full_name": "John Smith",
      "linkedin_url": "https://linkedin.com/in/johnsmith",
      "headline": "Head of Sales at Widgets"
    }
  }
}

Errors

StatusMeaning
400Invalid request body.
401The API key is missing or invalid.
403The workspace does not have access to this resource.
404Returned when string company identifier(s) (company_domain, company_linkedin_handle, or company_linkedin_url) were supplied and none resolved to a known company in your workspace. company_id is never validated and never triggers a 404.
429Rate limit exceeded.
500An unexpected server error occurred.

Error responses use RFC 9457 problem documents with Content-Type: application/problem+json:

{
  "type": "https://docs.getsillage.com/errors/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "No matching company found for the supplied identifiers.",
  "instance": "/api/v2/workspace/signals/query"
}

On this page