Skip to main content

Webhook Events

This page documents the various webhook events that can be triggered by the Payrix platform. Webhooks are used to notify your application when specific events occur.

Table of Contents

  1. Payment Success
  2. Merchant Onboarding
  3. Merchant Approval
  4. Documents Signed
  5. ACH Update
  6. Void from EasyPay
  7. Partial Refund
  8. Payment Request (Preauth)
  9. Pre-Authorization Update
  10. Capture Pre-Authorization

Payment Success

Event: payment.success

Description

This event is triggered when a payment is successfully processed.

Payload Example

{
    "event_type": "payment.success",
    "data": {
        "transaction_id": "txn_a7f0b5340a",
        "merchant_id": "m_xxxxxxxxxx",
        "amount": 1.00,
        "net_amount": 0.95,
        "fee_amount": 0.05,
        "order_id": "example-order-id",
        "last_four": "0043",
        "brand": "VISA",
        "link_id": ""
    }
}

Explanation

  • transaction_id: Unique identifier for the transaction.
  • merchant_id: Unique identifier for the merchant.
  • amount: Total amount of the payment.
  • net_amount: Amount received after fees.
  • fee_amount: Fee amount charged for the transaction.
  • order_id: Identifier for the associated order.
  • last_four: Last four digits of the card used.
  • brand: Card brand (e.g. VISA, Mastercard).
  • link_id: Associated pay link ID, if applicable.

Merchant Onboarding

Event: merchant.onboarding

Description

This event is triggered when a merchant completes the onboarding process.

Payload Example

{
    "event_type": "merchant.onboarding",
    "data": {
        "merchant_key": "example-merchant-api-key",
        "merchant_id": "m_xxxxxxxxxx",
        "business_name": "Example Business",
        "email": "merchant@example.com",
        "public_key": "example-public-key"
    }
}

Explanation

  • merchant_key: Unique key assigned to the merchant.
  • merchant_id: Unique identifier for the merchant.
  • business_name: Name of the merchant’s business.
  • email: Merchant’s contact email.
  • public_key: Public key associated with the merchant.

Merchant Approval

Event: merchant.approval

Description

This event is triggered when a merchant’s application is approved.

Payload Example

{
    "event_type": "merchant.approval",
    "data": {
        "merchant_id": "m_xxxxxxxxxx",
        "business_name": "Example Business",
        "email": "merchant@example.com",
        "company_id": "example-company-id"
    }
}

Explanation

  • merchant_id: Unique identifier for the merchant.
  • business_name: Name of the merchant’s business.
  • email: Merchant’s contact email.
  • company_id: Unique identifier for the associated company.

Testing Merchant Approval Webhooks in Sandbox

This guide walks through how to mock a merchant approval event in the sandbox environment to verify your webhook integration before going live.
The mock approval endpoint is sandbox-only and will return an error if called in production.

Step 1: Set Up a Webhook Receiver

If you don’t already have a server endpoint ready to receive webhooks, use webhook.site to get a temporary public URL that logs all incoming requests. Copy the unique URL it generates — you’ll use it in Step 2.

Step 2: Register Your Webhook URL

Register the URL where Fractal should send webhook events. This replaces any previously registered URL for your client account.
curl -X POST https://testapi.fractalpay.com/api/v1/client/update-webhooks-url \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://your-endpoint.com/webhook"
  }'
Response:
{
  "status": true,
  "message": "Webhook url updated successfully"
}

Step 3: Trigger a Mock Merchant Approval

Send a POST request with the merchant’s GUID as merchant_id. This fires a merchant.approval webhook to your registered URL immediately.
curl -X POST https://testapi.fractalpay.com/api/v1/approve-merchant \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "m_xxxxxxxxxx"
  }'
Response:
{
  "status": true,
  "message": "Webhook dispatch started"
}

Step 4: Verify the Webhook Payload

