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 (except type, which the count endpoint does not accept).

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). Defaults to 90 days before now. Cannot go further back than 180 days — an older value is clamped to that limit.
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.
typestring[]Return only detections of these signal types. Native JSON array of at least one value from: keywordDetection, newJob, recentlyPromoted, jobPostingKeywordDetection, competitorInboundComment, competitorOutboundComment, partnerInboundComment, partnerOutboundComment, customerInboundComment, customerOutboundComment, influencerInboundComment, influencerOutboundComment, championInboundComment, championOutboundComment, championJobChange, linkedinComment, linkedinReaction, leadLikedOwnContent, leadCommentedOwnContent, leadLikedCompetitorContent, leadCommentedCompetitorContent, jobPosting, jobPostingInsight, jobPostingHiringManager, deepSearch — an unknown value is rejected with a 400. Pass ["championJobChange"] to isolate champion job changes; that type covers every champion career move, including departures where the new company is not known yet. Pass ["leadLikedCompetitorContent", "leadCommentedCompetitorContent"] to isolate the people who engaged with a tracked competitor's own posts — see Content engagement detections below. Not supported by the count endpoint.
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.
lead_idintegerReturn only signals directly linked to this lead. Pass the lead_id carried by signal items; resolve details via Get lead. Only matches signals directly linked to the lead — not signals attributed through fallback matching.
countriesstring[]Return only signals whose linked lead is located in one of these countries. Native JSON array of country names from GET /workspace-leads/locations. Combined with cities. Only matches signals directly linked to a lead — company-only signals are excluded.
citiesobject[]Return only signals whose linked lead is located in one of these cities. Each entry is { "name": "Paris", "country": "France" }. Use the values from GET /workspace-leads/locations. Only matches signals directly linked to a lead.

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}'

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, championJobChange, or one of the four partner types listed below.
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.

Interaction agent comment detections

Detections produced by interaction 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"
    }
  }
}

Competitor activity detections

Detections for linkedinComment and linkedinReaction use a dedicated data shape: the comment or reaction on one side (interaction), the post it was left on the other (post). interaction.author is the external person who left the comment or reaction; post.author is the person you are tracking. interaction.content_id is the ID of the matching content in your workspace — pass it to the Contents endpoints to fetch the full post. It is null when no matching content exists in your workspace. Unlike the interaction-agent shape above, post does not carry a content_id.

{
  "interaction": {
    "type": "comment",
    "url": "https://linkedin.com/feed/update/urn:li:activity:7100000000000000001/",
    "content_id": 5101,
    "author": {
      "full_name": "Jane Doe",
      "first_name": "Jane",
      "last_name": "Doe",
      "linkedin_url": "https://linkedin.com/in/janedoe",
      "company_name": "Acme",
      "role": "VP Product",
      "comment_text": "Great point, we're seeing the same shift on our side."
    }
  },
  "post": {
    "url": "https://linkedin.com/feed/update/urn:li:activity:7100000000000000000/",
    "extract": "Excited to share how AI is transforming our product roadmap this quarter.",
    "author": {
      "full_name": "John Smith",
      "first_name": "John",
      "last_name": "Smith",
      "linkedin_url": "https://linkedin.com/in/johnsmith",
      "company_name": "Widgets Inc",
      "role": "Head of Sales"
    }
  }
}

Content engagement detections

Detections for leadLikedOwnContent (the lead liked a post published by your own company), leadCommentedOwnContent (the lead commented on a post published by your own company), leadLikedCompetitorContent and leadCommentedCompetitorContent use the same interaction / post shape as competitor activity detections above, mirrored: post is the post published by the person you are tracking, and interaction is the reaction or comment left on it by an external person — the one now surfacing as the lead. Unlike competitor activity detections, neither side carries a content_id here.

{
  "interaction": {
    "type": "comment",
    "url": "https://linkedin.com/feed/update/urn:li:activity:7100000000000000003/",
    "author": {
      "full_name": "Priya Shah",
      "first_name": "Priya",
      "last_name": "Shah",
      "linkedin_url": "https://linkedin.com/in/priyashah",
      "company_name": "Globex",
      "role": "Director of Sales",
      "comment_text": "This matches what we're seeing too."
    }
  },
  "post": {
    "url": "https://linkedin.com/feed/update/urn:li:activity:7100000000000000000/",
    "extract": "Excited to share how AI is transforming our product roadmap this quarter.",
    "author": {
      "full_name": "John Smith",
      "first_name": "John",
      "last_name": "Smith",
      "linkedin_url": "https://linkedin.com/in/johnsmith",
      "company_name": "Widgets Inc",
      "role": "Head of Sales"
    }
  }
}

These are the people who engaged with a tracked competitor's own posts. lead_discovery and all always produce both these and the competitor activity detections above from the same run — there is no way to get only one. The two carry different labels: this one is content_engagement, and the one above, for the people the competitor's team engaged with, is competitor_activity. The agent producing both always reads back as type: "competitor" (see Create agent). Filter on agent_id (see the Request body above) to get every result from one agent regardless of which label it carries.

Partner detections

Four signal types are specific to partner agents: partnerStaffComment and partnerStaffReaction (a comment or reaction left by a person on the partner's side), and leadLikedPartnerContent and leadCommentedPartnerContent (a person who liked or commented on the partner's own posts). You can read them on detections returned for an agent, and they follow the same interaction / post shape as the competitor detections above. The type filter does not accept these four values.

Job-posting keyword detections

Detections for jobPostingKeywordDetection use a dedicated data shape: the tracked keywords that matched (keywords_found) and the job posting they matched in (posting), with its title and a job_url you can open directly.

{
  "keywords_found": ["Senior Engineer", "Remote"],
  "posting": {
    "title": "Senior Backend Engineer",
    "job_url": "https://www.linkedin.com/jobs/view/1234567890"
  }
}

Champion career-move detections

Detections for championJobChange use a dedicated data shape describing a tracked champion's career move: change_type describes the kind of move, previous_position is the role they are leaving (role, company_name, linkedin_url), and new_position is the role they are moving into (role, company_name, start_date, linkedin_url). As noted above, this type covers every champion career move, including departures where the new company is not known yet — in that case new_position fields are null, as in the example below.

{
  "change_type": "left_company",
  "previous_position": {
    "role": "VP Product",
    "company_name": "Acme",
    "linkedin_url": "https://linkedin.com/company/acme"
  },
  "new_position": {
    "role": null,
    "company_name": null,
    "start_date": null,
    "linkedin_url": null
  }
}

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