Skip to main content

Overview

Webhooks notify your application when events occur in your HopNow account. This page documents all available event types and their payload structures.

Event Structure

All webhook events follow this structure:
{
  "id": "evt_1234567890abcdef",
  "type": "payout.completed",
  "created": "2024-01-15T10:00:00Z",
  "data": {
    // Event-specific data
  }
}

Account Events

account.created

Triggered when a new account is created.
{
  "id": "evt_123",
  "type": "account.created",
  "created": "2024-01-15T10:00:00Z",
  "data": {
    "id": "acc_1234567890abcdef",
    "customer_id": "cus_1234567890abcdef",
    "status": "active",
    "created": "2024-01-15T10:00:00Z"
  }
}

account.updated

Triggered when account details are updated.

account.deleted

Triggered when an account is deactivated.

Payout Events

payout.created

Triggered when a payout is initiated.
{
  "id": "evt_456",
  "type": "payout.created",
  "created": "2024-01-15T10:00:00Z",
  "data": {
    "id": "pout_1234567890abcdef",
    "account_id": "acc_1234567890abcdef",
    "beneficiary_id": "ben_1234567890abcdef",
    "amount": "100.00",
    "currency": "USD",
    "status": "pending",
    "created": "2024-01-15T10:00:00Z"
  }
}

payout.processing

Triggered when a payout begins processing.

payout.completed

Triggered when a payout is successfully delivered.
{
  "id": "evt_789",
  "type": "payout.completed",
  "created": "2024-01-17T09:30:00Z",
  "data": {
    "id": "pout_1234567890abcdef",
    "account_id": "acc_1234567890abcdef",
    "amount": "100.00",
    "currency": "USD",
    "status": "completed",
    "completed_at": "2024-01-17T09:30:00Z"
  }
}

payout.failed

Triggered when a payout fails.
{
  "id": "evt_101",
  "type": "payout.failed",
  "created": "2024-01-17T10:00:00Z",
  "data": {
    "id": "pout_1234567890abcdef",
    "status": "failed",
    "error": {
      "code": "insufficient_funds",
      "message": "Insufficient balance"
    }
  }
}

Payin Events

payin.created

Triggered when an incoming payment is initiated.

payin.processing

Triggered when a payin begins processing.

payin.completed

Triggered when funds are credited to the account.
{
  "id": "evt_202",
  "type": "payin.completed",
  "created": "2024-01-15T10:05:00Z",
  "data": {
    "id": "pin_1234567890abcdef",
    "account_id": "acc_1234567890abcdef",
    "amount": "1000.00",
    "currency": "USD",
    "status": "completed",
    "payment_method": "virtual_account",
    "completed_at": "2024-01-15T10:05:00Z"
  }
}

payin.failed

Triggered when a payin fails.

FX Events

fx.quote.created

Triggered when an FX quote is requested.

fx.quote.expired

Triggered when an FX quote expires without execution.

fx.trade.completed

Triggered when currency conversion is executed.
{
  "id": "evt_303",
  "type": "fx.trade.completed",
  "created": "2024-01-15T10:00:30Z",
  "data": {
    "id": "fxt_1234567890abcdef",
    "account_id": "acc_1234567890abcdef",
    "from_currency": "USD",
    "to_currency": "EUR",
    "source_amount": "1000.00",
    "target_amount": "920.50",
    "exchange_rate": "0.9205"
  }
}

Beneficiary Events

beneficiary.created

Triggered when a beneficiary is added.

beneficiary.updated

Triggered when beneficiary details are updated.

beneficiary.deleted

Triggered when a beneficiary is removed.

beneficiary.disabled

Triggered when a beneficiary is disabled.

Virtual Account Events

virtual_account.created

Triggered when a virtual account is created.

virtual_account.deleted

Triggered when a virtual account is deactivated.

Wallet Events

wallet.created

Triggered when a crypto wallet is created.

wallet.deleted

Triggered when a wallet is deactivated.

Event Filtering

Subscribe to specific events when creating webhook endpoints:
create_webhook_endpoint(
    customer_id="cus_123",
    url="https://api.example.com/webhooks",
    events=[
        "payout.created",
        "payout.completed",
        "payout.failed",
        "payin.completed"
    ]
)

Event Handling Best Practices

1. Idempotency

Handle duplicate events gracefully:
def handle_webhook(event):
    # Check if already processed
    if is_event_processed(event["id"]):
        return {"status": "already_processed"}

    # Process event
    process_event(event)

    # Mark as processed
    mark_event_processed(event["id"])

    return {"status": "success"}

2. Event Ordering

Events may arrive out of order. Use timestamps to determine sequence:
def process_payout_event(event):
    payout_id = event["data"]["id"]
    event_time = event["created"]

    # Check if this is the latest event for this payout
    if is_newer_event_exists(payout_id, event_time):
        return  # Skip outdated event

    # Process the event
    update_payout_status(payout_id, event["data"]["status"])

3. Error Handling

Return 2xx status codes quickly, process asynchronously:
@app.post("/webhooks")
async def handle_webhook(request):
    # Verify signature first
    verify_webhook_signature(request)

    # Queue for async processing
    event = await request.json()
    queue.enqueue(process_webhook_event, event)

    # Return 200 immediately
    return {"status": "received"}

def process_webhook_event(event):
    # Process event asynchronously
    # Handle errors, retries, etc.
    pass

Event Retry Logic

If your endpoint returns non-2xx status, we’ll retry:
  • Retry 1: After 1 minute
  • Retry 2: After 5 minutes
  • Retry 3: After 15 minutes
  • Retry 4: After 1 hour
  • Retry 5: After 6 hours
After 5 failed attempts, the endpoint is automatically disabled.

Testing Webhooks

Use test mode to trigger events:
# Create test payout to trigger webhook
payout = create_payout(
    account_id="acc_test_123",
    beneficiary_id="ben_test_456",
    amount="10.00",
    currency="USD"
)

# This will trigger:
# 1. payout.created event
# 2. payout.processing event
# 3. payout.completed event (in test mode)