Your registered endpoint will receive a POST request with the following payload:
{
  "event_type": "merchant.approval",
  "data": {
    "merchant_id": "m_xxxxxxxxxx",
    "business_name": "Example Business",
    "email": "merchant@example.com",
    "company_id": "example-company-id"
  }
}
This matches the payload your integration will receive when a merchant’s application is approved.

Finding Your merchant_id

The merchant_id is the merchant’s GUID — not the API key. It can be found in the Fractal dashboard or from the response when the merchant was originally onboarded.

Documents Signed

Event: documents.signed

Description

This event is triggered when a merchant completes signing the required documents.

Payload Example

{
    "event_type": "documents.signed",
    "data": {
        "merchant_id": "m_xxxxxxxxxx",
        "business_name": "Example Business",
        "website": "https://example.com",
        "email": "merchant@example.com",
        "company_id": "example-company-id"
    }
}

Explanation

  • merchant_id: Unique identifier for the merchant.
  • business_name: Name of the merchant’s business.
  • website: Merchant’s website URL.
  • email: Merchant’s contact email.
  • company_id: Unique identifier for the associated company.

ACH Update

Event: ach.update

Description

This event is triggered when an ACH transaction status changes — either approved or declined.

Payload Example

{
  "event_type": "ach.update",
  "data": {
    "merchant_id": "m_xxxxxxxxxx",
    "status": "Approved",
    "tran_id": "txn_xxxxxxxx",
    "message": "Approved"
  }
}

Explanation

  • merchant_id: Unique identifier for the merchant.
  • status: Approved or Declined.
  • tran_id: The original ACH transaction ID.
  • message: Approved or Insufficient Funds.
  • return_transaction_id: ID of the reversal record — only present on Declined.

Testing ACH Update Webhooks in Sandbox

This guide walks through how to mock an ACH payment status update in the sandbox environment to verify your webhook integration.
The mock ACH update endpoint is sandbox-only and will return an error if called in production.

Prerequisites

Before testing you’ll need:
  • A merchant under your client account that has at least one ACH transaction in the system
  • Your webhook URL registered (see the Merchant Approval Webhook guide for setup steps — same endpoint)

Step 1: Register Your Webhook URL

If not already set up from a previous test:
curl -X POST https://testapi.fractalpay.com/api/v1/client/update-webhooks-url \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://your-endpoint.com/webhook"
  }'

Step 2: Trigger a Mock ACH Approval

curl -X POST https://testapi.fractalpay.com/api/v1/update-ach \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_key": "your-merchant-api-key",
    "transaction_id": "txn_xxxxxxxx",
    "new_status": "approved"
  }'
Response:
{ "result": true, "message": "Success." }
Webhook payload received:
{
  "event_type": "ach.update",
  "data": {
    "merchant_id": "m_xxxxxxxxxx",
    "status": "Approved",
    "tran_id": "txn_xxxxxxxx",
    "message": "Approved"
  }
}

Step 3: Trigger a Mock ACH Decline

Same endpoint, new_status set to "declined":
curl -X POST https://testapi.fractalpay.com/api/v1/update-ach \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_key": "your-merchant-api-key",
    "transaction_id": "txn_xxxxxxxx",
    "new_status": "declined"
  }'
Webhook payload received:
{
  "event_type": "ach.update",
  "data": {
    "merchant_id": "m_xxxxxxxxxx",
    "status": "Declined",
    "tran_id": "txn_xxxxxxxx",
    "message": "Insufficient Funds",
    "return_transaction_id": "txn_yyyyyyyyyy"
  }
}
return_transaction_id is the ID of the reversal record created when an ACH is declined.

Finding a Valid transaction_id

The transaction_id must be the pos_guid of an existing ACH transaction for the merchant. Only approved and declined are valid values for new_status — anything else returns a 400.

Void from EasyPay

Event: payment.void

Description

This event is triggered when a transaction is successfully voided.

Payload Example

