# Webhooks

> Inbound provider webhooks ProductOS receives, and the planned shape of outbound event subscriptions.

Source: https://productos.ca/documentation/api/v1/webhooks (last reviewed 2026-09-23)

Webhooks run in two directions: **inbound** (available: other services notify ProductOS) and **outbound** (planned: ProductOS notifying your service).

## Inbound

ProductOS receives inbound webhooks on two kinds of URL. The GitHub App delivers every workspace's events to `/api/integrations/github/webhook`; the App's own configuration points at it, so **there is nothing for you to set up** once GitHub is connected. Per-workspace endpoints at `/api/webhooks/{provider}/{workspaceId}` accept Linear, GitHub issue and Jira deliveries verified against a per-workspace secret; these are not yet configurable from Settings.

### How signatures are verified

Each delivery is verified against the secret ProductOS holds for that connection before anything runs:

| Provider | Algorithm | Header |
| :--- | :--- | :--- |
| GitHub | HMAC-SHA256 | `X-Hub-Signature-256` |
| Linear | HMAC-SHA256 | `Linear-Signature` |
| Jira | Shared secret (Jira sends no signature) | `?secret=` query parameter |

A delivery that fails verification is rejected with `401` and never runs a handler. Deliveries to the per-workspace endpoints, accepted or rejected, are recorded in the integration health log. There is no timestamp check on inbound deliveries; replay protection rests on the secret and HTTPS.

### Event mapping

Each connector maps what it receives or syncs to ProductOS state changes. For the precise mapping per provider see:

- [Connecting GitHub](https://productos.ca/documentation/guides/integrations/github)
- [Linear integration](https://productos.ca/documentation/guides/integrations/linear)
- [Intercom integration](https://productos.ca/documentation/guides/integrations/intercom)

### Side-effect: notifications

Beyond syncing state, the GitHub webhook emits an in-app notification when a delivered event matches a user-relevant change. Specifically, `pull_request` deliveries fan out to:

- `pr.merged_for_my_feature`: when a PR is merged and its URL is attached to a feature (a `github_pr` document link) that you lead or are assigned to

Delivery channels (in-app bell, email; the Slack column is saved but does not deliver yet) are governed per-user from [Settings → Notifications](https://productos.ca/documentation/pages/settings/notifications). Other inbound providers don't emit notification events today.

## Outbound (not yet available)

Outbound webhook subscriptions are on the roadmap but **not available yet**, and no release date is committed. Today you can poll the API or read changes through the [MCP server](https://productos.ca/documentation/api/v1/mcp). What follows is a preview of the intended design and may change before it ships. The planned event types:

| Event | Fires when |
| :--- | :--- |
| `feedback.created` | New feedback row arrives (any source) |
| `feedback.linked` | Feedback gets linked to a theme or feature |
| `theme.promoted` | A theme is promoted into a feature |
| `feature.shipped` | Feature flips to `status = shipped` |
| `release.published` | A release with `is_public = true` is published |
| `outcome.recorded` | Outcome is finalized (result + learning fields filled) |

Subscriptions will be created through a `/api/v1/` endpoint and signed with HMAC-SHA256 in `X-ProductOS-Signature` plus `X-ProductOS-Timestamp`; the exact shape is not final.

> **Need outbound today?**
>
> Until outbound webhooks ship, the practical workaround is to poll the entity's list endpoint sorted by `updated_at` (`?sort=updated_at&order=desc`) and stop at the last timestamp you saw. Or, for low-latency needs, mount the MCP server in your editor and have Claude Code react to changes directly.

## Next

- [Authentication](https://productos.ca/documentation/api/v1/authentication): how to authenticate outbound polling.
- [Connecting GitHub](https://productos.ca/documentation/guides/integrations/github): the most-used inbound webhook integration.
