> ## Documentation Index
> Fetch the complete documentation index at: https://docs.openpulse.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Get one signal

> A single signal by id, regardless of how old it is or where it falls in the list's pagination. Use this to resolve a signal a link names; use `GET /v1/results` to browse.



## OpenAPI

````yaml /api-reference/openapi.json get /v1/results/{id}
openapi: 3.1.0
info:
  title: Open Pulse API
  version: 1.0.0
  summary: Read buying signals, accounts and pipeline from an Open Pulse workspace.
  description: >-
    The programmatic surface of Open Pulse, authenticated with a workspace API
    key.


    **A key always acts as a member, never an admin.** Privilege comes from
    scopes, and

    the scope vocabulary has no way to express an admin action: a leaked key can
    read

    signals and move deals, and can never create a listener, change billing, or
    mint

    another key. Routes that declare no scope are unreachable by a key at all,
    which is

    why this document describes fewer operations than the server has handlers.


    For an assistant rather than a program, prefer the MCP server at `/mcp`: it
    is the

    same data with tool descriptions a model can act on, and it uses OAuth
    rather than a

    shared secret. See `/api.md`.


    **Webhooks**, described below, are the other direction: calls this
    workspace's server

    makes to yours. Verify each one by recomputing the HMAC and comparing it to
    the

    `x-openpulse-signature` header — an unverified delivery is a POST from
    anyone

    who finds the URL, not proof it came from Open Pulse.
  contact:
    email: support@openpulse.cloud
    url: https://openpulse.cloud/api.md
  license:
    name: Proprietary
    identifier: LicenseRef-OpenPulse-Terms
servers:
  - url: https://api.openpulse.cloud
    description: Production
security:
  - apiKey: []
externalDocs:
  url: https://openpulse.cloud/api.md
  description: Open Pulse for agents
paths:
  /v1/results/{id}:
    get:
      tags:
        - results
      summary: Get one signal
      description: >-
        A single signal by id, regardless of how old it is or where it falls in
        the list's pagination. Use this to resolve a signal a link names; use
        `GET /v1/results` to browse.
      operationId: getV1ResultsById
      parameters:
        - name: id
          in: path
          required: true
          description: The signal's id.
          schema:
            type: string
      responses:
        '200':
          description: >-
            The signal, outside every window and every page. Unlike the list,
            this applies no `postedAt` bound — a lead found yesterday but
            published five weeks ago is returned here and is not in the list
            that links to it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Signal'
        '401':
          description: The key is missing, malformed, expired or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: The key lacks the `signals:read` scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: >-
            No signal with this id, or it belongs to another workspace. The two
            are deliberately indistinguishable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: >-
            Too many requests from this key. `Retry-After` carries the seconds
            to wait.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - apiKey:
            - signals:read