{
  "event_type": "payment.void",
  "data": {
    "merchant_id": "m_xxxxxxxx",
    "transaction_id": "txn_454e460495",
    "Status": "Success",
    "linked_txn_id": "txn_c3b9865e78",
    "sales_id": "txn_454e460495",
    "order_createdfrom": "API",
    "amount": 2,
    "txn_id": "txn_Y9tiwFkMbv9vcHHwAZVE",
    "txn_date": "2026-06-15T16:31:44.000Z",
    "type": "Void"
  }
}

Explanation

  • merchant_id: Unique identifier for the merchant.
  • transaction_id: ID of the void transaction record created.
  • Status: Success or Error.
  • linked_txn_id: ID of the original transaction that was voided.
  • sales_id: Same as transaction_id — the void record ID.
  • order_createdfrom: Source of the original order (e.g. API).
  • amount: Amount voided.
  • txn_id: Gateway-level transaction reference ID.
  • txn_date: Timestamp of the void.
  • type: Always Void.

Testing in Sandbox

Step 1: Register your webhook URL (skip if already done)

curl -X POST https://testapi.fractalpay.com/api/v1/client/update-webhooks-url \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://your-endpoint.com/webhook"
  }'

Step 2: Trigger a void

curl -X POST https://testapi.fractalpay.com/api/v1/order/sales-void \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_key": "your-merchant-api-key",
    "transaction_id": "txn_xxxxxxxx",
    "amount": "2.00"
  }'
Response:
{
  "result": true,
  "message": "Transaction voided successfully",
  "data": {
    "sales_id": "txn_454e460495",
    "order_createdfrom": "API",
    "amount": 2,
    "txn_id": "txn_Y9tiwFkMbv9vcHHwAZVE",
    "txn_date": "2026-06-15T16:31:44.000Z",
    "linked_txn_id": "txn_c3b9865e78",
    "transaction_id": "txn_454e460495",
    "Status": "Success",
    "type": "Void"
  }
}
The transaction must be voided before it settles. A settled transaction returns "The transaction has been settled, a void is not allowed." Use a transaction created the same day for sandbox testing.

Partial Refund

Event: payment.refund

Description

This event is triggered when a refund is successfully processed on a completed card transaction.

Payload Example

{
  "event_type": "payment.refund",
  "data": {
    "merchant_id": "m_xxxxxxxx",
    "transaction_id": "txn_a8f83189a5",
    "Status": "Success",
    "linked_txn_id": "txn_a8f83189a5",
    "parent_transaction_id": "txn_b65f37d127",
    "sales_id": "txn_a8f83189a5",
    "order_createdfrom": "API",
    "amount": 1,
    "txn_id": "txn_lyWexIP9z2MJkrBBtscY",
    "txn_date": "2026-06-15T19:13:03.000Z",
    "type": "Refund"
  }
}

Explanation

  • merchant_id: Unique identifier for the merchant.
  • transaction_id: ID of the new refund record created.
  • Status: Success.
  • linked_txn_id: Same as transaction_id — the refund record ID.
  • parent_transaction_id: The original transaction that was refunded.
  • sales_id: Same as transaction_id.
  • order_createdfrom: Source of the original order (e.g. API).
  • amount: Refund amount (positive decimal).
  • txn_id: Gateway-level transaction reference ID.
  • txn_date: ISO 8601 timestamp of the refund.
  • type: Always Refund.

How to Issue a Refund

Endpoint: POST /api/v1/order/sales-return Auth: Basic Auth (client_id:secret_key)

Request Parameters

FieldTypeRequiredDescription
merchant_keystringYesMerchant API key
transaction_idstringYespos_guid of the original completed transaction
amountstringYesAmount to refund — must not exceed the original or remaining refundable balance

Step 1: Register your webhook URL (skip if already done)

curl -X POST https://testapi.fractalpay.com/api/v1/client/update-webhooks-url \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://your-endpoint.com/webhook"
  }'
