Webhook events notify your application when specific actions occur in your 1Money account. Your endpoint must respond with a 2xx status code within 10 seconds.
Webhook Events
Detailed onboarding-specific payload guidance is documented separately for Business Onboarding Webhooks and Individual Onboarding Webhooks.
| Category | Event Name | Description |
|---|---|---|
| Business Onboarding | account.business.ready | The business sub-account has passed compliance review and is ready for use. |
account.business.rejected | The business sub-account has been rejected during compliance review and cannot be activated. | |
account.business.under_review | The business sub-account is under manual compliance review. | |
account.business.rfi | Compliance review has identified issues that require partner action before the account can proceed. | |
| Individual Onboarding | account.individual.ready | The individual account has passed review and is ready for use. |
account.individual.rejected | The individual account has been rejected during review and cannot be activated. | |
account.individual.under_review | The individual account is under manual compliance review. | |
account.individual.rfi | Review has identified issues that require partner action before the account can proceed. | |
account.individual.reviewed | The individual account's reviewed tier or limits have changed. | |
| Deposits | deposit.pending | Deposit transaction is pending |
deposit.completed | Deposit successfully credited to account | |
deposit.failed | Deposit transaction failed | |
deposit.reversed | Completed deposit was reversed | |
| Crypto Deposit Address | deposit_instruction.ready | Deposit wallet address provisioned and ready for use |
deposit_instruction.failed | Deposit wallet address provisioning failed | |
| Withdrawals | withdrawal.completed | Withdrawal successfully processed |
withdrawal.failed | Withdrawal transaction failed | |
withdrawal.returned | Withdrawal was returned by the receiving bank | |
| Internal Transfers | transfer.completed | Internal transfer completed successfully |
| Recipients | recipient.approved | Recipient has been approved by compliance review |
recipient.rejected | Recipient has been rejected by compliance review | |
| External Accounts | external_account.approved | External account verified and approved |
external_account.failed | External account verification failed | |
| Conversions | conversion.completed | Conversion executed successfully |
conversion.failed | Conversion transaction failed | |
| Auto-Conversion Rules | auto_conversion_rule.create | Auto-conversion rule creation result (SUCCESS or FAILED) |
auto_conversion_rule_order.completed | Full conversion flow completed | |
auto_conversion_rule_order.deposit_failed | Failed at deposit stage | |
auto_conversion_rule_order.conversion_failed | Failed at conversion stage | |
auto_conversion_rule_order.withdrawal_failed | Failed at withdrawal stage |
HTTP Headers
All webhook requests include the following headers:
| Header | Description | Example |
|---|---|---|
Content-Type | Request content type | application/json |
X-Webhook-Signature | HMAC-SHA256 signature | a1b2c3d4e5f6... (64 chars) |
X-Webhook-Timestamp | Unix timestamp (seconds) | 1705920000 |
X-Webhook-Event-Id | Unique event ID | 12345 |
X-Webhook-Event-Name | Event type | deposit.completed |
X-Webhook-Delivery-Id | Delivery attempt ID | 67890 |
Signature Verification
To verify webhook authenticity, compute the HMAC-SHA256 signature and compare it with X-Webhook-Signature.
Algorithm
signed_payload = "{timestamp}.{request_body}"
expected_signature = HMAC-SHA256(signed_payload, webhook_secret)
Verification Steps
- Extract
X-Webhook-SignatureandX-Webhook-Timestampfrom headers - Concatenate:
{timestamp}.{raw_request_body} - Compute HMAC-SHA256 using your webhook secret
- Compare signatures using constant-time comparison
- Reject if timestamp is older than 5 minutes (replay attack prevention)
Code Example (Node.js)
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, timestamp, secret) {
// Check timestamp (5 minute tolerance)
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - timestamp) > 300) {
return false;
}
// Compute expected signature
const signedPayload = `${timestamp}.${payload}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// Constant-time comparison
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}Code Example (Python)
import hmac
import hashlib
import time
def verify_webhook_signature(payload: str, signature: str, timestamp: int, secret: str) -> bool:
# Check timestamp (5 minute tolerance)
now = int(time.time())
if abs(now - timestamp) > 300:
return False
# Compute expected signature
signed_payload = f"{timestamp}.{payload}"
expected_signature = hmac.new(
secret.encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()
# Constant-time comparison
return hmac.compare_digest(signature, expected_signature)Payload Structure
All webhook payloads follow this structure:
{
"event_name": "event.type",
"resource": {
"type": "resource_type",
"id": "resource_id",
"idempotency_key": "client_passed_idempotency_key"
},
"data": {
// Event-specific data
}
}Retry Policy
If your endpoint does not respond with a 2xx status code, we will retry the webhook delivery:
| Retry | Delay |
|---|---|
| 1st | ~30 minutes |
| 2nd | ~1 hour |
| 3rd | ~2 hours |
| 4th | ~4 hours |
| 5th | ~8 hours |
After 5 failed attempts (approximately 15.5 hours total), the webhook is marked as permanently failed
Best Practices
- Respond quickly - Return
200 OKimmediately, process asynchronously - Verify signatures - Always validate
X-Webhook-Signaturebefore processing - Handle duplicates - Use
X-Webhook-Event-Idfor idempotency - Use HTTPS - Webhook endpoints must use HTTPS
- Log deliveries - Track
X-Webhook-Delivery-Idfor debugging
