Write Stripe events to Postgres, your database
Pipe any Stripe event into your own Postgres database. Templated columns, upserts, idempotent retries. The action Stripe will never ship natively.
Free trial · From $19/mo · No credit card required
The problem
Every serious SaaS eventually needs Stripe data in its own database. Not Stripe's data warehouse, not a third-party CDP, your Postgres, the one with the user table that has a foreign key to billing. Today you write stripe events to postgres with a custom webhook handler, hosted somewhere, with retries, signature verification, and a schema you maintain by hand. Or you pay Fivetran $500+/mo to sync the whole Stripe object model on a delay. Both are heavy for what should be a workflow step.
The Postgres action is the one Stripe will not ship natively. Point it at your database with a connection string (sslmode required), map Stripe event fields to columns with JSONata, set an upsert key, and write. It runs as a step in Stripe Workflows. The data lands in your schema, in your timezone, with your column names, deduped on Stripe's invocation id. That's the whole differentiator.
How write stripe events to postgres works
- 1
Add your connection string
Paste a postgres:// URL into the action. Outbound requires sslmode=require, stores the string encrypted per account, and connects through a pool. Works with Neon, Supabase, RDS, Crunchy, Railway, self-hosted. - 2
Map event fields to columns
Configure the target table and a column-to-JSONata mapping. Outbound infers column types from the database and validates the mapping on save. Type coercion handles numbers, timestamps, jsonb, and booleans without code. - 3
Set an upsert key (optional)
Pick a column or set of columns that uniquely identify a row — usually a stripe id. Outbound builds an ON CONFLICT clause so re-running a workflow won't create duplicates. Skip the key and every execution appends. - 4
Publish and watch your tables fill
The action writes one row per Stripe invocation. The execution log shows the row count, timing, and any constraint violations. Your queries return Stripe data the moment the event fires.
// outbound.postgres step in a Stripe Workflow
{
trigger: 'invoice.paid',
action: 'outbound.postgres',
config: {
connection_string_secret: 'PG_URL',
table: 'stripe_invoices',
upsert_key: ['stripe_invoice_id'],
columns: {
stripe_invoice_id: '{{invoice.id}}',
customer_id: '{{customer.id}}',
amount_paid: '{{invoice.amount_paid}}',
currency: '{{invoice.currency}}',
paid_at: '{{$fromMillis(invoice.status_transitions.paid_at * 1000)}}',
raw: '{{$}}' // store full event as jsonb
},
},
}Example workflow configuration
Screenshot of the Postgres action config inside the Stripe Workflow builder. Shows a connection string field with masked characters, a table picker dropdown listing tables from the connected database, a column-to-JSONata mapping grid with type indicators (text, int, timestamp, jsonb) next to each column, and an upsert key multi-select with two columns chosen.
Outbound vs custom webhook handler
| Outbound | custom webhook handler | |
|---|---|---|
| Hosted infrastructure to maintain | — | |
| Signature verification + retries | built-in | you build it |
| Idempotent on Stripe invocation id | you build it | |
| Setup time | ≈5 minutes | 1-2 days |
| Pricing model | From $19/mo flat | Hosting + dev time |
| JSONata column templating | — | |
| Lives inside the Stripe Dashboard | — |
Frequently asked questions
Which Postgres flavors are supported?+
How do you handle schema changes?+
Can the connection go through a VPC or private network?+
What about transactional writes across multiple tables?+
How do you keep credentials safe?+
Related integrations
Stripe to Notion database
Notion when the team wants to browse. Postgres when SQL needs to join against the user table.
Generic webhook to any HTTPS URL
Use the HTTP action if your Postgres lives behind a service you can't expose publicly.
Trigger Stripe from any HTTPS POST
Coming with Inbound: a Postgres trigger or pg_notify could one day fire a Stripe Workflow.