How it works
Create an endpoint
In your Viably dashboard, create a relay endpoint and assign it to one or more Slack channels. You'll receive a URL and a secret token.
POST a JSON payload
From your site or service, send a POST request with your event data. Include a type field to route it to the right Slack message template.
See it in Slack
Viably formats the payload into a clean Block Kit message and posts it to your channel โ immediately, or batched on a schedule you choose. Stripe charge and subscription events are combined into a single summary card.
Environment variables
VIABLY_RELAY_URL=https://hq.viably.app/<your-endpoint-provided>
VIABLY_RELAY_TOKEN=<your-secret-token>Both values are shown in the dashboard immediately after creating an endpoint. Your token is only displayed once โ store it somewhere safe.
TypeScript types
viablyTypes.ts. Each event type carries its own recommended fields โ Viably will surface a hint in Slack when a recommended field is missing.// viablyTypes.ts
export type ViablyEventType =
| "transaction"
| "leadgen"
| "signups"
| "contactform"
| "devops"
| "others"
| "ocr";
type ViablyPayloadBase = {
/** Routes the event to the right Slack template. Defaults to "others" if unrecognised. */
type: ViablyEventType;
/** Recommended. Identifies the origin โ e.g. "My Site โ Contact Page". */
source?: string;
/** ISO 8601 timestamp. Viably formats it using your workspace timezone. */
submitted_at?: string;
/** Override the default Slack channels set in your dashboard. */
channels?: string[];
[key: string]: unknown;
};
export type ViablyTransactionPayload = ViablyPayloadBase & {
type: "transaction";Sending a payload
// lib/viably.ts
import type { ViablyPayload } from "./viablyTypes";
export async function sendToViably(payload: ViablyPayload): Promise<void> {
const res = await fetch(process.env.VIABLY_RELAY_URL!, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.VIABLY_RELAY_TOKEN}`,
},
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error(`Viably relay failed: ${res.status}`);
}
// --- Contact form submission ---
await sendToViably({
type: "contactform",
source: "My Site โ Contact Page",
message: form.message,
email: form.email,
name: form.name,
submitted_at: new Date().toISOString(),
});
Event types
type field routes the event to the matching Slack message template. If omitted or unrecognised, it falls back to others. Viably adds an inline ๐ก hint to the Slack message when a recommended field is absent.| type | Description | Recommended fields |
|---|---|---|
transaction | Payments, purchases, and financial events | amount |
leadgen | Lead captures, downloads, and gated content fills | email |
signups | New user or account registrations | email |
contactform | Contact or enquiry form submissions | messageemail |
devops | Deployments, CI/CD results, and infrastructure alerts | titlestatus |
others | Anything that doesn't fit a specific category | โ |
ocr | Reserved for Viably OCR integrations. Results are silently routed back to the originating Slack thread โ nothing is posted to the relay's configured channel. | job_idmessagepart |
"My Site โ Contact Page". Viably uses this for footer attribution on every message. If omitted, it falls back to the HTTP Referer/Origin header, then to the domain you set on the endpoint in the dashboard.new Date().toISOString(). Viably formats it using your workspace's default timezone and displays it as a Submitted at field in the Slack message. If omitted, no timestamp is shown.Channel override
channelsarray in your payload. If Viably can't access any of the override channels, it falls back to the endpoint defaults automatically.// Send to specific channels, overriding the defaults in your dashboard
await sendToViably({
type: "devops",
source: "GitHub Actions",
title: "Production deploy",
status: "deployed",
channels: ["C0123456789", "C9876543210"], // Slack channel IDs
});Channel IDs can be found by right-clicking a channel in Slack โ View channel details โ copy the ID at the bottom.
Stripe Integration
- 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 / query-param URL โ 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 two events:
charge.succeeded, andcustomer.subscription.updated.charge.succeededin particular would have captured all your successful transactions in Slack, whether they originated from Stripe Checkout, or from your own custom integration using Stripe's APIs. Viably looks for this event's payload to extract the correctamount,currency, anddescriptionto build out your transaction messages in Slack.
You should not send any other Stripe events to this Webhook Relay endpoint, as that could cause an unexpected heavy load of message in your workspace and even causing double notifications or double-counting in revenue tracking. - 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.* | Transaction card โ amount, customer name, email, phone, location from billing_details. In batch mode, each charge is a row in a per-transaction table. |
customer.subscription.updated | Informational card showing plan, price, and customer ID. Automatically detects cancellation, new activation, or generic update. Rapid-fire events for the same subscription within ~90 s are deduplicated. |
Any extra fields are welcome
Beyond the recognised fields, you can include anything in the payload โ city, plan, current_page_url, custom metadata. Viably will render them as additional context in the Slack message so nothing is lost.