Response:
{
  "status": true,
  "message": "Webhook url updated successfully"
}

Step 2: Issue the refund

curl -X POST https://testapi.fractalpay.com/api/v1/order/sales-return \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_key": "your-merchant-api-key",
    "transaction_id": "txn_xxxxxxxx",
    "amount": "1.00"
  }'
Response:
{
  "result": true,
  "message": "Transaction refunded successfully",
  "data": {
    "sales_id": "txn_a8f83189a5",
    "order_createdfrom": "API",
    "amount": 1,
    "txn_id": "txn_lyWexIP9z2MJkrBBtscY",
    "txn_date": "2026-06-15T19:13:03.000Z",
    "linked_txn_id": "txn_xxxxxxxx",
    "transaction_id": "txn_a8f83189a5",
    "Status": "Success",
    "type": "Refund"
  }
}

Step 3: Verify the webhook payload

Your registered endpoint will receive a POST request with the following payload:
{
  "event_type": "payment.refund",
  "data": {
    "merchant_id": "m_xxxxxxxx",
    "transaction_id": "txn_a8f83189a5",
    "Status": "Success",
    "linked_txn_id": "txn_a8f83189a5",
    "parent_transaction_id": "txn_xxxxxxxx",
    "sales_id": "txn_a8f83189a5",
    "order_createdfrom": "API",
    "amount": 1,
    "txn_id": "txn_lyWexIP9z2MJkrBBtscY",
    "txn_date": "2026-06-15T19:13:03.000Z",
    "type": "Refund"
  }
}
Use parent_transaction_id to trace back to the original charge.

Error responses

ScenarioMessage
Transaction not found or not completed"Payment method not found."
Amount exceeds original transaction"Amount should not be greater than sales charge amount."
Amount exceeds remaining refundable balance"The amount should not be greater than the remaining amount."
Transaction already fully refunded"Refund already done. No refund allowed."
Partial refunds can be issued multiple times against the same transaction as long as the cumulative total does not exceed the original charge amount. If the refund amount equals the full transaction amount and the transaction is older than 24 hours, the API automatically performs a void instead.

Payment Request (Preauth)

Event: preauth

Description

This event is triggered when a customer successfully completes a preauthorization via a payment request link. The customer’s card is authorized for the amount but not charged until you explicitly capture the preauth.

Flow

  1. Your server calls POST /api/v1/requests with preauth: true — returns a pay_link
  2. You send the pay_link to your customer (or the API sends it via SMS/email automatically)
  3. The customer visits the link and enters their card details to authorize
  4. On successful authorization → your registered webhook URL receives the preauth event

Payload Example

{
  "event_type": "preauth",
  "data": {
    "merchant_id": "m_f2d5caadab",
    "guid": "txn_b71af1a75f",
    "amount": 10,
    "order_id": "5467",
    "customer_id": 0
  }
}

Explanation

  • merchant_id: Unique identifier for the merchant.
  • guid: Transaction ID of the preauthorization record.
  • amount: Amount that was preauthorized.
  • order_id: The order reference you provided in the original request.
  • customer_id: Internal customer ID, or 0 if no customer profile was linked.

How to Create a Payment Request

Endpoint: POST /api/v1/requests Auth: Basic Auth (client_id:secret_key)

Request Parameters

FieldTypeRequiredDescription
merchant_keystringYesMerchant API key
amountstringYesPayment amount
order_idstringYesYour unique order reference (max 100 characters)
phone_numberstringNoCustomer phone — triggers SMS with pay link
emailstringNoCustomer email — triggers email with pay link
namestringNoCustomer name, used in the email template
preauthbooleanNoSet to true to issue a preauthorization instead of a charge
pass_feebooleanNoPass the processing fee to the customer
require_3dsbooleanNoRequire 3D Secure authentication
allow_cardbooleanNoAllow card payment (default: true)
allow_achbooleanNoAllow ACH payment (default: true)
invoice_numberstringNoInvoice reference (alphanumeric, max 100 characters)
sub_merchant_keystringNoSub-merchant key, if applicable

