Configure Webhooks

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.

Create or Edit a Webhook

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

Format

Webhooks will be sent with an HTTP POST to your configured URL.

The body of the message:

{
    "id": "hook-1c1d7937f9715e3e",
    "event_type": "tx-validated",
    "data": {},
    "request_id": "attempt-4843253923fd59a8",
}
  • id: the unique ID for the webhook
  • event_type: the type of event that triggered the webhook. This field also indicates what object will be in the data field. List of event states.
  • data: this is the main payload of the message. Description of the different event objects.
  • request_id: since messages can be sent multiple times, this is the unique ID per request

The headers will include

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

Acknowledgment

To acknowledge that you have successfully received a webhook, you should respond with a 2XX status code. Anything else will be considered a failure, and the message will be re-sent.

Unsuccessful webhooks 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.

Event States

  • tx-pending: A transaction has entered the pending state. Object transaction.
  • tx-validated: A transaction has entered the validated state. Object transaction.
  • tx-declined: A transaction has entered the declined state. Object transaction.

Event Objects

Transaction

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

A transaction is created when a user moves from one app to another and takes an action like installing the app, or purchasing an item (API details for a Commerce app to report an order). This is so that we can credit the Publisher app for driving the user, and debit the Commerce app as they received something they wanted (a new user or an order). Since there various events that can drive a transaction, we capture that in the category field. Also, transactions have a status associated with them, to allow you to identify the current state the of the life-cycle the transaction is in.

  • id: The unique identifier for the transaction.
  • amount: The amount your organization is owed or earned, in the smallest decimal currency units.
  • currency: The currency of the amount, as a three-letter ISO currency code (ISO 4217).
  • category: The type of event that has lead to the transactions.
    • new-user-order: An order for a user who just installed the commerce partner application.
    • existing-user-order: An order for a user who already had the commerce partner application installed.
    • app-install: An installation of the commerce partner application.
  • created_date: When the transaction was first created.
  • modified_date: When the transaction was last modified.
  • validated_date (optional): When the transaction moved to validated status.
  • event_date: When the order or app install occurred.
  • btn_ref: Button's tracking token associated with this transaction.
  • pub_ref: The publisher token set when a Button or Link was fetched (only visible to the Publisher).
  • account_id: Account ID.
  • order_id (optional): Order ID reported by the commerce partner. (only available for new-user-order and existing-user-order events and commerce partners)
  • button_order_id (optional): Order ID generated by Button. (only available for new-user-order and existing-user-order events)
  • order_currency (optional): The currency of the order. (only available for new-user-order and existing-user-order events)
  • 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)
  • order_line_items (optional): An array of the order's line item details. (only available for new-user-order and existing-user-order events).
    • identifier: The unique identifier of your line item.
    • amount: The line item order value, in smallest decimal currency units. Will be an integer >= 0. Example: 1234 for $12.34.
    • quantity (optional): The number of units of this line item. Will be an integer > 1
    • description (optional): Text describing the line item.
    • attributes (optional): A key/value store for strings. Defaults to an empty object.
  • publisher_organization: The organization ID of the application that drove the user to another application.
  • publisher_customer_id (optional): The publisher's internal customer ID (only available for the publisher organization, and only if it is configured)
  • button_id: The ID of the button.
  • commerce_organization: The organization ID of the application that the user was driven into.
  • 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).
  • advertising_id (optional): The device's advertising ID (IDFA on iOS and Google AID on Android). Only available to commerce partners, when this value was reported.

An example object

{
    "id": "tx-070dd05f5db6e4ec",
    "amount": 600,
    "currency": "USD",
    "category": "new-user-order",
    "created_date": "2016-06-01T19:02:09Z",
    "modified_date": "2016-06-01T19:02:09Z",
    "validated_date": "2016-06-01T19:02:09Z",
    "event_date": "2016-06-01T19:01:50Z",
    "button_order_id": "btnorder-4db429a36440c00e",
    "order_id": "order-1",
    "account_id": "acc-123",
    "order_currency": "USD",
    "order_total": 6000,
    "order_line_items": [
        {
            "identifier": "sku-1234",
            "amount": 2000,
            "quantity":3,
            "description": "T-shirts",
            "attributes": {
                "size": "M"
            }
        }
    ],
    "btn_ref": "srctok-713c963c1c30c55f",
    "pub_ref": null,
    "publisher_organization": "org-63cd58a1a2a8b543",
    "publisher_customer_id": "6815467b-93ca-47ce-97ae-bcf8b4292a87",
    "button_id": "btn-59722058ab439774",
    "commerce_organization": "org-44ae5a8149efe050",
    "status": "validated",
    "advertising_id": "02840796-66B3-4C96-AF72-84393B4925BF"
}

Security

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 any web framework:

var crypto = require('crypto');

var WEBHOOK_SECRET = process.env['WEBHOOK_SECRET'];

function signature(requestBody) {
  return crypto.createHmac('sha256', WEBHOOK_SECRET)
    .update(requestBody)
    .digest('hex');
}
require 'openssl'

def signature request_body
  OpenSSL::HMAC.hexdigest(
    OpenSSL::Digest.new('sha256'),
    ENV['WEBHOOK_SECRET'],
    request_body
  )
end
import os
import hmac
import hashlib

WEBHOOK_SECRET = os.environ['WEBHOOK_SECRET']

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

For use in one of the popular web frameworks:

// For use in an express application

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

var app = express();

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

app.use(bodyParser.json({ verify: verify, type: 'application/json' }));
# For use in a Flask application
import hmac
from flask import Flask, request, abort

app = Flask(__name__)

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

  if not hmac.compare_digest(computed_signature, sent_signature):
    abort(403)

  return 'ok'

You can find your Webhook's Secret in the Dashboard on the page for a specific Webhook.