Integration

Webhooks

Receive asynchronous payment events on your server and keep your application synchronized with payment status changes.

Overview

crptoX sends webhook requests to the URL configured for your merchant account when a subscribed payment event occurs.

Webhook delivery is performed asynchronously. If the destination returns a transient error, crptoX automatically retries the delivery according to its retry schedule.

Events

Subscribe to the payment events you want to receive. Events are generated when the corresponding payment request changes state.

payment.confirming

A detected payment has entered the confirmation process.

payment.paid

The payment has reached the required confirmation state and is marked as paid.

payment.underpaid

The detected payment amount is lower than the required invoice amount.

payment.overpaid

The detected payment amount is higher than the required invoice amount.

payment.expired

The payment request expired before a valid payment was completed.

Request headers

Each webhook request includes the following HTTP headers.

Content-Type application/json
Accept application/json
User-Agent crptoX-Webhook/1.0
X-crptoX-Timestamp Unix timestamp indicating when the webhook request was generated.
X-crptoX-Signature sha256=HMAC-SHA256 signature generated from the timestamp and the exact raw JSON request body.

Signature verification

Each webhook request is signed using your webhook secret with HMAC-SHA256. Verify the signature before processing the webhook payload.

The signature is generated using HMAC-SHA256 with your webhook secret as the signing key.

Signature input TEXT
TIMESTAMP.RAW_JSON_REQUEST_BODY

The signed message consists of the Unix timestamp, followed by a dot, followed by the exact raw JSON request body.

PHP PHP
$signature = hash_hmac(
'sha256',
"{$timestamp}.{$rawBody}",
$webhookSecret,


);
X-crptoX-Signature TEXT
sha256=SIGNATURE
Use the exact raw request body when verifying the signature. Do not decode, reformat, reorder, or re-encode the JSON before verification, as this changes the signed message.

Webhook payload

Webhook requests contain a JSON payload describing the payment event and the related invoice.

Example JSON
{


"id": "42a869de-03e2-4701-a938-423996ce3065",
"type": "payment",
"event": "paid",
"created_at": "2026-09-10T21:30:00.000000Z",
"payment": {
"id": "42a869de-03e2-4701-a938-423996ce3065",
"status": "paid",
"network": "tron",
"asset": "usdt",
"amount": "10.000000",
"expires_at": "2026-09-10T22:00:00.000000Z"
},
"invoice": {
"id": "e075d503-6d3e-4cf7-b2f2-a449a9c58dc9",
"status": "paid"
}
}

Payload fields

id

Unique payment request identifier.

type

The resource type. Currently this value is payment.

event

The payment event that triggered the webhook.

created_at

ISO 8601 timestamp indicating when the payment request was created.

payment

Payment request details, including status, network, asset, amount, and expiration time.

invoice

Related invoice information, including its unique identifier and current status.

Delivery and retries

Webhook deliveries are processed asynchronously. A 2xx response marks the delivery as successful. Connection failures, HTTP 408, HTTP 429, and HTTP 5xx responses are treated as transient failures and may be retried.

Initial delivery Immediately
First retry 30 seconds
Second retry 2 minutes
Third retry 10 minutes
Fourth retry 30 minutes

A delivery can be attempted up to five times in total. If all attempts fail, the delivery is marked as failed.

HTTP responses

The response returned by your webhook endpoint determines whether the delivery is considered successful or retried.

2xx The webhook is considered successfully delivered.
408 / 429 The delivery is retried after the configured delay.
5xx The delivery is retried after the configured delay.
Other 4xx The delivery is marked as failed and is not automatically retried.

Verify a webhook

Read the raw request body, build the signed message using the timestamp header, and compare the calculated signature with the X-crptoX-Signature header using a constant-time comparison.

PHP PHP
$timestamp = request()->header('X-crptoX-Timestamp');


$signature = request()->header('X-crptoX-Signature');
$rawBody = request()->getContent();

$expected = 'sha256=' . hash_hmac(
'sha256',
"{$timestamp}.{$rawBody}",
$webhookSecret,
);

if (! hash_equals($expected, $signature)) {
abort(401);
}