Stripe to Slack in 3 steps
Create a Stripe Transactions relay
From Viably, create a relay endpoint and select Stripe Transactions in the dropdown. This helps Viably expect Stripe-formatted payloads and to route them to the right Slack message template.
Add Webhook Destination in Stripe
In your Stripe dashboard, add a new Webhook Destination with the Viably query-param URL. Select charge.succeeded, invoice.paid, and optionally customer.subscription.updated for cancellation alerts.
Copy the signing secret back to Viably
Viably needs the Stripe signing secret to verify incoming requests. Copy it from the Stripe dashboard and paste it into your relay endpoint's config in Viably.
Stripe Integration (Detailed Instructions)
- 1
Create a relay endpoint
In the Viably dashboard, create a new relay endpoint with Stripe Integration enabled. Once created, open the token dialog and copy the Stripe Webhook Destination โ it looks like
https://hq.viably.app/relay?token=โฆ. - 2
Create a webhook destination in Stripe
In your Stripe Dashboard, go to Developers โ Webhooks โ Add destination. Paste the Viably query-param URL as the Endpoint URL. Select these events:
charge.succeeded,invoice.paid, and optionallycustomer.subscription.updated.charge.succeededcaptures all successful transactions in Slack โ whether from Stripe Checkout or your own API integration.invoice.paidenriches each charge card with the customer's name, email, phone, and country.customer.subscription.updatedis optional โ it fires a cancellation recovery alert when a subscriber schedules their cancellation, so your customer care team can reach out before the billing period ends.
Do not send other Stripe events to this endpoint โ Viably silently drops unrecognised event types, but sending them unnecessarily adds noise to your webhook logs. - 3
Copy the signing secret
After saving, Stripe displays a signing secret beginning with
whsec_. Copy it to complete your setup on Viably. If you miss it, you can reveal or roll it from the destination details page. - 4
Complete setup in Viably
Back in the Viably dashboard, click Edit on the relay endpoint. Paste the
whsec_โฆvalue into the Stripe webhook secret field and save. Viably will now verify every incoming Stripe request and reject anything unsigned. - 5
Test it
Use Stripe's Send test event button on the destination page to fire a sample event. Check your configured Slack channel โ you should see a formatted message within a few seconds.
Authorization headers. The ?token=โฆ format is the Viably-supported alternative for these integrations. Both URLs are shown in the token dialog after endpoint creation.| Event | How Viably renders it |
|---|---|
charge.succeededrequired | Transaction card โ amount and charge time as title, customer name (with country flag) as subtitle, email, phone, plan, subscription period in the body. In batch mode, each charge gets its own swipeable card in a carousel. |
invoice.paidrequired | Enriches the matching charge.succeeded card with customer name, email, phone, country, plan nickname, and billing period. In non-batch mode, also caches customer contact details (keyed by Stripe customer ID) for up to 90 days to enrich subscription cancellation alerts. |
customer.subscription.updatedoptional | Posts a cancellation recovery alert when a subscriber schedules cancellation (cancel_at_period_end flips false โ true). Shows customer name, email, phone, plan, billing period, and churn reason. Duplicate events are silently dropped. New activations and generic updates are also forwarded. |
charge.succeeded โ Transaction fields
data.object and maps the following fields into a Transaction card in Slack. Every field is optional โ Viably renders what it can find and omits the rest.| Stripe field | Shown as | Notes |
|---|---|---|
data.object.amount | Amount | Divided by 100 โ Stripe stores amounts in the smallest currency unit (e.g. cents) |
data.object.currency | โ | Used for currency formatting; defaults to USD if absent |
data.object.description | Product | |
data.object.billing_details.name | Name | Falls back to data.object.customer_email |
data.object.billing_details.email | Falls back to data.object.customer_email | |
data.object.billing_details.phone | Phone | |
data.object.billing_details.address | Location | city and country joined โ e.g. "Singapore, SG" |
data.object.payment_intent | Payment Intent | |
data.object.metadata.* | Various | Selected keys from the Stripe metadata map โ see below |
Recognised metadata keys
Viably scans data.object.metadata and surfaces the following keys as labelled fields in the Slack card. When multiple key aliases are listed, the first non-empty one wins. Any other metadata key is silently ignored.
| metadata key(s) | Shown as |
|---|---|
discount | Discount |
eventName, event | Event |
orderId, order_id | Order ID |
type | Type |
tickets | Tickets |
voucherCode, voucher | Voucher |
description | Description |
product | Product |
tier | Tier |
customer.subscription.updated โ Subscription events
| Stripe field | Shown as | Notes |
|---|---|---|
data.previous_attributes.cancel_at_period_end: false โ data.object.cancel_at_period_end: true | Heading | Triggers the recovery alert: "๐ซ Cancellation Scheduled". Only fires on the specific false โ true flip โ not on subsequent events where it is already true. |
data.object.customer | Customer | Enriched from cached invoice.paid data when available (shows name with country flag, email, phone). Falls back to bare Stripe customer ID (cus_โฆ) if no invoice.paid was ever received for this customer. |
data.object.items.data[0].price.nickname | Plan | Falls back to lookup_key, then product ID |
data.object.items.data[0].price.unit_amount + currency + recurring.interval | Price | Formatted as e.g. "$59.00 / monthly" or "$99.00 / every 3 months". unit_amount divided by 100. |
data.object.items.data[0].current_period_start + current_period_end | Current Period | Formatted in workspace timezone. The recovery alert leads with 'Reach out before {end date}' to give the team a clear deadline. |
data.object.cancellation_details.feedback / reason / comment | Feedback / Reason / Comment | Each field is shown only when non-null. feedback and reason are capitalised (e.g. 'Too expensive', 'Cancellation requested'). |
data.previous_attributes.status (incomplete / trialing) โ data.object.status: active | Heading | Triggers "๐ New Subscription Activated" when a trial or incomplete subscription becomes active. |
data.object.current_period_end | Period ends | Shown on generic (non-cancellation, non-activation) subscription updates. |
customer.subscription.updated events for a single user action, and some carry cancel_at_period_end: true in both the current and previous attributes (not a new cancellation). Viably drops those silently โ only the event where cancel_at_period_end transitions from false to true triggers the recovery alert. New activations and other subscription updates are collapsed into one Slack message per subscription within a 90-second window.Periodic reporting
Period label
A 1-day window shows a single date (e.g. Apr 25). A multi-day window shows a date range (e.g. Apr 19 โ Apr 25). All dates are rendered in your workspace's configured timezone.
Revenue sparkline
When events span more than one time bucket, a bar sparkline (e.g. โโโโโโ
โโ) shows the relative revenue distribution across buckets at a glance โ taller bars mean more revenue in that slot.
Transaction carousel
Up to 20 charges are shown newest-first as swipeable cards. Each card's title is the amount, subtitle is the customer name (or email), and the body shows email, phone/location, plan, subscription period, and charge time. If the batch has more than 20, a footer note shows the remaining count.
Unsupported events
Any Stripe events other than charge.succeeded and invoice.paid that arrive at this endpoint are acknowledged (not dropped) but excluded from the revenue count. Their event type names are listed in a footer warning on the batch card.
5 Transactions โ My Stripe Relay), with a Total Revenue and Transactions stats section directly below for visual hierarchy. Stripe charges are de-duplicated by charge ID before the total is calculated, so retried or re-delivered webhooks are never double-counted.