signal.clustered
A signal was clustered into a theme
data
| signalId | string | |
| themeId | string |
Every state change you act on is appended to one durable per-org log. Webhooks and 2 server-sent event streams carry it.
Each event is an envelope { id, type, seq, createdAt, data }. seq is the row's creation time in milliseconds, so it increases within an org, is not dense and can be fractional: order by it, dedupe by id.
A delivery is a POST of the envelope with Belora-Event, Belora-Delivery and Belora-Signature: t=<unix seconds>,v1=<hex>. v1 is the lowercase hex HMAC-SHA256, keyed with the endpoint secret, of t + "." + raw body. Reject a t more than 300 seconds from your clock.
A signal was clustered into a theme
| signalId | string | |
| themeId | string |
A decision was opened and awaits a person
| decisionId | string | |
| themeId | string | |
| question | string | |
| options | string[] |
A decision received its verdict
| decisionId | string | |
| themeId | string | |
| chosenIndex | integer | |
| option | string | The label of the chosen option |
| rationale | string | |
| decidedBy | string | The person who decided: an auth id for the console and API doors, a Slack user id for Slack, Belora for an auto-applied decision |
| door | "api" | "console" | "slack" | Which surface carried the verdict |
A job was queued for a runner to claim
| jobId | string | |
| repo | string | The repository the job targets, as owner/name |
| decisionId | string | null | The decision the job implements; null when it follows a theme |
| linkedJobIds | string[] | Jobs already queued for the same decision, claimed together across repositories |
A job closed with a recap; the grade is what its pull request said at that moment
| jobId | string | |
| themeId | string | null | |
| grade | object | null | Null when the closer is not an executor kind that is graded |
A job failed for good: the retry cap, a closing that never finished, or a claim too large to run
| jobId | string | |
| themeId | string | null | |
| reason | string |
A theme shipped: every pull request it recorded is merged
| themeId | string | |
| title | string | |
| prs | object[] | |
| prs[].repo | string | owner/name |
| prs[].number | integer | |
| prs[].url | string | |
| changed | string[] | One line per changed file from the recaps of the theme's jobs, as path: what |
| recapSynthesized | boolean | Belora wrote at least one of those recaps from the diff because the executor sent none |
A theme was merged into another and archived
| themeId | string | The theme that was absorbed |
| intoThemeId | string |
An unanswered decision, or the question of a paused job, was nudged to its owner or re-routed to the org owner
| decisionId | string | null | The decision; null when a paused job's question escalated |
| jobId | string | null | The paused job; null when a decision escalated |
| step | "nudge" | "reroute" | |
| to | string | null | The person addressed; null when a nudge has nobody with a verified identity to reach |
A job lost its lease or stalled in closing, and was requeued or failed
| jobId | string | |
| outcome | "requeued" | "failed" | |
| reason | "lease" | "closing" | |
| attempts | integer | |
| personId | string | null | The person whose API key held the job; null for the runner |
Server-sent events of the org's event log, resumable with Last-Event-ID; a stream lasts at most 280 seconds, so reconnect
Server-sent events of one session's messages and commit, resumable with Last-Event-ID; a stream lasts at most 280 seconds, so reconnect