# Receivers

The URLs StreamAgent listens on. Revenue sources report purchases and refunds into the outcomes ledger against the lead who watched; scheduling tools confirm bookings so a lead is stamped as booked. Every receiver is per workspace and verified by a secret; the URL itself carries no trust.

## Revenue sources

Connect a source under Integrations → Revenue. Stripe and ClickFunnels connect in one click (Stripe Connect, and the ClickFunnels connection from the platforms section); when that path cannot complete, the card opens the manual form instead. Shopify asks for its webhook signing secret, and the generic source mints a secret for you and shows it once. Whichever way a source connects, the manual endpoint below stays valid: point the provider at it yourself and paste its signing secret if you would rather not connect the account. The endpoint shape is the same for all four:

```text
https://app.streamagent.io/api/webhooks/outcomes/{provider}?ws={workspace id}
```

| Provider | Signature header | Records | Reverses |
| --- | --- | --- | --- |
| Stripe | `stripe-signature` | checkout.session.completed (mode=payment), invoice.paid | charge.refunded |
| ClickFunnels | `x-webhook-clickfunnels-signature` | one-time-order.completed | one_time_order.refunded |
| Shopify | `x-shopify-hmac-sha256` | orders/paid | refunds/create, orders/cancelled |
| Any system | `x-streamagent-signature` | purchase, custom | refund |

A recorded outcome lands as `outcome.recorded` on your [webhooks](/docs/webhooks) and in the revenue figures on the dashboard. Provider retries are recognised by the provider's own id and answered as duplicates; unrelated events are acknowledged and ignored so the provider does not retry them.

## Stripe Connect

Connect Stripe on the Revenue card authorizes StreamAgent on your own Stripe account (Stripe Connect, OAuth). You pick the account on Stripe's page; on return the card reads "Connected via Stripe · acct_…" with that account's id. Stripe then delivers the account's events to StreamAgent itself: there is no endpoint to create and no secret to paste, and a sale does not have to start inside StreamAgent to count.

| Stripe event | What happens |
| --- | --- |
| `checkout.session.completed`, `mode=payment`, `payment_status=paid` | A purchase for `amount_total` in the session's currency; the payment intent is kept as the reference a refund can name. |
| `checkout.session.completed` in subscription or setup mode | Ignored: the payment is recorded from its `invoice.paid`, so the first payment is never doubled. |
| `invoice.paid` | A purchase for `amount_paid`, labeled by `billing_reason` unless `metadata.sa_label` is set. Zero-amount invoices are ignored. |
| `charge.refunded` with `refunded=true` | Reverses the purchase whose payment intent (or invoice) the charge names. |
| `charge.refunded` for a partial refund | Ignored: the purchase stands at its sale value. |
| `account.application.deauthorized` | You removed StreamAgent from inside Stripe: the connection is marked disconnected and the source paused. Reconnect from the card. |

Matching: the buyer's checkout email (`customer_details.email`, or the invoice's `customer_email`) is matched against the workspace's leads. For an exact match from a checkout you build, set `client_reference_id` to the StreamAgent lead id, or `metadata.sa_lead_id` on the session, the invoice, or the subscription. `metadata.sa_label` names the purchase in the ledger. Amounts are recorded in the currency Stripe reports, so a buyer who paid in a local currency shows in that currency.

> Disconnect on the card deauthorizes StreamAgent at Stripe and drops the connection. Events are verified with Stripe's signature on a StreamAgent-held secret; the per-workspace endpoint below is not involved on this path.

## The generic revenue source

Any system that can POST JSON can report revenue. Sign the raw body the way StreamAgent signs its own webhooks: an `X-StreamAgent-Signature` header of `t=<unix ms>,v1=<hex HMAC-SHA256 of "<t>.<body>">` using the secret the card minted. The verification is the mirror of the one on the [Webhooks](/docs/webhooks#verify) page.

| Field | Required | Notes |
| --- | --- | --- |
| `event` | yes | `purchase`, `custom` or `refund`. |
| `id` | yes | Your id for the order. Repeats are treated as the same outcome; a refund names the order it reverses. |
| `amount_cents` or `amount` | purchase, custom | Integer cents, or a decimal amount in the currency. |
| `currency` | no | Three-letter code, `USD` when omitted. |
| `label` | no | Up to 120 characters, shown in the ledger. |
| `email` | no | Used to match the lead when `lead_id` is absent. |
| `lead_id` | no | A StreamAgent lead id, when you have it. |
| `occurred_at` | no | ISO 8601; now when omitted. |
| `ref` | no | A secondary reference, kept with the outcome. |

```json
{
  "event": "purchase",
  "id": "ord_10492",
  "amount_cents": 49900,
  "currency": "USD",
  "label": "Annual plan",
  "email": "maria@example.com",
  "occurred_at": "2026-09-07T14:03:11.412Z",
  "ref": "inv_88a1"
}
```
_A purchase_

```json
{
  "event": "refund",
  "id": "ord_10492",
  "occurred_at": "2026-09-08T09:15:00.000Z"
}
```
_A refund of the same order_

> Match quality depends on what you send: a `lead_id` matches exactly, an `email` matches the lead who gave it, and neither leaves the outcome recorded but unattributed.

## Scheduling tools

When a Book a call element uses your own Calendly or Cal.com page, the booking still lands in StreamAgent once the tool is connected on the Integrations page.

| Tool | How it connects | What you configure |
| --- | --- | --- |
| Calendly | Sign in from the Calendly card; StreamAgent creates the webhook subscription itself and stores its signing key. | Nothing. Deliveries are verified with the `calendly-webhook-signature` header. |
| Cal.com | Paste a signing secret you choose into the Cal.com card; the connected card shows the receiver URL. | In Cal.com, create a webhook at `https://app.streamagent.io/api/webhooks/calcom?ws={workspace id}` with the same secret and enable Booking Created and Booking Cancelled. Deliveries are verified with `X-Cal-Signature-256`. |

- A created booking resolves or creates the lead from the invitee's email, name and phone, stamps the booking, and fires `booking.created` on your webhooks.
- A cancellation un-books the lead and fires `booking.canceled`.
- Reschedules and other triggers are acknowledged without changes.

---
Source: https://app.streamagent.io/docs/receivers
