Webhook
Webhook notifications allow you to receive real-time updates about payment transaction status changes. When a payment's status changes (e.g., from pending to authorised, or from authorised to waiting), Hello Clever will send a POST request to your specified webhook endpoint.
Webhook Object
When a payment status changes, you'll receive a webhook notification with the following object structure:
| Field | Type | Description |
|---|---|---|
uuid | string | Unique identifier for the payment transaction |
name | string | Customer's name |
email | string | Customer's email address |
external_id | string | Your reference ID for this payment |
status | string | Current status of the payment |
pay_code | object | When 3DS is required, contains 3ds_url. When the payment failed, contains error_code and error_message. |
└─ 3ds_url | string | URL for the customer to complete 3DS authentication (when required) |
└─ error_code | string | Error or decline code (e.g. issuer_declined) |
└─ error_message | string | Error or decline message |
currency | string | Transaction currency code |
amount | string | Original payment amount |
total | string | Total amount including any fees |
paid_amount | string | Amount that has been paid |
is_refundable | boolean | Whether the payment can be refunded |
payment_method | string | Payment method used (e.g. "card") |
expired_at | string | The timestamp indicating when the payment url expires (UTC) |
webhook_notification | object | Webhook configuration details |
└─ endpoint_url | string | URL where webhooks will be sent |
└─ authorization_header | string | Authorization header for webhook |
refund_information | object | Details about any refunds |
└─ total_amount | string | Total amount available for refund |
└─ refund_amount | string | Amount that was refunded |
└─ description | string | Reason for the refund |
sender_details | object | Payment method details |
└─ card | object | Card payment details |
└─── card_type | string | Type of card used |
└─── card_brand | string | Card brand (visa, mastercard etc) |
└─── card_last_4 | string | Last 4 digits of card number |
created_at | string | Timestamp when payment was created |
token | object | Card token details |
└─ id | string | Token for the card |
└─ type | string | Token type (e.g. "card") |
Example:
{
"uuid": "QVABPPC7",
"name": "Hello Clever",
"email": "[email protected]",
"external_id": "123",
"status": "authorised",
"pay_code": {
"3ds_url": "https://3ds-example.com"
},
"currency": "USD",
"amount": "100.0",
"total": "100.0",
"paid_amount": "0.0",
"is_refundable": false,
"payment_method": "card",
"expired_at": "",
"webhook_notification": {
"endpoint_url": "https://webhook.site/12da7803-c4cf-4f32-812d-aaeaecf20d9d",
"authorization_header": "****"
},
"refund_information": {
"total_amount": "250.0",
"refund_amount": "10.0",
"description": "Testing refund"
},
"sender_details": {
"card": {
"card_type": "card",
"card_brand": "visa",
"card_last_4": "1111",
"card_country": "US"
}
},
"created_at": "2025-05-30T05:11:17.602+0000",
"token": {
"id": "tok_dfe1988a1ffc0d6562d3",
"type": "card"
}
}
Error codes and messages
When a payment failed, pay_code includes error_code and error_message. Reference table:
| Error code | Message |
|---|---|
account_closed | The customer's bank account has been closed. |
amount_invalid | The payment amount is invalid, or exceeds the amount that's allowed. |
amount_too_large | The specified amount is greater than the maximum amount allowed. Use a lower amount and try again. |
amount_too_small | The specified amount is less than the minimum amount allowed. Use a higher amount and try again. |
authentication_expired | The card authorisation has expired. |
authentication_failed | The payment can't be authorised. |
authentication_required | The card was declined because the transaction requires authentication such as 3D Secure. |
capture_not_authorised | Transaction must be in 'authorised' status before it can be captured. |
card_decline_rate_limit_exceeded | This card has been declined too many times. You can try to charge this card again after 24 hours. We suggest reaching out to your customer to make sure they've entered all of their information correctly and that there are no issues with their card. |
card_expired | The card has expired. |
card_lost | The payment was declined because the card is reported lost. |
card_not_supported | The card does not support this type of purchase. |
card_number_incorrect | The card number is incorrect. |
card_restricted | The customer can't use this card to make this payment (it's possible it was reported lost or stolen). |
card_stolen | The payment was declined because the card is reported stolen. |
card_velocity_exceeded | The customer has exceeded the balance, credit limit, or transaction amount limit available on their card. |
currency_not_supported | The card does not support the specified currency. |
customer_canceled | The customer has stopped the payment with their bank. Contact them for details and to arrange payment. |
cvc_incorrect | The CVC number is incorrect. |
debit_not_authorised | The customer has notified their bank that this payment was unauthorised. |
expiry_month_invalid | The expiration month is invalid. |
expiry_year_invalid | The expiration year is invalid. |
fraudulent | The payment was declined because we suspects that it's fraudulent. |
generic_declined | The card was declined for an unknown reason or blocked the payment. |
insufficient_funds | The card has insufficient funds to complete the purchase. |
issuer_declined | The card was declined for an unknown reason. |
issuer_not_available | The card issuer couldn't be reached, so the payment couldn't be authorised. |
pin_required | The card was declined because it requires a PIN. |
pin_try_exceeded | The allowable number of PIN tries was exceeded. |
processing_error | An error occurred while processing the card. Try again later or with a different payment method. |
return_url_invalid | Return URL is invalid. |
transaction_is_blocked | Transaction is blocked. If you paid, we will refund you soon. |
transaction_duplicated | A recent transaction with identical details was submitted recently. |
withdrawal_count_limit_exceeded | The customer has exceeded the balance or credit limit available on their card. |
Token for Authorised Cards
When a payment reaches the authorised or waiting status for card payment, we will additonal a token object in the webhook notification. You should be save this token and can be used it to create subsequent payments for the same customer without requiring re-authorisation of their card.
Note: For security reasons, the token object is only included in the webhook notification when the payment status changes to authorised or waiting. It will not be included in webhook notifications for other status changes.
The token object has this structure:
{
...,
"token": {
"id": "tok_773085396b86562040f4",
"type": "card"
}
}
Setting Up Webhooks
For security reasons, when using our SDK Integration, you need to contact us to provide your default webhook information to receive notifications. However, when using API Create Payment via Tokenisation, if you specify a different webhook URL, we will send notifications to that URL instead of the default webhook. You can do this by including the webhook_notification object in your payment creation request:
{
"webhook_notification": {
"endpoint_url": "https://your-domain.com/webhook",
"authorization_header": "Bearer your-secret-token" // Optional
}
}
Webhook Security
For enhanced security, you can include an authorization_header in your webhook configuration. This header will be sent with each webhook request, allowing you to verify the authenticity of the notifications.
Status Changes
Webhook notifications are sent when a payment's status changes to any of the following:
pending: Customer started a new payment but hasn't proceeded yet.authorised: Payment has been authorised, ready to be captured.waiting: Payment has been approved, awaiting funds to settle.received: Funds have been received.expired: Payment session expired before completion.return_pending: Refund request initiated, awaiting processing.return_expired: The refund request has expired. Expiration time is set to be 10 days.partially_refunded: Partial refund has been issued to the customer.return_received: Full amount has been refunded to the customer.return_rejected: Refund request was denied. The system will not retry again.failed: Payment failed due to an error or decline.in_dispute: Customer has raised a dispute, under review.dispute_lost: Dispute has been resolved in customer's favour The disputed amount is not returned to the merchant.
Note: When a payment status is
in_dispute, you can contact our support team to provide evidence for the dispute resolution process.
Best Practices
- Always verify the webhook signature or authorization header to ensure the request is legitimate
- Implement idempotency in your webhook handler to prevent duplicate processing
- Respond with a 200 OK status code as soon as you receive the webhook
- Process the webhook data asynchronously to avoid timeout issues
- Keep your webhook endpoint URL secure and private
Testing Webhooks
You can test webhook notifications using tools like webhook.site or by setting up a local endpoint using tools like ngrok.
Error Handling
If the target endpoint does not return HTTP 200, Hello Clever will retry the webhook call 3 times with 15 minutes delay per call.