Skip to content

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 CaseWithout WebhooksWith Webhooks
Update order statusPoll the API every few seconds to check if payment completedReceive instant notification when transaction settles
Sync your databaseRun scheduled jobs to fetch recent transactionsUpdate records in real-time as events occur
Trigger fulfillmentDelay shipping until batch job confirms paymentShip immediately when you receive the approved webhook
Handle declinesCustomer waits while you poll for statusInstantly notify customer or retry with backup payment method
ReconciliationPull settlement reports manuallyReceive 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

  1. Enable webhooks in your merchant control panel: Manage → Settings → Webhooks
  2. Configure your endpoint to return HTTP 200 responses
  3. Verify signatures using your webhook's Signature UUID

Types

CategoryEvent Types
Transactiontransaction_create, transaction_update, transaction_void, transaction_capture, transaction_settlement
Settlementsettlement_batch
Card Synctransaction_automatic_account_updater_vault_update, transaction_automatic_account_updater_vault_iw
Testtest (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.

RequirementValue
Response code200 OK
Timeout5 seconds
Retry period24 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:

  1. Retrieve the Signature header from the request
  2. Base64 URL decode the signature
  3. Compute HMAC SHA-256 of the raw request body using your Signature UUID (found in the control panel)
  4. 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:

FieldDescription
statusRequest status (success)
msgStatus message
typeWebhook event type (see Webhook Types above)
dataEvent-specific payload

Status Values

For transaction webhooks, the data.status field indicates the transaction state:

StatusDescription
authorizedTransaction authorized, awaiting capture
pending_settlementCaptured, waiting for settlement
settledSuccessfully settled
declinedTransaction declined
voidedTransaction voided
refundedFully refunded
partially_refundedPartially refunded
returnedACH return
late_returnLate ACH return
pendingAwaiting processor response
flaggedFlagged for review
flagged_partnerFlagged by partner
unknownUnknown status

Invoice & Subscription Transactions

  • Invoices: The invoice ID appears in both order_id and po_number fields
  • Subscriptions: The subscription ID appears in the subscription_id field

Examples

Transaction

Headers

http
POST /your-webhook-endpoint HTTP/1.1
Content-Type: application/json
Signature: Fs62H-ZaZH80Ccf-Ii9V4xC9AJLf8dQym7BFAbApJwc

Payload

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

IssueSolution
Webhook disabled automaticallyEnsure your endpoint returns HTTP 200 within 5 seconds
Signature verification failingCheck for trailing newline (\n) in request body; verify you're using the correct Signature UUID
Not receiving webhooksVerify webhook is enabled in control panel and endpoint passed the test request
Duplicate webhooksImplement idempotency using the transaction id field