---
title: "One signal by public id"
method: GET
path: "/api/v1/organizations/{organization_id}/signals/{id}"
tags: ["organizations"]
---

# One signal by public id

`GET /api/v1/organizations/{organization_id}/signals/{id}`

## Request

- Base URL: `https://composal.com` (the document declares no server; this is the origin it was published from)
- URL: `https://composal.com/api/v1/organizations/{organization_id}/signals/{id}`
- Auth: HTTP bearer

## Path parameters

- `organization_id` string, required
- `id` string, required

## Response `200`

The signal, with its additive resource envelopes.

- OpsSignalEnvelope
  - `signal` OpsSignal, required: One ops signal, as serialized by `OpsSignalResource` (frozen envelope, docs §3). Every id is a prefixed public id, never a raw bigint.
    - `id` string, required: The signal's own `sig_…` public id — also the paging cursor value.
    - `organization_id` string, required
    - `kind` string, required: A frozen taxonomy kind (§1) or an `app.custom.*` kind.
    - `severity` 'info' | 'warning' | 'error' | 'critical', required: Severity levels (docs §2, reusing `AlertRule::SEVERITIES`).
    - `subject_type` string, nullable, required: Polymorphic pointer: `AppDeployment`, `RuntimeInstance`, `AlertEvent`, `HostedApp`, or `SignalSubscription`.
    - `subject_id` string, nullable, required
    - `hosted_app_id` string, nullable, required
    - `app_service_id` string, nullable, required
    - `dedupe_key` string, nullable, required
    - `payload` object, required: Per-kind shape (docs §4), with the producer's `region`/`app`/ `service` attribution keys baked in when known. Frozen contract — deliberately not narrowed per kind here.
    - `summary` string, required: One-line, kind-composed human sentence for the feed row (docs §6d, e.g. `deploying → failed`, `47× fp_9c1e77a4b2d0f331`) — server-authored so consumers never re-derive it from `payload`.
    - `attribution` OpsSignalAttribution, required: The producer-stamped attribution (docs §3) lifted out of `payload` into a typed sibling (docs §6d), so consumers get the ids and names they need — e.g. vex-web building its own UI routes — without reading the per-kind JSON. Always present; each key is null when the producer did not know it.
      - `app` OpsSignalAttributionRef, required: One attribution target (hosted app or app service) — public id + display name.
        - `id` string, required
        - `name` string, required
      - `service` OpsSignalAttributionRef, required: One attribution target (hosted app or app service) — public id + display name.
        - `id` string, required
        - `name` string, required
      - `region` string, nullable, required
    - `source` OpsSignalSource, required: Provenance envelope (docs §6e), a sibling of the frozen `payload`. ALWAYS present, so a reader never branches on key presence: a Vex-produced signal renders `provider: "vex"` with null siblings.
      - `provider` 'vex' | 'sentry' | 'planetscale' | 'slack', required: Who produced the signal (docs §6e). `vex` is every signal the platform emits itself. Slack is the native Composal app; the others are signal-integration adapters.
      - `integration_id` string, nullable, required: The `sint_…` public id of the signal integration that ingested this signal. Null for native Vex and Slack sources; Slack uses its same-organization SlackInstallation subject.
      - `external_id` string, nullable, required: The provider's own id for the thing the signal is about (a Sentry issue id).
      - `external_url` string, nullable, required: Deep link to read the full event at the provider — Vex stores only a bounded reduction of it. Mirrored as `links.external`.
    - `recovery` OpsSignalRecovery: Recovered/resolved state folded from the `resolution` pairing (docs §6b/§6d). Present only when the pairing is satisfied — `recovered` for a runtime.health signal whose instance came back to running, `resolved` for an alert.fired whose event cleared. Absent otherwise.
      - `state` 'recovered' | 'resolved', required
      - `resolved_at` string, nullable, required
    - `occurred_at` string, required: ISO8601 domain event time — what the UI displays and `since` filters on.
    - `acked_at` string, nullable, required
    - `acked_by` object, nullable: Who acked the signal, read from `acked_by_user_id`. Always present so it reads as a pair with `acked_at`: null while unacked, and null for an ack recorded without a user (a bulk ack from a token-authed caller).
      - `id` string, required
      - `name` string, nullable, required
      - `email` string, nullable, required
      - `picture_url` string, nullable, required
    - `created_at` string, required: ISO8601 row insert time — retention math and debugging only.
    - `links` OpsSignalLinks, required: Canonical, relative API paths built from real routes (docs §6a). Sits alongside — never inside — the frozen `payload`. A key is present only when it resolves from the columns the signal carries; `self` always is. These are REST resource paths, not UI routes.
      - `self` string, required
      - `app` string
      - `service` string
      - `subject` string
      - `external` string: The provider deep link for an integration-sourced signal — the one absolute URL in this envelope, present only when the payload carries an http(s) `external_url` (docs §6e).
    - `resolution` OpsSignalResolution: Recovery pairing (docs §6b). Present only on signals with a pairing shape — `runtime.health` degraded/failed and `alert.fired`. An applicable-but-unrecovered signal renders just `{ "resolved": false }`; every other kind omits the key entirely.
      - `resolved` boolean, required
      - `resolved_by_signal` string, nullable: Public id (`sig_…`) of the signal that cleared the condition.
      - `resolved_at` string, nullable
    - `agent_runs` OpsSignalAgentRuns: Dispatched-run summary (docs §6c). Present only once a signal has at least one `SignalAgentRun`, of either origin.
      - `count` integer, required
      - `latest_status` 'dispatched' | 'running' | 'succeeded' | 'failed' | 'canceled', required
      - `solution_change` OpsSignalSolutionChange: The Change a linked agent run submitted as the fix (`link_signal_to_change`).
        - `id` string, required
        - `number` integer, required
        - `reference` string, required: Pre-formatted human id (`#42`).

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not found
- `422`: Unprocessable entity
- `502`: Bad gateway

---

[API](https://skmtc.dev/composal/apis/vex-api.md) - [All operations](https://skmtc.dev/composal/apis/vex-api/llms.txt) - [OpenAPI document](https://skmtc.dev/composal/apis/vex-api/revisions/74298a24e040?raw)
