Webhooks
What are Webhooks
Section titled “What are Webhooks”Webhooks allow apps to receive real-time events from Totus. Webhooks are required to be implemented in order to receive fulfillment information and are included as a security mechanism to make sure the right app receives the card data and is not intercepted by a bad actor.
To help orient you with the content in this guide, the following definitions explain webhook concepts:
Webhook: A single event message. Totus sends a webhook to an app’s webhook subscription endpoint. A webhook contains a JSON payload in the body and metadata in the headers.
Webhook headers
Section titled “Webhook headers”The following headers are included in the webhook call.
X-TOTUS-Topic: `/fulfillment/complete`X-TOTUS-Hmac-Sha256: `XWmrwMey6OsLMeiZKwP4FppHH3cmAiiJJAweH5Jo4bM=`X-TOTUS-API-Version: `v1.0`X-TOTUS-RequestId: `b54557e4-bdd9-4b37-8a5f-bf7d70bcd043`The following header fields are used:
| Field | Usage |
|---|---|
| X-TOTUS-Topic | The type of webhook call being made |
| X-TOTUS-Hmac-Sha256 | A hash of the content in the webhook and the API key used to create the original order |
| X-TOTUS-API-Version | The version of the Totus API |
| X-TOTUS-RequestId | The request-id from the POST of the order |
The webhooks API provides “at least once” delivery of webhook events. This means that an endpoint might receive the same webhook event more than once. You can detect duplicate webhook events by comparing the X-TOTUS-RequestId header to previous events.
Verify the Webhook
Section titled “Verify the Webhook”Before you process the webhook, you need to verify that the webhook was sent from Totus. You can verify the webhook by calculating a digital signature.
Each webhook request includes a base64-encoded X-TOTUS-Hmac-Sha256 header, generated using the API Key and the data sent in the request.
# The following example uses Ruby and the Sinatra web framework to verify a webhook request:
require 'rubygems'require 'base64'require 'openssl'require 'sinatra'require 'active_support/security_utils'
API_KEY = 'my_API_KEY'
helpers do # Compare the computed HMAC digest based on the client secret and the request contents to the reported HMAC in the headers def verify_webhook(data, hmac_header) calculated_hmac = Base64.strict_encode64(OpenSSL::HMAC.digest('sha256', API_KEY, data)) ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, hmac_header) endend
# Respond to HTTP POST requests sent to this web servicepost '/' do request.body.rewind data = request.body.read verified = verify_webhook(data, env["HTTP_X_TOTUS_HMAC_SHA256"])
halt 401 unless verified
# Process webhook payload # ...end# The following example uses PHP to verify a webhook request:
<?php
define('API_KEY', 'my_api_key');
function verify_webhook($data, $hmac_header){ $calculated_hmac = base64_encode(hash_hmac('sha256', $data, API_KEY, true)); return hash_equals($hmac_header, $calculated_hmac);}
$hmac_header = $_SERVER['HTTP_X_TOTUS_HMAC_SHA256'];$data = file_get_contents('php://input');$verified = verify_webhook($data, $hmac_header);
error_log('Webhook verified: '.var_export($verified, true)); // Check error.log to see the result
if ($verified) { # Process webhook payload # ...} else { http_response_code(401);}
?># The following example uses Python and the Flask framework to verify a webhook request:
from flask import Flask, request, abortimport hmacimport hashlibimport base64
app = Flask(__name__)
API_KEY = 'my_api_key'
def verify_webhook(data, hmac_header): digest = hmac.new(API_KEY.encode('utf-8'), data, digestmod=hashlib.sha256).digest() computed_hmac = base64.b64encode(digest)
return hmac.compare_digest(computed_hmac, hmac_header.encode('utf-8'))
@app.route('/webhook', methods=['POST'])def handle_webhook(): data = request.get_data() verified = verify_webhook(data, request.headers.get('X-TOTUS-Hmac-SHA256'))
if not verified: abort(401)
# Process webhook payload # ...
return ('', 200)Webhooks in Production
Section titled “Webhooks in Production”Setting the webhooks on an order basis allows the webhooks to call into the development machines (on a per-developer basis) and add additional data to the path. The issue with adding the ability to add webhook URLs in the order, if the API key is compromised, then the information can be sent to the bad actor.
To mitigate this scenario the webhook call in production is limited to the root domain that is set up in the account. The RootURL needs to match the URL setup in your account; otherwise, the POST for the order will fail. This ensures that the right endpoint is called. Only the RootURL needs to match what is stored in the account. The rest of the path can be changed per POST order call.
https://[RootURL]/fulfillment/A33433?partnerid=123&partnerid2=94333Copyright © 2026 Totus.