Transactions API

Transactions API

While webhooks are the standard method for retrieving transactions from Button, you can also retrieve the same information from our Transactions API. This API allows you to retrieve the current status of all your transactions that have been reported to Button by Brands. In this guide, we’ll go over:

  1. What is a Transaction?
  2. Use Cases
  3. Calling the Transactions API
  4. Transaction States
  5. Ingestion Logic

What is a Transaction?

A transaction is created when a user deeplinks from a Publisher to a Brand and either installs the Brand's app or makes a purchase.


Use Cases

The Transactions API enables you to pull transactions that you helped drive. So, you can:

  • Notify your users of rewards they've earned
  • Apply rewards to your users' accounts
  • Obtain granular line item details of what your users' purchased

Calling the Transactions API

Request Parameters

  • id: Your Account ID (unique per currency)
  • start: Filter transactions created at or after this time. Time is in RFC 3339 format. Example: 2019-01-01T00:00:00Z
  • end: Filter transactions created before this time. Time is in RFC 3339 format. Example: 2019-01-05T00:00:00Z
  • time_field (optional): Time field with which to order transactions. Recommend to use the value of modified_date in order to always pull in the latest state of all transactions.
  • cursor (optional): An opaque string that lets you view a consistent list of transactions.

Sample Request

curl https://api.usebutton.com/v1/affiliation/accounts/acc-XXX/transactions?start=2019-01-01T00%3A00%3A00Z&end=2019-01-05T00%3A00%3A00Z&time_field=modified_date \
  -X GET \
  -u YOUR_API_KEY:

var client = require('@button/button-client-node')('sk-XXX');

client.accounts.transactions('acc-XXX', {
  start: '2019-01-01T00:00:00Z',
  end: '2019-01-05T00:00:00Z'
  time_field: 'modified_date',
}, function(err, res) {
    // ...
});
from pybutton import Client

client = Client('sk-XXX')

response = client.accounts.transactions('acc-XXX',
    start='2019-01-01T00:00:00.000Z',
    end='2019-01-05T00:00:00.000Z',
    time_field='modified_date',
)

print(response)
# <class pybutton.Response [100 elements]>
require 'button'

client = Button::Client.new('sk-XXX')

response = client.accounts.transactions('acc-XXX', {
  start: '2019-01-01T00:00:00Z',
  end: '2019-01-05T00:00:00Z'
  time_field: 'modified_date',
})
cursor = response.next_cursor

puts response
# => Button::Response(75 elements)

Sample Response

{
  "meta": {
    "status": "ok",
    "next": "https://api.usebutton.com/v1/affiliation/acc-xxx/transactions?cursor=cD0yMDE2LTAyLTExKzE3JTNBMDUlM0ExNyUyQjAwJTNBMDA3",
    "previous": null
  },
  "objects": [
    //Sample Transaction -- order
    {
      "publisher_customer_id": "1111-3333-4444-999999999999",
      "publisher_organization": "org-YYY",
      "publisher_organization_name": "Publisher Company Name",
      "commerce_organization": "org-XXX",
      "commerce_organization_name": "Brand Company Name",
      "button_id": "static",
      "account_id": "acc-XXX",
      "btn_ref": "srctok-XXX",
      "pub_ref": "Publisher Reference String",
      "button_order_id": "btnorder-XXX",
      "order_id": null,
      "order_total": 6000,
      "order_currency": "USD",
      "order_line_items": [
        {
          "identifier": "sku-1234",
          "total": 6000,
          "amount": 2000,
          "quantity": 3,
          "publisher_commission": 600,
          "sku": "sku-1234",
          "gtin": "00400000000001",
          "category": "Clothes",
          "subcategory1": "Kids",
          "description": "T-shirts",
          "attributes": {
            "size": "M"
          }
        }
      ],
      "order_click_channel": "app",
      "order_purchased_date": null,
      "category": "new-user-order",
      "id": "tx-XXX",
      "created_date": "2019-01-01T19:49:17Z",
      "modified_date": "2019-01-01T19:49:17Z",
      "validated_date": null,
      "attribution_date": "2019-01-01T19:49:17Z",
      "amount": 600,
      "currency": "USD",
      "status": "pending",
      "advertising_id": null
    },
    //Sample Transaction -- app install
    {
      "publisher_customer_id": "1111-3333-4444-999999999999",
      "publisher_organization": "org-YYY",
      "publisher_organization_name": "Publisher Company Name",
      "commerce_organization": "org-XXX",
      "commerce_organization_name": "Brand Company Name",
      "button_id": "static",
      "account_id": "acc-XXX",
      "btn_ref": "srctok-XXX",
      "pub_ref": "Publisher Reference String",
      "button_order_id": null,
      "order_id": null,
      "order_total": null,
      "order_currency": null,
      "order_line_items": null,
      "order_click_channel": null,
      "order_purchased_date": null,
      "category": "app-install",
      "id": "tx-XXX",
      "created_date": "2019-01-02T19:49:17Z",
      "modified_date": "2019-01-02T19:49:17Z",
      "validated_date": "2019-01-02T19:49:17Z",
      "attribution_date": "2019-01-02T19:49:17Z",
      "amount": 100,
      "currency": "USD",
      "status": "validated",
      "advertising_id": null
    }
  ]
}

