Button Webhooks

Button Webhooks allow you to get notified when a transaction takes place based on traffic you drive to brands from your app. In this section, we'll dive into Button Webhooks and close the loop on the user's e-commerce journey.

Creating a Webhook

  • Creating a Button Webhook is done through the Button Dashboard.
  • Log in using your Organization’s Button credentials, then click on your Organization’s drop down icon in the top right to navigate menu options. Select Webhooks.
  • This will navigate you to the Webhooks overview page where you can see your existing Webhooks if any have been created. To create a Webhook, select Add a Webhook on the right side of this view.
  • A modal will pop up and ask you for some details before creating the Webhook. This includes your organization’s URL where you wish to receive Webhook data, along with they type of data you wish to receive. Check the three Transaction event types – Validated, Pending, and Declined. Then select Add.

Editing a Webhook

  • After you’ve created a Button Webhook, you can edit it from within the Webhooks page in your Button Dashboard.
  • Log in using your Organization’s Button credentials, then click on your Organization’s drop down icon in the top right to navigate menu options. Select Webhooks.
  • This will navigate you to the Webhooks overview page where you can see your existing Webhooks if any have been created. To edit a Webhook, select an existing one by clicking on the Webhook’s URL. Then, within that Webhook view, click Edit in the top right of the screen.

This will open the same Webhook dialogue as when you created it. Once you make your modifications, click Update to save your changes.

Pause or Resume a Webhook Endpoint

You can pause and resume a Webhook from sending data to the Webhook’s endpoint. Once in the Webhook view, select the Webhook you wish to pause/resume by clicking the checkbox to the left of the Webhook URL, then select Pause or Resume.

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.

Webhook Specifications

Webhooks are sent over HTTP as a POST request to your configured URL. The body of the webhook request includes the following root level fields

Request Body:

Field Key

Field Value / Purpose

request_id

The unique identifier of a request attempt, even when retrying a failed request

data

The order details represented as a Transaction containing order details

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:

Header Key

Header Value

Content-Type

application/json

User-Agent

Button Webhooks X.X.X

X-Button-Signature

XXXXXXXXX

Webhook 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 table below provides details on each state:

State

Mutable?

Billable?

Definition

tx-pending

Yes

No

Indicates that the transaction has been created but is not in a final state. It can still be adjusted or cancelled. Commission is not yet guaranteed for a transaction in this state.

tx-validated

No

Yes

Indicates that the finalization period for a transaction has concluded. The transaction will never be modified beyond this point. Commission as stated in the Webhook data.amount is guaranteed. All earnings are final and billable to the Brand.

tx-declined

No

No

Indicates that the transaction has been cancelled by the user or the Brand. The transaction will never be modified beyond this point. The transaction will not be billable to the Brand.

When a transaction is reported with the tx-pending state, make sure to check whether it already exists in your system using the button_order_id. If it does, the webhook is reporting an order adjustment (i.e. the state of the data field has
changed in some way).

Filter Out App Install Webhooks

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.
If you’re using webhooks to signal when to commission the end user for making a purchase, be sure to filter out app-install from the data.category webhook request payload. Both the Play and App stores prohibit rewarding users for app installs.

Transaction Details

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

A transaction is created when a user links from either a Publisher app or mobile website, to a Brand app or mobile website and makes a purchase. Commissionable events include: downloading a Brand's App or making a purchase. We capture the transaction event types in the category field.

Transactions have an associated status that indicates a stage in the transaction life-cycle. A tx-validated status indicates that the transaction is immutable and billable. All pending status transactions are subject to change.

There are a number of financial fields within the transaction. Button models all fields that contain a financial value as an an integer in the smallest currency unit (e.g. 100 for $1.00, -100 for $-1.00; 500 for £5.00, -500 for -£5.00).

Transaction Properties

Transaction Key

Transaction Value

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 Brand transaction.

advertising_id

The device's advertising ID (IDFA on iOS and Google AID on Android). Only available when this value was reported.

amount

The commission your organization is owed or has earned.

attribution_date

The date Button used for selecting commission rates. (ISO-8601).

btn_ref

Button tracking token associated with this transaction.

button_id

Legacy field; value is either static or unknown.

button_order_id

Button generated Order ID that is globally unique. Only available for new-user-order events.

campaign_id

A Button configuration value distinguishing the Brand Campaign ID tied to this transaction.

category

The type of event that generated this commission. Click here to navigate to those fields.

commerce_organization

The Button organization ID for the Brand that received the billable transaction.

created_date

When the transaction was first created in Button's system (ISO-8601).

currency

The currency of the commission, as a three-letter ISO currency code (ISO 4217).

customer_order_id

The customer-facing order ID reported by the Brand. Only available for new-user-order events and when reported by the Brand. Note: This field represents the order identifier communicated to the customer.

event_date

When the transaction was processed by Button's system (ISO-8601).

id

Button generated Transaction ID that is globally unique and persists throughout the lifecycle of a transaction.

modified_date

When the transaction was last modified (ISO-8601).

order_currency

The currency of the order. (only available for new-user-order events). (ISO 4217).

  • Note: Unless otherwise specified (by your Button representative), this will reflect the same value as currency (see above), and can be ignored in favor of that field. *

order_id

Brand reported ID in their own namespace. Only available for new-user-order events and when reported by the Brand.

order_click_channel

The channel in which the user completed the transaction. Values include: app, webview, unknown. Will be a string.

order_line_items

An array of the order's line item details. Only available for new-user-order events.

order_purchase_date

The Brand reported date and time that the user made the purchase. Only available when this value was reported. (ISO-8601).

order_total

The total value of the order. Includes total value of all items purchased, whether or not they qualified for a commission. Includes discounts, if applied. Only available for new-user-order events.

