Developer docs
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:
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 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.
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 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. |
{
"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"
}{
"event": "refund",
"id": "ord_10492",
"occurred_at": "2026-09-08T09:15:00.000Z"
}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.createdon your webhooks. - A cancellation un-books the lead and fires
booking.canceled. - Reschedules and other triggers are acknowledged without changes.