Step 1: Register your webhook URL (skip if already done)

curl -X POST https://testapi.fractalpay.com/api/v1/client/update-webhooks-url \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://your-endpoint.com/webhook"
  }'

Step 2: Create the payment request

curl -X POST https://testapi.fractalpay.com/api/v1/requests \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_key": "your-merchant-api-key",
    "amount": "10.00",
    "order_id": "5467",
    "phone_number": "7777771234",
    "email": "customer@example.com",
    "preauth": true,
    "pass_fee": true,
    "require_3ds": false,
    "invoice_number": "345",
    "allow_card": true,
    "allow_ach": true
  }'
Response:
{
  "message": "Authorization request has been sent",
  "data": {
    "order_id": "5467",
    "link_id": "gsgqll4xfa7",
    "pay_link": "https://testapi.fractalpay.com/authcheck-paymentlink/gsgqll4xfa7"
  }
}

Step 3: Customer completes preauth

Share the pay_link with your customer (or the API will send it automatically if phone_number or email was provided). When the customer authorizes, your webhook endpoint receives the preauth event.

Step 4: Verify the webhook payload

{
  "event_type": "preauth",
  "data": {
    "merchant_id": "m_xxxxxxxx",
    "guid": "txn_b71af1a75f",
    "amount": 10,
    "order_id": "5467",
    "customer_id": 0
  }
}
Use the guid to capture or void the preauth via the /preauth/capture or /preauth/void endpoints.
At least one of allow_card or allow_ach must be true. If phone_number or email is provided, the API automatically sends the pay link — you do not need to deliver it manually.
Partial refunds can be issued multiple times against the same transaction as long as the cumulative total does not exceed the original charge amount. If the refund amount equals the full transaction amount and the transaction is older than 24 hours, the API automatically performs a void instead.

Pre-Authorization Update

Events: preauth.increment / preauth.decrement

Description

These events are triggered when the authorized amount on an existing preauthorization is changed. The same endpoint handles both directions — the direction is determined by comparing new_authorized_amount to last_authorized_amount.
ConditionEvent fired
new_authorized_amount > last_authorized_amountpreauth.increment
new_authorized_amount < last_authorized_amountpreauth.decrement

Payload Example

{
  "event_type": "preauth.increment",
  "data": {
    "merchant_id": "m_f2d5caadab",
    "guid": "txn_f84144f73a",
    "amount": "11.00",
    "order_id": "5467",
    "customer_id": 0
  }
}
The preauth.decrement payload has the same shape — only event_type and amount differ.

Explanation

  • merchant_id: Unique identifier for the merchant.
  • guid: Transaction ID of the preauthorization record.
  • amount: The new authorized amount after the update.
  • order_id: The order reference associated with the preauth.
  • customer_id: Internal customer ID, or 0 if no customer profile was linked.

How to Update a Pre-Authorization

Endpoint: POST /api/v1/preauth/update Auth: Basic Auth (client_id:secret_key)

Request Parameters

FieldTypeRequiredDescription
merchant_keystringYesMerchant API key
transaction_idstringYesguid of the existing preauthorization
last_authorized_amountstringYesCurrent authorized amount — must match the amount on record
new_authorized_amountstringYesTarget authorized amount — determines increment or decrement
order_idstringNoYour order reference

Step 1: Register your webhook URL (skip if already done)

curl -X POST https://testapi.fractalpay.com/api/v1/client/update-webhooks-url \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://your-endpoint.com/webhook"
  }'

Step 2: Submit the update

Increment (raise the authorization):
curl -X POST https://testapi.fractalpay.com/api/v1/preauth/update \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_key": "your-merchant-api-key",
    "transaction_id": "txn_77c9a625a3",
    "last_authorized_amount": "10.00",
    "new_authorized_amount": "11.00",
    "order_id": "5467"
  }'