pub_ref

The publisher reference value set when a Button or Link was fetched. Only visible to the Publisher, and only if it is configured. String with a maximum length of 512.

publisher_customer_id

The publisher's internal customer ID. Only visible to the Publisher, and only if it is configured. String with a maximum length of 255.

publisher_organization

The Button organization ID for the Publisher application that drove the transaction.

status

The current state of the transaction, which will be one of the following values. More detail here.

validated_date

When the transaction moved to validated status. (ISO-8601).

Transaction Status Properties

Status Key

Status Value

pending

A transaction has been created, but it is subject to change. app-install events skip this state.

validated

A transaction is final and immutable and will be paid out.

declined

A transaction has been canceled (user or Brand cancels an entire order).

🚧

Note

Button may add additional fields at any time. As such, ensure that you do not have validations in place that check for the full schema of the webhook; only put validations/logic place on specific fields.

Transaction Order Line Item Properties

Order Line Item Key

Order Line Item Value

identifier

The unique identifier of your line item.

total

The total price of all items bought in a particular line item (e.g. if 3 bananas were purchased for $3.00 each, total would be 900). If provided, amount will be ignored and set to round (total / quantity). Must be net of any discounts or taxes and represent the value a Brand expects to commission on.

amount

The line item order value, in smallest decimal currency units. Will be an integer. Example: 1234 for $12.34, -1234 for -$12.34.

sku

(optional): The Stock Keeping Unit of the line item. A Brand-specific identifier for the line item. String with a maximum length of 255.

quantity

The number of individual units purchased. If present, will be an integer > 0.

publisher_commission

The total commission generated by the line item. Will be an integer >= 0. A publisher_commission equal to 0 means no commission was earned for this line item. This field will only appear on transactions where created_date is after 2018-07-18T23:00:00Z.

gtin

(optional): The Global Trade Item Number of the number. Supported values: UPC and EAN. String with 14 numeric digits.

category

(optional): The topmost (or most general) level category of the line item. Example: For an item with category Electronics > Computers > Laptops this field would have the value Electronics. Will be a string.

subcategory1

(optional): The first subcategory of the line item. Example: For an item with category Electronics > Computers > Laptops this field would have the value Computers. Will be a string.

subcategory2

(optional): The second subcategory of the line item. Example: For an item with category Electronics > Computers > Laptops this field would have the value Laptops. Will be a string.

description

Text describing the line item. Will be a string.

attributes

A key/value store for strings. Defaults to an empty object. Only available when this value was reported.

Transaction Category Properties

Category Key

Category Value

new-user-order

Commission driven by an order event. No relation to user segment.

app-install

Commission driven by the installation of the Brand application.

Example Webhook

{
      "id": "hook-xxxxxxxxxxxxxxxx",
      "event_type": "tx-validated",
      "data": {
        "posting_rule_id": null,
        "order_currency": "USD",
        "modified_date": "2020-04-04T00:00:00Z",
        "created_date": "2020-04-04T00:00:00Z",
        "order_line_items": [
          {
            "total": 1000,
            "identifier": "000000000000",
            "quantity": 1,
            "publisher_commission": 100,
            "subcategory1": "T-Shirts",
            "attributes": {},
            "amount": 1000,
            "description": "A very comfortable t-shirt",
            "sku": "000000000000",
            "category": "Clothes"
          }
        ],
        "button_id": "unknown",
        "campaign_id": null,
        "rate_card_id": "ratecard-xxxxxxxxxxxxxxxx",
        "order_id": null,
        "account_id": "acc-xxxxxxxxxxxxxxxx",
        "customer_order_id": null,
        "attribution_date": "2020-04-04T00:00:00Z",
        "btn_ref": "srctok-xxxxxxxxxxxxxxxx",
        "currency": "USD",
        "pub_ref": "publisher_pub_ref_123",
        "status": "validated",
        "event_date": "2020-04-04T00:00:00Z",
        "order_total": 1000,
        "advertising_id": null,
        "publisher_organization": "org-xxxxxxxxxxxxxxxx",
        "commerce_organization": "org-xxxxxxxxxxxxxxxx",
        "amount": 100,
        "button_order_id": "btnorder-xxxxxxxxxxxxxxxx",
        "publisher_customer_id": "publisher_user_id_123",
        "order_purchase_date": "2020-04-04T00:00:00Z",
        "id": "tx-xxxxxxxxxxxxxxxx",
        "order_click_channel": "app",
        "category": "new-user-order",
        "validated_date": "2020-04-04T00:00:00Z"
      },
      "request_id": "attempt-xxxxxxxxxxxxxxxxx"
    }

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 https://app.usebutton.com/webhooks 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' }));

    app.post('/webhook', function(req, res) {
      // Here is the webhook data
      var data = req.body.data;

      res.sendStatus(200);
    })

    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)
        .update(requestBody)
        .digest('hex');
    }
require 'sinatra'

    require 'openssl'
    require 'json'

    # You can find your Webhook's Secret in the Dashboard at https://app.usebutton.com/webhooks on the page for a specific Webhook.
    WEBHOOK_SECRET = 'YOUR_WEBHOOK_SECRET' # Do not publicly expose this

    post '/webhook' do
      request_body = request.body.read
      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
    end

    def signature request_body
      OpenSSL::HMAC.hexdigest(
        OpenSSL::Digest.new('sha256'),
        WEBHOOK_SECRET,
        request_body
      )
    end
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 https://app.usebutton.com/webhooks on the page for a specific Webhook.
    WEBHOOK_SECRET = b'YOUR_WEBHOOK_SECRET' # Do not publicly expose this

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

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

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

        return 'ok', 200

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

    if __name__ == "__main__":
        app.run()

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


What’s Next
Did this page help you?