Configure Webhooks on
All Platforms

Configure Webhooks

Button allows you to configure webhooks so you can get real-time updates to your system when users push Buttons in your app. To do so, you will need to configure the destination of the webhook and what events you want to receive. Then, Button will send you an HTTP request when any of those events are created.

It's a very good idea to not inherently trust any request received by your webhook endpoint. See Security for details.

For full source code of sample apps in Node.js, Ruby, and Python that securely receive Button webhooks, see here. You can also find these in the Example Implementation section, below.

In this guide, we will go over:

Create or Edit a Webhook

To create a new webhook or edit an existing one, you can do so on the Dashboard.

Pause or Resume a Webhook Endpoint

You can pause, resume, and monitor the status of a webhook endpoint through the Dashboard.

Automatically Paused Webhooks

A webhook endpoint is automatically paused when greater than 10% of webhooks created and attempted in the past hour has failed, and there is a minimum of 5 failed webhooks. We will send out an email notification to the organization indicating that the endpoint was paused. While the endpoint is paused, we will collect all new webhooks and redeliver them when the endpoint is resumed. You can resume a paused webhook through the Dashboard.


Request Body

Webhooks will be sent with an HTTP POST to your configured URL. The body of the webhook request will include the following root level fields:

Field Key Field Value/Purpose
request_id The unique identifier for the request attempt. If a webhook fails to receive the proper response code, it will make attempts, each with a unique request_id to re-deliver the webhook payload (see more about Retry Logic below)
data An object of the order details for the transaction (see more about the data fields below)
id The unique identifier for the webhook event
event_type The "state" the transaction event represents (see more about Event States below)