components:
  schemas:
    Signal:
      type: object
      description: >-
        One public post, job posting or review a listener judged relevant, with
        its score and the reasoning behind it.


        **Most fields are optional and that is structural, not incidental.**
        Rows are stored by a pipeline that has gained stages over time, and a
        field added after a row was written is absent from it for ever: nothing
        is re-scored, because that is a model call per signal for no new
        information. Read defensively.
      required:
        - id
        - orgId
        - listenerId
        - source
        - url
        - title
        - excerpt
        - postedAt
        - relevanceScore
        - classification
        - rationale
        - matchedKeywords
        - firstSeenAt
      properties:
        id:
          type: string
          description: Stable identifier. What `GET /v1/results/{id}` takes.
        orgId:
          type: string
          description: The workspace this belongs to (`org_…`).
        listenerId:
          type: string
          description: The listener that found it.
        source:
          type: string
          enum:
            - x
            - linkedin
            - facebook
            - reddit
            - hackernews
            - linkedin_jobs
            - indeed
            - ashby
            - greenhouse
            - lever
            - google_reviews
            - g2
            - trustpilot
            - youtube
          description: The platform it came from.
        url:
          type: string
          description: Link to the post itself.
          format: uri
        title:
          type: string
          description: The post's title, or its first line where the platform has none.
        excerpt:
          type: string
          description: >-
            The post's text, as much of it as the source gave up. See
            `coverage`.
        author:
          type: string
          description: The poster's handle, where the source reports one.
        publishedAt:
          type: string
          format: date-time
          description: When the post was published, when the source reports it.
        postedAt:
          type: string
          format: date-time
          description: >-
            The sort and filter key: `publishedAt` when known, otherwise
            `firstSeenAt`.
        approximateDate:
          type: boolean
          description: >-
            True when `publishedAt` was derived from "3 months ago" rather than
            a real timestamp. Rendered as `~`, because implying a precision the
            source never gave is worse than admitting the approximation.
        dedupeKey:
          type: string
          description: >-
            Normalised URL, used to collapse one post reached through different
            links.
        titleKey:
          type: string
          description: Normalised title, used to collapse one post syndicated across URLs.
        coverage:
          type: string
          enum:
            - full_text
            - snippet
            - best_effort
            - enriched
          description: >-
            How much of the post the classifier actually read. `snippet` and
            `best_effort` cap the evidence component of the score; `enriched`
            means the page behind a snippet was fetched. Absent on rows written
            before the enrichment stage.
        relevanceScore:
          type: integer
          description: 0–100. The sort order of every list in the product.
          minimum: 0
          maximum: 100
        scoreVersion:
          type: integer
          enum:
            - 1
            - 2
            - 3
          description: >-
            Which rubric produced `relevanceScore`. No version is comparable to
            another and they sort against each other in the same column, which
            is why they are marked rather than silently mixed. Absent means 1.
        scoreBreakdown:
          $ref: '#/components/schemas/ScoreBreakdown'
        classification:
          type: string
          enum:
            - high_intent
            - evaluating
            - relevant
            - competitor
            - content
            - strong_match
            - worth_a_look
            - stretch
            - mismatch
            - stale
            - hiring_now
            - expanding
            - replacing
            - adjacent
            - switching
            - frustrated
            - comparing
            - praise
            - pain_confirmed
            - at_risk
            - healthy
            - noise
          description: >-
            The intent label. **The valid set depends on the listener's mode** —
            a `market` listener never produces `pain_confirmed` — so filtering
            by one from the wrong mode returns an empty page rather than an
            error. The retired `jobs` labels are still valid here: no listener
            produces them, and signals carrying them are still stored and still
            read.
        signalCategory:
          type: string
          enum:
            - direct_intent
            - hire_vs_buy
            - competitor_pain
            - growth
            - funding
            - procurement
            - construction
            - regulatory
            - operational_pain
            - tech_change
            - leadership_change
            - real_estate
            - new_business
            - cx_failure
            - missing_capability
            - incident
          description: >-
            What kind of event this is, as opposed to how good it is. Orthogonal
            to `classification`: a funding round and a support complaint can
            both be `high_intent`, and are not actioned by the same person.
        urgency:
          type: string
          enum:
            - low
            - medium
            - high
            - immediate
          description: How soon this needs acting on.
        purchaseIntent:
          type: string
          enum:
            - weak
            - moderate
            - strong
            - explicit
          description: How directly a wish to buy was expressed.
        confidence:
          type: string
          enum:
            - low
            - medium
            - high
          description: How much the model trusts its own read.
        inferredNeed:
          type: string
          description: 'What the subject probably needs: the product-shaped restatement.'
        recommendedAction:
          type: string
          description: The single next action worth taking.
        situation:
          type: string
          description: >-
            What is happening, stated without reference to the seller. Separate
            from `opportunity` so the leap between them is checkable by whoever
            reads the row.
        opportunity:
          type: string
          description: >-
            Why that situation matters to this seller, or absent when it does
            not.
        inferenceHops:
          type: integer
          enum:
            - 0
            - 1
          description: >-
            How far the reasoning reached: `0` they said it, `1` one step from
            what they said. There is no `2` — two stacked assumptions are
            indistinguishable from invention, and the prompt refuses it.
        rationale:
          type: string
          description: Why this was kept, in the model's words.
        suggestedAngle:
          type: string
          description: 'One line on why this person might care: the outreach angle.'
        matchedKeywords:
          type: array
          items:
            type: string
            description: A keyword from the listener's plan.
          description: Computed from the text, never taken from the model.
        themes:
          type: array
          items:
            type: string
            description: A short noun phrase.
          description: Two to four phrases naming what the post is about.
        rating:
          type: number
          description: >-
            Star rating 1–5 on a review source. Never the same number as
            `relevanceScore`.
          minimum: 1
          maximum: 5
        subjectOrg:
          $ref: '#/components/schemas/SubjectOrg'
        subjectName:
          type: string
          description: Display name of the place or rival, so a row reads without a join.
        accountKey:
          type: string
          description: >-
            The company this is about, namespaced (`d:acme.com` for a domain,
            `n:` for a name, `p:` for a place). **Absent is a real and common
            answer** — a thread that names no company has none.
        rivalKey:
          type: string
          description: Which rival this is about, on a `rivals` listener.
        placeKey:
          type: string
          description: Which place this review belongs to, on a `places` listener.
        policy:
          $ref: '#/components/schemas/SurfacingPolicy'
        passedBy:
          type: string
          enum:
            - keyword
            - rerank
          description: >-
            How this got past the on-topic gate. `rerank` means it shares no
            keyword and the cross-encoder judged it relevant anyway.
        rerankScore:
          type: number
          description: Cross-encoder relevance, when the rerank stage ran.
        foundByQuery:
          type: string
          description: Which planned query surfaced this, for per-query yield reporting.
        queryAttribution:
          type: string
          enum:
            - exact
            - inferred
          description: >-
            Whether `foundByQuery` is known or was worked out afterwards from
            token overlap. Recorded because per-query yield decides which
            queries get retired.
        assignedTo:
          type: object
          description: >-
            Who is working this lead. Set through `PATCH
            /v1/results/{id}/assign`.
          required:
            - userId
            - at
            - by
          properties:
            userId:
              type: string
              description: Clerk user id of the assignee.
            at:
              type: string
              format: date-time
              description: When it was assigned.
            by:
              type: string
              description: Clerk user id of whoever assigned it.
        feedback:
          type: object
          description: A person's verdict on the signal, which the classifier reads back.
          required:
            - verdict
            - at
          properties:
            verdict:
              type: string
              enum:
                - good
                - bad
              description: What they said.
            at:
              type: string
              format: date-time
              description: When they said it.
        readAt:
          type: string
          format: date-time
          description: >-
            When somebody in the workspace read it. **Workspace-wide rather than
            per member**: a signal is a lead somebody acts on once. Absent means
            unread.
        alertedAt:
          type: string
          format: date-time
          description: When an urgency alert was sent for this. Absent means never.
        firstSeenAt:
          type: string
          format: date-time
          description: >-
            When this workspace first saw it, which is not when it was
            published.
    Error:
      type: object
      description: What went wrong, in a shape every failing route shares.
      required:
        - message
      properties:
        message:
          type: string
          description: Human-readable explanation, safe to show a user.
        code:
          type: string
          description: >-
            Stable machine-readable error code, when the failure has one worth
            branching on. `website_unreadable` and `forbidden` are the two a
            caller acts on.
        detail:
          description: >-
            Validator output on a 400, as an array of issues naming the
            offending field. Diagnostic only: the shape comes from the
            validation library and is not part of the contract.
    ScoreBreakdown:
      type: object
      description: >-
        The seven components the score is the sum of, each capped at its own
        weight. Present only on `scoreVersion` 2 and 3. The components are the
        point rather than the total: "86" says nothing, "26 of 30 on intent, 8
        of 20 on fit" says a real buyer who is probably not yours.
      required:
        - purchaseIntent
        - fit
        - urgency
        - recency
        - commercialValue
        - evidenceQuality
        - multiSignal
      properties:
        purchaseIntent:
          type: integer
          description: 0–30.
          minimum: 0
          maximum: 30
        fit:
          type: integer
          description: 0–20.
          minimum: 0
          maximum: 20
        urgency:
          type: integer
          description: 0–15.
          minimum: 0
          maximum: 15
        recency:
          type: integer
          description: 0–10.
          minimum: 0
          maximum: 10
        commercialValue:
          type: integer
          description: 0–10.
          minimum: 0
          maximum: 10
        evidenceQuality:
          type: integer
          description: 0–10.
          minimum: 0
          maximum: 10
        multiSignal:
          type: integer
          description: 0–5.
          minimum: 0
          maximum: 5
    SubjectOrg:
      type: object
      description: >-
        The organisation the signal is about, which is not the poster. Every
        field is optional and absent is the common case: most conversations name
        no company, and inventing one from the platform host would file the
        whole corpus under one domain.
      properties:
        name:
          type: string
          description: The company named in the post.
        location:
          type: string
          description: Free text as written, which `region` on the account is parsed from.
        workMode:
          type: string
          enum:
            - any
            - remote
            - hybrid
            - onsite
          description: Where the work happens, on a posting.
        seniority:
          type: string
          description: Only meaningful on a posting.
        compensation:
          type: string
          description: As stated, when stated. Never normalised.
    SurfacingPolicy:
      type: object
      description: >-
        What the surfacing policy did when this signal was stored, for
        off-policy evaluation. Absent on every signal written before the holdout
        existed — and absent must read as "unknown", never as a propensity of 1.
      required:
        - propensity
        - explored
        - scoreAtDecision
        - barAtDecision
      properties:
        propensity:
          type: number
          description: P(shown) under the policy in force at the time. Never 0.
          exclusiveMinimum: 0
          maximum: 1
        explored:
          type: boolean
          description: True when the random holdout surfaced this rather than its score.
        scoreAtDecision:
          type: integer
          description: The score this was judged on.
        barAtDecision:
          type: integer
          description: The listener's bar at that moment.
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: >-
        A workspace API key, created in workspace settings and sent as
        `Authorization: Bearer op_live_…`. Session cookies are not accepted.
        Scopes: signals:read, accounts:read, listeners:read, listeners:analyse,
        pipeline:read, pipeline:write, collections:write, export:read.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.