Appearance
Webhooks
Webhooks are automated HTTP POST requests that we send to your server whenever specific events occur—like a transaction being approved, a batch settling, or card details being updated.
Instead of your application repeatedly asking "did anything change?" (polling), webhooks flip the model: We tell you when something happens. Your server receives the event data, processes it, and responds with a simple 200 OK.
Benefits
| Use Case | Without Webhooks | With Webhooks |
|---|---|---|
| Update order status | Poll the API every few seconds to check if payment completed | Receive instant notification when transaction settles |
| Sync your database | Run scheduled jobs to fetch recent transactions | Update records in real-time as events occur |
| Trigger fulfillment | Delay shipping until batch job confirms payment | Ship immediately when you receive the approved webhook |
| Handle declines | Customer waits while you poll for status | Instantly notify customer or retry with backup payment method |
| Reconciliation | Pull settlement reports manually | Receive batch settlement data automatically each day |
Webhooks reduce latency, API calls, and complexity—your integration reacts to events as they happen rather than constantly checking for changes.
Quick Start
- Enable webhooks in your merchant control panel: Manage → Settings → Webhooks
- Configure your endpoint to return HTTP 200 responses
- Verify signatures using your webhook's Signature UUID
Types
| Category | Event Types |
|---|---|
| Transaction | transaction_create, transaction_update, transaction_void, transaction_capture, transaction_settlement |
| Settlement | settlement_batch |
| Card Sync | transaction_automatic_account_updater_vault_update, transaction_automatic_account_updater_vault_iw |
| Test | test (used for endpoint validation) |
Endpoint
Important
Your webhook endpoint MUST return a HTTP 200 response to acknowledge successful receipt of the webhook. Failure to return a 200 response will result in the webhook being disabled after repeated failures.
| Requirement | Value |
|---|---|
| Response code | 200 OK |
| Timeout | 5 seconds |
| Retry period | 24 hours (5-minute exponential backoff) |
Webhook notifications are processed within a few seconds of a transaction being processed as long as the response code is within the Approved or Declined range.
Control Panel Requirement
The control panel requires a successful 200 response test before a webhook can be created or updated. This ensures your endpoint is properly configured.
Test webhooks include "type": "test" in the payload to distinguish them from production notifications.
Test
json
{
"status": "success",
"msg": "success",
"type": "test",
"account_type": "merchant",
"account_uid": "your-merchant-id"
}Signatures
Every webhook request includes a Signature header for security verification. The signature is HMAC SHA-256 signed and Base64 URL encoded.
To verify:
- Retrieve the
Signatureheader from the request - Base64 URL decode the signature
- Compute HMAC SHA-256 of the raw request body using your Signature UUID (found in the control panel)
- Compare the signatures
Important Note About Line Feeds
The request body may include a trailing linefeed character (\n). If your signature doesn't match, check whether you need to include this character when computing the hash.
go
package main
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"fmt"
"os"
)
// testSignatureBase64 is an example Base64 RFC4648 encoded signature you would
// receive in the header of the webhook request.
const testSignatureBase64 = "JacUiw_ztpEZJWvOhhKoHTLBf4b-aZv9n_0YmJJxltc"
// clientSecret is an example secret specific to you, which we use to sign
// requests.
const clientSecret = "12345678-1234-1234-1234-123456789012"
func main() {
// Set example HTTP request body.
// NOTE: The body may include a trailing linefeed character ('\n').
body := `{"data":"this is test data"}`
// Hash body to create signature.
hash := hmac.New(sha256.New, []byte(clientSecret))
_, err := hash.Write([]byte(body))
if err != nil {
fmt.Println(err)
os.Exit(1)
}
signature := hash.Sum(nil)
// Base64 decode test signature.
testSignature, err := base64.RawURLEncoding.DecodeString(testSignatureBase64)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Compare signatures.
if !bytes.Equal(signature, testSignature) {
fmt.Println("Signatures do not match")
os.Exit(0)
}
fmt.Println("Signatures match")
}Payload
Fields
All webhook payloads include these top-level fields:
| Field | Description |
|---|---|
status | Request status (success) |
msg | Status message |
type | Webhook event type (see Webhook Types above) |
data | Event-specific payload |
Status Values
For transaction webhooks, the data.status field indicates the transaction state:
| Status | Description |
|---|---|
authorized | Transaction authorized, awaiting capture |
pending_settlement | Captured, waiting for settlement |
settled | Successfully settled |
declined | Transaction declined |
voided | Transaction voided |
refunded | Fully refunded |
partially_refunded | Partially refunded |
returned | ACH return |
late_return | Late ACH return |
pending | Awaiting processor response |
flagged | Flagged for review |
flagged_partner | Flagged by partner |
unknown | Unknown status |
Invoice & Subscription Transactions
- Invoices: The invoice ID appears in both
order_idandpo_numberfields - Subscriptions: The subscription ID appears in the
subscription_idfield
Examples
Transaction
Headers
http
POST /your-webhook-endpoint HTTP/1.1
Content-Type: application/json
Signature: Fs62H-ZaZH80Ccf-Ii9V4xC9AJLf8dQym7BFAbApJwcPayload
json
{
"status": "success",
"msg": "success",
"type": "transaction_create",
"data": {
"id": "bm5s8gm9ku6ejcu15t9g",
"type": "sale",
"status": "pending_settlement",
"amount": 450,
"amount_authorized": 450,
"amount_captured": 450,
"amount_settled": 0,
"currency": "usd",
"payment_method": "card",
"payment_type": "card",
"response": "approved",
"response_code": 100,
"response_body": {
"card": {
"card_type": "visa",
"masked_card": "411111******1111",
"first_six": "411111",
"last_four": "1111",
"expiration_date": "12/20",
"auth_code": "TAS000",
"avs_response_code": "",
"cvv_response_code": "",
"processor_response_code": "00",
"processor_response_text": "APPROVAL TAS000"
}
},
"billing_address": {
"first_name": "",
"last_name": "",
"company": "",
"address_line_1": "",
"address_line_2": "",
"city": "",
"state": "",
"postal_code": "",
"country": "US",
"phone": "",
"email": ""
},
"shipping_address": {
"first_name": "",
"last_name": "",
"company": "",
"address_line_1": "",
"address_line_2": "",
"city": "",
"state": "",
"postal_code": "",
"country": "US",
"phone": "",
"email": ""
},
"processor_id": "bm5s7im9ku6el2qvgsrg",
"processor_name": "TSYS default",
"processor_type": "tsys_sierra",
"customer_id": "",
"subscription_id": "",
"order_id": "",
"po_number": "",
"ip_address": "127.0.0.1",
"transaction_source": "api",
"user_id": "testmerchant12345678",
"user_name": "test_merchant",
"created_at": "2019-09-25T19:47:14.001901427Z",
"captured_at": "2019-09-25T19:47:14.031268117Z",
"settled_at": null,
"updated_at": "2019-09-25T19:47:14.031268348Z"
}
}Account Updater
Sent when card-on-file data is updated via the automatic account updater service.
json
{
"status": "success",
"msg": "success",
"type": "transaction_automatic_account_updater_vault_update",
"account_type": "merchant",
"account_type_id": "testmerchant12345678",
"action_at": "2020-10-08T18:45:39.053107-05:00",
"data": {
"card_id": "btvq916vvhfmlmgnfdh0",
"record_id": "btvq916vvhfmlmgnfdhg",
"masked_number": "411111******1111",
"expiration_date": "02/25",
"instrument_type": "credit",
"status": "updated_card"
}
}Settlement Batch
Sent when a batch is settled with the processor.
json
{
"status": "success",
"msg": "success",
"type": "settlement_batch",
"account_type": "merchant",
"account_type_id": "testmerchant12345678",
"action_at": "2024-06-05T15:45:17.788695-05:00",
"data": {
"id": "cpgcsnbug2jm1i6kv4vg",
"batch_number": 1,
"batch_date": "2024-06-05T15:45:17.728045-05:00",
"num_transactions": 2,
"amount_captured": 1630,
"base_amount": 1630,
"net_amount": 1630,
"net_deposit": 1630,
"amount_credit": 0,
"surcharge_amount": 0,
"payment_adj_amount": 0,
"processor_id": "cpg94brug2jhhon86rsg",
"processor_name": "TSYS default",
"processor_type": "tsys_sierra",
"merchant_id": "testmerchant12345678",
"response_code": 100,
"response_message": "ACCEPT"
}
}Troubleshooting
| Issue | Solution |
|---|---|
| Webhook disabled automatically | Ensure your endpoint returns HTTP 200 within 5 seconds |
| Signature verification failing | Check for trailing newline (\n) in request body; verify you're using the correct Signature UUID |
| Not receiving webhooks | Verify webhook is enabled in control panel and endpoint passed the test request |
| Duplicate webhooks | Implement idempotency using the transaction id field |