Ambassly

Set Up the Stripe Webhook

Set up the Ambassly Stripe webhook by adding your per-company endpoint in Stripe and selecting the required checkout, invoice, refund, and subscription events. Ambassly verifies Stripe signatures and processes events idempotently so retried webhooks do not create duplicate referrals or commissions.

Ambassly receives subscription and refund events from your Stripe account through a per-company webhook endpoint.

Your endpoint URL is:

https://ambassly.com/api/hook/COMPANY_PUBLIC_ID

Replace COMPANY_PUBLIC_ID with the company public ID shown in Ambassly.

Add the endpoint in Stripe

  1. Open the Stripe Dashboard for the account that processes your customer payments.
  2. Go to Developers -> Webhooks.
  3. Click Add endpoint.
  4. Enter your Ambassly endpoint URL:
https://ambassly.com/api/hook/COMPANY_PUBLIC_ID
  1. Select these four events exactly:
checkout.session.completed
invoice.paid
charge.refunded
customer.subscription.deleted
  1. Save the endpoint.
  2. Open the endpoint, reveal the signing secret, and save that whsec_... value in Ambassly for this company.

Use the signing secret from this Dashboard webhook endpoint. Do not use a Stripe CLI signing secret for the production Dashboard endpoint.

Why these events are required

checkout.session.completed creates the Ambassly referral. Ambassly resolves the referral code from the Checkout Session's client_reference_id or metadata.ambassly, then links the referral to the Stripe customer.

invoice.paid creates a pending commission when the Stripe customer maps to a referral and the invoice is within the program's commission window: the first paid invoice for a one-time program, or each invoice within the recurring window (up to recurring_months, or every invoice when the program is lifetime) for a recurring program.

charge.refunded claws back matching pending or approved commissions when the charge is refunded.

customer.subscription.deleted claws back matching pending or approved commissions when the subscription is deleted.

Signature verification

Ambassly verifies each request with Stripe's webhook signature. Stripe sends the signature in the Stripe-Signature header, and Ambassly verifies it against the per-company webhook signing secret you saved.

The verification model is:

const event = stripe.webhooks.constructEvent(
  rawRequestBody,
  stripeSignatureHeader,
  companyWebhookSecret
);

The request body must be the raw body Stripe sent. If the body is parsed or changed before verification, signature verification can fail.

If Ambassly returns a signature error during setup, check:

  • The endpoint URL uses the correct COMPANY_PUBLIC_ID.
  • The saved secret starts with whsec_.
  • The secret came from the same Stripe Dashboard webhook endpoint that is sending events.
  • Test-mode events are sent to a test-mode endpoint secret, and live-mode events are sent to the live-mode endpoint secret.

Idempotency guarantees

Stripe may deliver the same event more than once. Ambassly treats webhook processing as idempotent.

For every incoming Stripe webhook, Ambassly first inserts a row into its webhook_events ledger using Stripe's event ID as a unique key:

webhook_events.stripe_event_id

If the Stripe event ID has already been seen, Ambassly skips processing it.

This means:

  • Sending the same checkout.session.completed twice does not create two referrals.
  • Sending the same invoice.paid twice does not create two commissions.
  • Sending the same charge.refunded twice does not claw back twice.
  • Sending the same customer.subscription.deleted twice does not claw back twice.

Ambassly's idempotency is scoped to Stripe event IDs. A different Stripe event with a different event ID is processed independently.

Testing

Use Stripe test mode first.

  1. Create a test checkout with an Ambassly code in client_reference_id and/or metadata.ambassly.
  2. Complete the checkout.
  3. Confirm Stripe delivered checkout.session.completed to:
https://ambassly.com/api/hook/COMPANY_PUBLIC_ID
  1. For recurring products, confirm Stripe later sends invoice.paid.
  2. Refund the charge in test mode and confirm Stripe sends charge.refunded.
  3. Cancel the test subscription and confirm Stripe sends customer.subscription.deleted.