1. Rowfire
  2. Blog

How to trigger Braze campaigns from your database

Send a Braze custom event when something becomes true in PostgreSQL or MySQL, like a declined card or a stalled trial, and let a campaign take it from there.

  • PostgreSQL
  • MySQL
  • Braze
  • how-to
  • sql
  • braze

Lifecycle campaigns start with an event: the card was declined, the trial is three days from ending and the account never activated, the customer hasn’t logged in for two weeks. When those moments only exist in your database, the campaign waits for an engineer to add a tracking call. Rowfire, which is open source, sends the event to Braze from a SQL query instead.

1. The moment, as a query

A trigger is one read-only SELECT on PostgreSQL, MySQL or MariaDB:

SELECT a.id          AS account_id,
       a.name        AS account,
       pa.invoice_no,
       pa.amount,
       pa.attempted_at
FROM payment_attempts pa
JOIN accounts a ON a.id = pa.account_id
WHERE pa.outcome = 'failed'
  AND a.is_internal = false

The clock is attempted_at and the key is account_id. A rule decides how often one account may get the event: once ever, once per day, week or month, or every nth time. Once per account per week keeps a run of retries from starting the campaign three times.

2. The Braze action

Connect Braze with a REST API key and your cluster’s base URL (Braze is regional), then use the track_event action:

field value
User ID (external_id) {{ account_id }}
Event name payment_failed
Properties { "invoice_no": "{{ invoice_no }}", "amount": "{{ amount }}" }

Leave the event time blank and Rowfire uses the trigger’s clock. Each fired row becomes a request of this shape to /users/track:

{
  "events": [
    {
      "external_id": "5",
      "name": "payment_failed",
      "time": "2026-10-08T09:14:00+00:00",
      "properties": { "invoice_no": "INV-5-LIVE", "amount": "49.00" }
    }
  ]
}

3. The campaign

In Braze, create an action-based campaign or Canvas whose trigger is the custom event payment_failed. From there it’s Braze’s: the message, the delays, the exits. Rowfire’s job is to send the event once, at the right moment, for the right user.

4. Check it before customers see it

A backtest replays the rule over past days and shows how many events it would have sent, with example rows. Shadow mode then renders each request against live data and records it without sending it, so nobody gets a campaign while you’re still checking. Promote the rule when the events look right. A new trigger starts at now, so switching it on doesn’t send an event for every old decline.

Good to know

Triggers are polled, within a minute by default: fine for campaigns and follow-ups, not for messages that must arrive instantly. The same trigger can also tell your team: one rule sends the Braze event, another posts to Slack or opens a Zendesk ticket.

For more, see push when a customer nears their quota, rescue trials that never activated and Rowfire vs reverse ETL.

Questions

Does this need a code change in our app?

No. Rowfire reads your database with a read-only query and sends the custom event to Braze's /users/track endpoint itself. Nothing in your application changes.

Which user does the event go to?

The one whose external_id you map from the row, usually your own customer or account id, for example {{ account_id }}.

Can it also update a profile attribute?

Yes. The Braze integration has a second action, update_attribute, which sets a custom attribute on the user from any column.

Isn't this what reverse ETL does?

Reverse ETL keeps attributes in sync with columns. A custom event says something just happened, once, which is what starts an action-based campaign. Many teams use both.