Docs

Everything you need to install Outbound and wire your first action.

Quickstart

  1. Install Outbound from the Stripe App Marketplace.
  2. Open Apps → Outbound → Settings in your Stripe Dashboard.
  3. Paste credentials for the integrations you want to use (see Integrations below).
  4. Go to Workflows → New Workflow.
  5. Pick a trigger (e.g. invoice.paid), then click Add Action and search for the Outbound action you want.
  6. Fill in the action form. Dynamic dropdowns (Linear teams, Notion databases, Postgres tables) populate automatically from your credentials.
  7. Click Test to fire the action once with a sample event, then Activate.

Integrations

Each integration needs its credential entered in the Outbound settings page. Credentials live in Stripe's Secret Store — Outbound never sees them at rest.

Discord

Credential: webhook URL. In your Discord server, go to Server Settings → Integrations → Webhooks, create a new webhook scoped to the channel you want to post to, and copy the URL. Paste it as discord_webhook_url in Outbound settings.

Action input: message (required, max 2000 chars, auto-truncated), optional username and avatarUrl to override the webhook defaults per message.

Telegram

Credential: bot token from @BotFather. Format: <bot_id>:<hash>.

Action input: chatId (numeric, e.g.-1001234567890, or @channelusername), text (max 4096 chars), optional parseMode (MarkdownV2, HTML, or Markdown), optional disableNotification.

To get a chat ID, add your bot to the chat then visit https://api.telegram.org/bot<TOKEN>/getUpdates.

Resend

Credential: API key from resend.com/api-keys, starts with re_.

Action input: from (must be on a domain verified in your Resend account), to, subject, plus at least one of html or text. Optional replyTo.

Notion

Credential: internal integration token from notion.so/my-integrations.

One-time setup: the database(s) you want to write to must be shared with your integration. Open the database in Notion → ⋯ menu →Add connections → select your integration.

Action input: databaseId (use the dynamic dropdown, or paste the 32-char hex ID from the database URL), properties as an object in Notion's API shape.

Notion's property values vary by column type. Examples:

{
  "Name": { "title": [{ "text": { "content": "New customer" } }] },
  "Email": { "email": "x@example.com" },
  "MRR": { "number": 99 },
  "Plan": { "select": { "name": "Pro" } },
  "Tags": { "multi_select": [{ "name": "trial" }, { "name": "enterprise" }] },
  "Signed up": { "date": { "start": "2026-05-18" } }
}

See Notion's property-value reference.

Linear

Credential: personal API key from Settings → API → Personal API keys. Starts with lin_api_.

Action input: teamId (use the dynamic dropdown or paste from Linear team settings), title, optional description (markdown), projectId, labelIds (array), priority (0–4: 0=none, 1=urgent, 4=low), assigneeId.

Postgres

Credential: connection string for your own Postgres database. Format: postgresql://user:pass@host:port/db?sslmode=require. SSL is strongly recommended.

Action input: table (use the dynamic dropdown or type a name; must match /^[A-Za-z_][A-Za-z0-9_]*$/), optional schema (defaults to public), columns as an object of column name → value pairs, optional returning array of column names to return from the inserted row.

All values are bound as parameters — no SQL injection vector through values. Identifiers are strictly validated then double-quoted.

Webhook + JSONata

The escape hatch. Fires an HTTP request to any HTTPS endpoint with either a literal JSON body or a JSONata-templated body computed from the Stripe event.

Credential (optional): generic_webhook_auth_header — the full Authorization header value (e.g. Bearer xxx or Basic abc=). Leave empty for unauthenticated endpoints.

Action input: url (HTTPS only), optional method (default POST), optional headers object, and one of bodyJson (literal) or bodyTemplate (JSONata expression) plus a context object to evaluate against.

Example JSONata template:

bodyTemplate: '{ "amount_cents": amount_paid, "customer": customer_email, "currency": $uppercase(currency) }'
context:      { amount_paid, customer_email, currency }

See the JSONata docs for the full template language reference.

Error handling

Every action classifies errors as permanent (Stripe stops retrying) or retryable (Stripe retries with exponential backoff):

  • 4xx from a third party (other than 429) → permanent. Your input or credentials likely need to change.
  • 429 rate limit → retryable. We honor Retry-After when present.
  • 5xx from a third party → retryable.
  • Network failure → retryable.
  • Schema/validation failure on your action input → permanent.
  • Action exceeds the 18-second budget → 504, retryable. (Stripe gives us 20s total; we leave 2s buffer.)

Every invocation is idempotent on Stripe's invocation ID, so retries can't cause duplicate side-effects (no duplicate Discord pings or Notion rows).

FAQ

Do I need to write any code?

No. Outbound is configured entirely in the Stripe Dashboard. The exception is the Webhook action, where the JSONata template is plain text (no JavaScript).

What happens during a third-party outage?

Failures are classified as retryable and Stripe will keep retrying with backoff. For long outages you'll start seeing executions count against your monthly limit without producing results; check the execution log if you suspect this.

Can I have multiple workflows triggering the same action?

Yes. Each workflow is independent. Idempotency is per-invocation, not per-workflow.

How do I see what an action returned?

Each action emits a result object that downstream workflow steps can reference via dynamic fields. The Discord action returns { delivered: true }; Notion returns { pageId, url }; Linear returns { issueId, identifier, url, title }; Resend returns { emailId }; Postgres returns { rowCount, returned }; webhook returns { status, responseBody }.

Where do I send feature requests?

Email support@outboundforworkflows.com — every request is read.