Decrement (partial reversal): same endpoint, new_authorized_amount less than last_authorized_amount. Response:
{
  "result": true,
  "message": "Amount Updated Successfully",
  "data": {
    "Status": "Approved",
    "Account": "0043",
    "Brand": "Visa",
    "AuthCode": "",
    "Authorized": "11.00",
    "transaction_id": "txn_77c9a625a3",
    "orderid": "5467"
  }
}

Step 3: Verify the webhook payload

{
  "event_type": "preauth.increment",
  "data": {
    "merchant_id": "m_xxxxxxxx",
    "guid": "txn_xxxxxxxx",
    "amount": "11.00",
    "order_id": "5467",
    "customer_id": 0
  }
}

Error responses

ScenarioMessage
last_authorized_amount doesn’t match current record"Amount mismatch"
Trying to increment an already-decremented auth"Authorization can only be decreased, captured, or voided."
Trying to decrement an already-incremented auth"Authorization can only be increased, captured, or voided."
Preauth not found"Authorization not found"
The last_authorized_amount must exactly match the current authorized amount stored on the preauthorization record. If the amount has changed since you last queried it, the request will return an "Amount mismatch" error.

Capture Pre-Authorization

Event: preauth.charge

Description

This event is triggered when a preauthorization is successfully captured — the customer’s card is charged for the authorized amount and the preauth is converted into a completed transaction.

Payload Example

{
  "event_type": "preauth.charge",
  "data": {
    "merchant_id": "m_f2d5caadab",
    "link_id": "txn_f84144f73a",
    "guid": "txn_77c9a625a3",
    "amount": "11.00",
    "order_id": "5467",
    "net_amount": 10.64
  }
}

Explanation

  • merchant_id: Unique identifier for the merchant.
  • link_id: The preauthorization transaction ID that was captured (the transaction_id you passed in the request).
  • guid: The resulting completed transaction ID created by the capture.
  • amount: Amount that was captured.
  • order_id: The order reference associated with the preauth.
  • net_amount: Amount received after fees.

How to Capture a Pre-Authorization

Endpoint: POST /api/v1/preauth/capture Auth: Basic Auth (client_id:secret_key)

Request Parameters

FieldTypeRequiredDescription
merchant_keystringYesMerchant API key
transaction_idstringYesguid of the preauthorization to capture

Step 1: Register your webhook URL (skip if already done)

curl -X POST https://testapi.fractalpay.com/api/v1/client/update-webhooks-url \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://your-endpoint.com/webhook"
  }'

Step 2: Capture the preauthorization

curl -X POST https://testapi.fractalpay.com/api/v1/preauth/capture \
  -u "{client_id}:{secret_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_key": "your-merchant-api-key",
    "transaction_id": "txn_f84144f73a"
  }'
Response:
{
  "result": true,
  "message": "Payment captured successfully.",
  "data": {
    "Status": "Approved",
    "Account": "0043",
    "Brand": "Visa",
    "AuthCode": "",
    "amount": "11.00",
    "Authorized": 11,
    "orderid": "5467",
    "transaction_id": "txn_77c9a625a3",
    "net_amount": 10.64
  }
}

Step 3: Verify the webhook payload

{
  "event_type": "preauth.charge",
  "data": {
    "merchant_id": "m_xxxxxxxx",
    "link_id": "txn_f84144f73a",
    "guid": "txn_77c9a625a3",
    "amount": "11.00",
    "order_id": "5467",
    "net_amount": 10.64
  }
}
Note that link_id is the preauth transaction_id you passed in — use it to correlate the capture back to the original preauth. guid is the new completed transaction ID.

Error responses

ScenarioMessage
Preauth not found"Authorization not found"
The last_authorized_amount must exactly match the current authorized amount stored on the preauthorization record. If the amount has changed since you last queried it, the request will return an "Amount mismatch" error.