Account IDs

You will need to pass in your Account ID into the API request. You will have one Account ID per currency that your users transact in. So, if you have a user who buys a product in USD, and one user who buys a product in GBP, these two transactions will sit under two separate Account IDs. Button will create a new Account ID only after a transaction is created in our system in a new currency; as such, you should call this API daily in order to check for the existence of a new Account ID (i.e. transaction(s) in a new currency).

You will need to call the Transactions API for each Account ID you have with Button to ensure you pull all of your transactions. For more details on calling the Accounts API, please see here).

Call Frequency

In order to ensure you are keeping your system and your users to up to date, it is important that you call the Transactions API multiple times per hour. We typically recommend calling the API every 5-10 minutes during high-traffic periods, and every 30-60 minutes during low-traffic periods.

Paging through results

The Transactions API returns a fixed number of transactions and includes the HTTP URL for the “next” page of transactions. As such, after you process the first page of transactions in the API response, you can utilize the meta.next URL as the next request URL for this API call until the value is null .


Transaction States

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

State Terminal? Mutable? Billable? Definition
tx-pending No 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 this transaction.
tx-validated Yes 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 transaction payload is guaranteed. All earnings are final and billable to the Brand.
tx-declined Yes 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.

Please note – each time you call the Transactions API, you will receive the latest state of a given transaction. This means that if a transaction changes state multiple times between your API calls, you will not be able to see the historical changes; you will only see the latest state of the transaction based on when you called the API.


Ingestion Logic

There are a few fields you should be keying in on from the Transactions API payload, incorporating them into your ingestion and validation logic:

Field Description
order_attribution_date
  • The date that the order took place and is useful if you have time-based promotions
commerce_organization
  • The unique Brand Organization ID in Button’s namespace, which tells you which Brand the transaction relates to
  • This value will need to be reference-able via your CMS that houses Brand details/offers
button_order_id
  • Use this as the “transaction ID”
  • If an order is adjusted, cancelled, or finalized, another transaction will be available via the API; you will want to lookup the transaction ID on that transaction to see if you already have a record of that transaction in your system
  • If so, the order has either been adjusted, cancelled, or finalized, and your system should act accordingly (e.g. adjust / finalize user rewards, notify the user)
status
  • This is the status of the transaction.
  • It will first contain status=pending. This means that the order has taken place, but there’s still a chance the user cancels or adjusts the order
  • At some point in time later (based on the finalization window of the Brand), you will see a transaction with the same button_order_id that has one of three statuses:
    • status=pending – signifies the transaction has been adjusted
    • status=validated – signifies that the transaction is now final and immutable
    • status=declined – signifies that the transaction has been cancelled and is immutable
  • Ensure that logic exists for all state changes:
    • Pending to pending (i.e. an adjustment was made)
    • Pending to cancelled (i.e. order was cancelled)
    • Pending to finalized (i.e. order is finalized / cannot be changed)
publisher_customer_id and
pub_ref
  • These values are both pass-through-values that you passed to Button’s SDK (or into a Button Link), that we pass back to you so that you know which of your users made the purchase
  • In order to know which user an order was generated from, you’ll need to reference one or both of these fields
  • Be sure to let Button know which field you rely on to look up the user you will be rewarding
category
  • If the transaction is for a user purchase, this value will be either new-user-order or existing-user-order
  • If the transaction is for a Brand app installation, this value will be app-install
  • You cannot reward the user for app installs, so you should filter out any transactions with category = app-install and not include these transactions in the rest of your transaction ingestion validation logic
order_total
  • This is the total amount spent by the user
amount
  • This is the total commission earned by you, the Publisher
order_line_items
  • This array contains line item details comprise & sum-up to the total transaction.
  • order_line_items[].publisher_commission - the commission you earned for the specific line item purchased
    • Adding up all of the publisher_commission values across all of the line items will equate to the top-level amount value
  • order_line_items[].total - the order total for the specific line item