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 completed within 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 the 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 have failed, and there is a minimum of five 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 once 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 three 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 canceled. 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 final. 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 integer in the smallest currency unit (e.g. 100 for $1.00, -100 for $-1.00; 500 for £5.00, -500 for -£5.00, 100 for ¥100).
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.
Updated 5 months ago