Below is an example request payload highlighting these root level fields:

    "request_id": "attempt-XXX",
    "data": {
    "id": "hook-XXX",
    "event_type": "tx-validated"

The headers will include:

  • Content-Type: application/json
  • User-Agent: Button Webhooks X.X.X
  • X-Button-Signature: XXXXXXXXX

Event States

There are 3 unique states to orders in Button's systems, presented in the event_type field in the webhook's root. The below table provides details on each state:

State Terminal? Mutable? Billable? Definition
tx-pending No Yes No Indicates that the transaction has been created, but not yet entered a final state, meaning that it can still be cancelled or adjusted through Button's API.
tx-validated Yes No Yes Indicates that the finalization period for a transaction has concluded successfully, and thus will be billable to the Merchant.
tx-declined Yes No No Indicates that the transaction has been cancelled by the user, and thus will not be billable to the Merchant.

Event Objects


A transaction is a record of a commission owed or earned.

A transaction is created when a user moves from a Publisher to a Merchant and takes an action like installing the app or making a purchase (API details for a Merchant to report an order). A finalized transaction initiates a credit the Publisher for driving the user's transaction or app installation, and a debit the Merchant as they received an order or an app installation. We capture the various transaction event types in the category field. Additionally, transactions have a status associated with them so that you can identify the current state of the life-cycle the transaction is in.

  • account_id: The publisher's Account ID for this transaction. A Publisher will have a separate Account ID for each currency in which they drive a Merchant transaction.
  • advertising_id (optional): The device's advertising ID (IDFA on iOS and Google AID on Android). Only available to Merchants when this value was reported.
  • amount: The amount your organization is owed or has earned (i.e. commission amount), in the smallest decimal currency units.
  • btn_ref: Button's tracking token associated with this transaction.
  • button_id (optional): The ID of the button.
  • button_order_id (optional): Order ID generated by Button. (only available for new-user-order and existing-user-order events)
  • campaign_id (optional): a Button configuration value distinguishing the Merchant Campaign ID tied to this transaction.
  • category: The type of event that has lead to the transactions.
    • new-user-order: A newly created order for a user.
    • existing-user-order: An updated order for a user.
    • app-install: An installation of the Merchant application.
  • commerce_organization: The organization ID of the application that the user was driven into.
  • created_date: When the transaction was first created (ISO-8601).
  • currency: The currency of the amount, as a three-letter ISO currency code (ISO 4217).
  • event_date: When the order or app install occurred (ISO-8601).
  • modified_date: When the transaction was last modified (ISO-8601).
  • order_currency (optional): The currency of the order. (only available for new-user-order and existing-user-order events)
  • order_id (optional): Order ID reported by the Merchant. (only available for new-user-order and existing-user-order events and Merchants)
  • order_click_channel: If known, the channel in which the user made the purchase (e.g. App, Webview)
  • order_line_items (optional): An array of the order's line item details. (only available for new-user-order and existing-user-order events).
    • attributes (optional): A key/value store for strings. Defaults to an empty object.
    • amount: The line item order value, in smallest decimal currency units. Will be an integer. Example: 1234 for $12.34, -1234 for -$12.34.
    • identifier: The unique identifier of your line item.
    • description (optional): Text describing the line item.
    • quantity (optional): The number of units of this line item. Will be an integer > 1
  • order_purchase_date (optional): When the customer made the purchase, if reported by the Merchant.
  • order_total (optional): The total attributable order value of the order, in the smallest decimal currency units. (only available for new-user-order and existing-user-order events)
  • pub_ref: The publisher token set when a Button or Link was fetched (only visible to the Publisher).
  • publisher_customer_id (optional): The publisher's internal customer ID (only available for the publisher organization, and only if it is configured)
  • publisher_organization: The organization ID of the application that drove the user to another application.
  • status: The current state of the transaction.
    • pending: A transaction has been created, but it is subject to change. app-install events skip this state.
    • validated: A transaction is final and will be paid on a later date.
    • declined: A transaction has been canceled (this can be from an order being canceled).
  • validated_date (optional): When the transaction moved to validated status (ISO-8601).

Don't be surprised if Button adds some additional fields to help with any necessary debugging (Ex. 'campaign_id')

An example webhook object:

  "request_id": "attempt-XXX",
  "data": {
    "posting_rule_id": null,
    "order_currency": "USD",
    "modified_date": "2017-01-01T20:00:00.000Z",
    "created_date": "2017-01-01T20:00:00.000Z",
    "order_line_items": [
            "attributes": {
                "size": "M"
            "amount": 2000,
            "identifier": "sku-1234",
            "description": "T-shirts",
            "quantity": 3
    "button_id": "btn-XXX",
    "campaign_id": "camp-XXX",
    "rate_card_id": "ratecard-XXX",
    "order_id": "order-1",
    "account_id": "acc-XXX",
    "btn_ref": "srctok-XXX",
    "currency": "USD",
    "pub_ref": "publisher-token",
    "status": "validated",
    "event_date": "2017-01-01T20:00:00Z",
    "order_total": 6000,
    "advertising_id": "aaaaaaaa-1111-3333-4444-999999999999",
    "publisher_organization": "org-XXX",
    "commerce_organization": "org-XXX",
    "amount": 1000,
    "button_order_id": "btnorder-XXX",
    "publisher_customer_id": "11111111-2222-3333-4444-999999999999",
    "id": "tx-XXX",
    "order_click_channel": "app",
    "category": "new-user-order",
    "validated_date": "2017-01-06T19:02:09Z"
  "id": "hook-XXX",
  "event_type": "tx-validated"

Filter webhook payload for "order" events

Button’s webhooks fire requests on all commissionable events, including orders and app-installations. So, when a commissionable event occurs, you will receive a webhook from Button. Depending on how and what you're using Button webhooks for, you'll want to appropriately filter out webhook requests.

For example, if you’re rewarding users on purchases they make, you’ll want to filter out the webhook requests representing app-installs by checking the value of the category field in the request payload. If the value is app-install, ignore the request. If the value is new-user-order or existing-user-order, process the request into user rewards.

Validate payload request data

When validating the data in the webhook request payload, we recommend you verify the availability of specific fields you need instead of validating against a specific JSON structure. This is because the JSON structure may change as we enhance the data reported to us by Merchants (e.g. "Jet now reports UPC data with each line-item"). This approach will ensure that your webhooks don’t error when such an update is made.


To acknowledge that you have successfully received a webhook, you should respond with a 2XX status code.

Unsuccessful webhooks due to connection issues or non-4XX error will be re-sent for up to 3 days. Retries follow an exponential back-off strategy for the first hour. After that, retries happen once an hour.

To maintain the proper receipt order of messages, new webhooks will only be sent once previous ones are acknowledged. Therefore, if you don't acknowledge a webhook, you won't be bombarded by new webhooks, but you also won't receive recent information.

Error handling

Button's transaction webhooks enforce ordering on a single transaction, but sends webhooks for independent transactions concurrently to maximize throughput. This means that for a webhooks concerning a specific transaction, before the "next" event that occurred, we require confirmation that the "current" webhook was properly accepted. However, webhooks for other transactions can still send as normal. We limit the total number of transactions webhooks we are trying to send in parallel for to an endpoint at any one time.

For this reason, it’s important to respond to a webhook request with a 2XX level HTTP response code, unless a real error occurred requiring subsequent webhook for that transaction to be paused until this error is resolved.

Below is a list of suggested response codes and what they signify to Button:

  • 200 or 204 - Webhook accepted and successfully processed.
  • 202 - Webhook accepted but processing was unsuccessful.
  • 400 - Webhook is invalid and cannot be processed. Button will not attempt to re-send this webhook.
  • 401 - Webhook signature is invalid. Button will attempt to re-send this webhook.

Retry Logic

If Button receives a 400 response for a webhook, we will not attempt to re-send that webhook.

If Button receives a 3XX, 4XX (other than 400), or 5XX response for the webhook, we will retry this webhook with an exponential back-off strategy for the first hour. After that, retries happen once an hour up to 3 days.

Button limits the number of webhooks that it tries to send to an endpoint at any one time. If the number of failing webhooks in the retry state for an endpoint exceeds the number that Button attempts to send, Button will not send any new webhooks to that endpoint until at least one of the webhooks in the retry state succeeds or exceeds its retry limit.

We recommend logging 202 and 4XX responses and flagging them up to our Customer Service team for further investigation.


To receive Webhooks from Button, you must expose a public route on the open internet. Because sensitive business-logic often sits behind a webhook handler, it's important to verify that a request received in your webhook endpoint is actually from Button.

To do this, Button cryptographically hashes the payload we send with a Webhook Secret. Each Webhook configured in the Dashboard will have its own secret. Then, on the receiving end, the server may compute the same hash given a copy of the Secret and the request payload. Equating this computed hash with the one Button sent guarantees a few things (assuming your Secret has never been compromised):

  1. The request is guaranteed to be from Button
  2. The contents of the request are guaranteed to be exactly as Button sent them (that is, no malicious computer could have intercepted the request and altered the contents of the payload)

To generate an equivalent hash, you must:

  1. Capture the raw request body before any parsing has occurred. This should be a UTF-8 encoded string.
  2. Supply your Webhook Secret and the string from (1) to your language's HMAC implementation, using SHA-256 as the hashing algorithm.
  3. Digest the result from (2) as a hex-encoded string.

Note: It's a good idea to read your Webhook Secret from the server's environment rather than checking it into version control.

Client Libraries

Button's client libraries include helper methods for validating incoming webhook requests:

Alternatively, you can integrate one of the following examples directly into your project.

Example Implementation

For generic use in the popular web frameworks Express, Sinatra, and Flask for Node.js, Ruby, and Python, respectively:

var express = require('express');
var app = express();

var bodyParser = require('body-parser');
var crypto = require('crypto');

var port = process.env.PORT || 5000;

// You can find your Webhook's Secret in the Dashboard at on the page for a specific Webhook.
var WEBHOOK_SECRET = 'YOUR_WEBHOOK_SECRET' // Do not publicly expose this

app.use(bodyParser.json({ verify: verify, type: 'application/json' }));'/webhook', function(req, res) {
  // Here is the webhook data
  var data =;


app.listen(port, function() {
  console.log('Listening on port: ' + port)

function verify(req, res, buf, encoding) {
  if (req.headers['x-button-signature'] !== signature(buf)) {
    throw new Error('Invalid Webhook Signature');

function signature(requestBody) {
  return crypto.createHmac('sha256', WEBHOOK_SECRET)
require 'sinatra'

require 'openssl'
require 'json'

# You can find your Webhook's Secret in the Dashboard at on the page for a specific Webhook.
WEBHOOK_SECRET = 'YOUR_WEBHOOK_SECRET' # Do not publicly expose this

post '/webhook' do
  request_body =
  raise 'Invalid Webhook Signature' if request.env["HTTP_X_BUTTON_SIGNATURE"] != signature(request_body)

  json = JSON.parse(request_body)

  # Here is the webhook data
  data = json['data']

  status 200

def signature request_body

import os
import hmac
import hashlib

from flask import Flask, request, abort

app = Flask(__name__)

# You can find your Webhook's Secret in the Dashboard at on the page for a specific Webhook.
WEBHOOK_SECRET = 'YOUR_WEBHOOK_SECRET' # Do not publicly expose this

@app.route('/webhook', methods=['POST'])
def webhook():
    computed_signature = signature(
    sent_signature = request.headers.get('X-Button-Signature').encode('utf8')

    if not hmac.compare_digest(computed_signature, sent_signature):

    # Here is the webhook data
    data = request.json['data']

    return 'ok', 200

def signature(request_body):
    return, request_body, hashlib.sha256).hexdigest()

if __name__ == "__main__":

The full source code for each of these sample apps can be found here.

What's Next

If you haven't already checked it out, see how to pass parameters into the Button SDK to then receive via webhooks or use webhooks for Loyalty and Rewards:

Pass Data through Button

You may want to pass data unique to your system through Button for easier end-to-end tracking when integrating Button. This guide goes over different methods for setting these values and reading them downstream.

View Guide

Use Button for Loyalty and Rewards

With Button you can perform real-time loyalty & rewards transactions across the app ecosystem so you can begin rewarding your members for their daily purchases on their mobile devices.

View Guide