ViablyViably
๐Ÿ’ณWebhook Relay

Send any payment event to Slack

Not on Stripe? No problem. Viably's Webhook Relay accepts a simple JSON payload from any payment provider or homegrown billing system, formats it into a clean transaction card, and posts it to your Slack channels โ€” with optional periodic revenue summaries so your team always knows where revenue stands.

Get started in 2 steps

โš ๏ธ Before you start:Invite @Via to the Slack channel where you want webhooks to appear. Without this, Viably will accept your webhook but cannot post to the channel.
Step 1

Create a Non-Stripe Transactions relay

In the Viably dashboard, click "+ New Relay" โ†’ Transactions โ†’ Non-Stripe Transactions. Give it a name, pick your Slack channels, and hit Create. You'll get a Bearer token and relay URL immediately.

Step 2

POST your payload from your payment handler

After a successful payment in your backend, send a POST to the relay URL with your Bearer token in the Authorization header. Viably formats it and posts to Slack within seconds.

Bearer auth, not query-param. Unlike Stripe (which posts to a plain URL), your own backend can set custom headers. Always pass the token as Authorization: Bearer <token> and store it in an environment variable โ€” never hardcode it.

Payload reference

POST a flat JSON object to the relay URL. Viably reads the following top-level fields and maps them into a transaction card in Slack. Fields marked required must be present for the message to render correctly and for batch reporting to produce accurate totals.
FieldStatusNotes
amountrequiredInteger in minor currency units โ€” e.g. 4990 for $49.90. Divided by 100 before display. Also summed across events for batch totals and periodic revenue reports โ€” if missing or sent as a decimal, report figures will be wrong.
currencyrequiredISO 4217 code โ€” e.g. "USD", "EUR", "IDR". Defaults to USD if absent, but explicit is better.
namerecommendedCustomer full name. Shown as card subtitle with country flag if country is also provided. Falls back to email if absent.
emailrecommendedCustomer email. Shown as a labelled field in the card body when name is also present.
descriptionrecommendedWhat was purchased โ€” shown as "Product". Falls back to the product field if absent.
sourceoptionalOrigin site or app (e.g. pay.myapp.com). Shown in the message footer. Suppresses the missing-source hint.
titleoptionalOverrides the default card header. Useful for branding โ€” e.g. '๐Ÿ’ฐ New Sale on Acme Pay'.
submitted_atoptionalISO 8601 timestamp of the transaction (e.g. "2026-04-27T08:30:00Z"). Shown as "Submitted at".
phoneoptionalCustomer phone number.
cityoptionalCustomer city. Joined with country into a "Location" field โ€” e.g. "Jakarta, ID".
countryoptionalISO 3166-1 alpha-2 code โ€” e.g. "US", "ID", "SG". Drives the country flag emoji next to the customer name.
Minor units โ€” always. amount must be an integer in the smallest currency unit, consistent with Stripe and most payment processors. Send 4990 for $49.90 โ€” not 49.90. Viably divides by 100 before display and sums raw integers for batch totals and periodic reports. Sending major units will make every revenue figure 100ร— too large.

Code examples

Call the relay from your payment success handler โ€” after your payment provider confirms the charge. Store the token in an environment variable, never in source code.
const VIABLY_TOKEN = process.env.VIABLY_RELAY_TOKEN;
const VIABLY_URL  = "https://hq.viably.app/relay";

await fetch(VIABLY_URL, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${VIABLY_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    amount: 4990,                           // required โ€” minor units (4990 = $49.90)
    currency: "USD",                        // required โ€” ISO 4217
    name: "Samuel Chan",                    // recommended
    email: "[email protected]",             // recommended
    description: "Pro Plan โ€” Monthly",      // recommended โ€” shown as Product
    source: "pay.myapp.com",                // optional โ€” shown in message footer
    title: "๐Ÿ’ฐ New Transaction",            // optional โ€” overrides card header
    submitted_at: new Date().toISOString(), // optional โ€” ISO 8601
    phone: "+6287712121212",                // optional
    city: "Jakarta",                        // optional
    country: "ID",                          // optional โ€” ISO 3166-1 alpha-2
  }),
});
Channel override. Route individual events to a specific Slack channel by adding a channels array to the payload: "channels": ["C0XXXXXXX"]. Viably routes to those channels instead of the endpoint default, as long as the bot is a member.

Periodic reporting

Enable Batch Mode on a Non-Stripe Transactions relay to accumulate events over a configurable window and post a single summary card โ€” rather than one message per transaction. Enable Periodic Reporting to also receive a scheduled revenue digest on a daily or multi-day cadence.

Revenue total

Viably sums the amount values across all events in the batch window and displays a Total Revenue figure. The sum is computed from the raw integers you send, so the minor-unit convention is load-bearing โ€” a stray decimal will corrupt every report total.

Transaction carousel

Up to 20 transactions are shown newest-first as swipeable cards. Each card shows amount and submission time as the title, customer name with country flag as the subtitle, and email, phone, city, and product in the body.

Revenue sparkline

When events span more than one time bucket, a sparkline (e.g. โ–โ–‚โ–„โ–‡โ–ˆโ–…โ–‚โ–) shows the relative revenue distribution across buckets at a glance โ€” taller bars mean more revenue in that slot.

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.