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.
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.
$signature = hash_hmac(
'sha256',
"{$timestamp}.{$rawBody}",
$webhookSecret,
);
sha256=SIGNATURE
Webhook payload
Webhook requests contain a JSON payload describing the payment event and the related invoice.
{
"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.
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.
$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);
}