Skip to main content
Use the event index to choose your subscriptions, then follow the payload link for the resource you handle. Every delivery uses the same event envelope and headers.

Supported HOP Events

Subscribe through Create Webhook Endpoint or Update Webhook Endpoint. These events are emitted today: A trading-account payout rejected at creation for INSUFFICIENT_FUNDS or CREDIT_LIMIT_EXCEEDED emits payout.failed without a preceding payout.created. Match data.id to error.details.payout_id from the create response.

Reserved event names

The API accepts these subscriptions, but neither event is emitted today:

Unsupported event names

Any other event name is rejected with 422 Unprocessable Entity when you create or update an endpoint. Withdrawals raise payout.* events; withdraw.* is not an event family.

Event Envelope

Every delivery is an HTTP POST whose JSON body follows this envelope:

Envelope fields

Delivery headers

The request also includes these headers: See Webhook Security before accepting a delivery.

Payin Payload

Event: payin.received data is the payin object exactly as the Payins API returns it, with every field present. Fields that do not apply are null, not omitted.
  • Status: pending, completed, failed, or voided.
  • Credited balance: deposit_destination is {"type": "trading_account"} or {"type": "virtual_account", "virtual_account_id": "va_…"}.
  • Identifiers: id, object, account_id.
  • Amount: amount, currency, amount_in_usd.
  • State: status, payin_type.
  • Deposit: deposit_destination, payment_rail, note.
  • Crypto: network, txn_hash, destination_wallet_id, destination_address, source_address.
  • Sender: sender_name, sender_account_masked, sender_bank_code, sender_country.
  • Lifecycle: initiated_at, completed_at, failed_at, failed_reason, voided_at, voided_reason.
  • Audit: created, updated, created_by, updated_by.

Payout Payload

Events: payout.created, payout.completed, payout.failed These events cover both payouts and withdrawals. data.funding_source.type tells them apart.
  • Resource shape: data is the expanded payout object, matching an item in List Payouts. Get Payout returns a different shape.
  • Nested resources: beneficiary and payout_destination are included. Each is null when the payout was created without that stored resource.
  • Account context: account_id appears only in the envelope, never inside data.
  • Status: pending_approval, pending, processing, completed, failed, cancelled, returned, or voided. A payout.failed delivery has data.status of failed or cancelled.
A payout that the receiving bank later returns has no event of its own, and Hop emits no webhook for it. Detect returns by reading the payout’s status through the Payouts API.
  • Identifiers: id, object.
  • Recipient: beneficiary, payout_destination.
  • Amount and fee: amount, currency, amount_in_usd, platform_fee.
  • Funding: funding_source.
  • State: status, payout_type.
  • Tracking: tracking_details (uetr, imad, omad, trace_number), the compatibility field uetr, network, txn_hash, note.
  • Lifecycle: initiated_at, submitted_at, completed_at, failed_at, failed_reason, voided_at, voided_reason.
  • Audit: created, updated, created_by, updated_by.

Transfer Payload

Event: transfer.completed data is the transfer object. Its id starts with trf_. Each leg (source and destination) is {"type": "trading_account"} or {"type": "virtual_account", "virtual_account_id": "va_…"}.
  • Identifiers: id, object, account_id.
  • Balances: source, destination.
  • Amount: amount, currency.
  • State: status.
  • References: reference, internal_note.
  • Audit: created, updated, created_by, updated_by.

Account Payload

Events: account.created, account.updated
  • Creation: account.created covers the default account Hop provisions when your customer is approved and every end-customer account a platform creates through the API. The latter echoes your own request, so a service that only listens to webhooks still sees every account; match it to your request by data.id.
  • Updates: account.updated is sent only when Hop changes the account’s status. Your own API updates and changes to other fields do not emit it.
  • Identity and balances: legal_name, business_registration_number, address, created_by, and updated_by are always null. Read identity through the Accounts API. balances is always an empty array.
  • Identifier and status: id starts with acct_; status is active, suspended, or deactivated.
  • Identifiers and name: id, object, customer_id, name.
  • State: status.
  • Identity: legal_name, business_registration_number, address.
  • Balances: balances.
  • Audit: created, updated, created_by, updated_by.

Payout Destination Payload

Event: payout_destination.updated data is the payout destination exactly as the Payout Destinations API returns it, so account numbers and wallet addresses stay masked. Fields that do not apply are null, not omitted. The event fires on every change Hop makes to a destination, wallet or bank:
  • Hop approves a wallet: status moves from pending to active, and payouts to it can be created from then on.
  • Hop rejects a wallet: status moves from pending to disabled, and the wallet cannot be paid.
  • Hop corrects a destination’s details or status.
Each delivery carries the destination as it stands after the change. Creating a destination does not emit the event, and neither do your own changes through the API (update, disable, delete). A payout to a destination that is not active is rejected with 422 PAYOUT_DESTINATION_INACTIVE.

Virtual Account Payload

Events: virtual_account.created, virtual_account.updated data is the virtual account exactly as Get Virtual Account returns it, with balances as they stood when the event was sent.
  • Creation: virtual_account.created is sent for every virtual account opened on your accounts. One you open through the API echoes your own request; one Hop opens for you is announced only here. It is always pending with every bank detail null, so do not publish it as funding instructions.
  • Updates: virtual_account.updated is sent when the virtual account’s status changes. pending to active means the bank details have been issued and provisioned_at is set: share them with payers from then on. active to frozen and back means Hop blocked or released balance movements, and disabled means Hop closed the virtual account. Balance changes do not emit it (use payin.* and payout.*), and neither do label changes you make.
  • Order and duplicates: deliveries can repeat or arrive out of order. Each one carries the virtual account as it stood when the event was sent, so keep the one with the latest updated for each data.id, and read the virtual account when in doubt.

Virtual Account Group Payload

Events: virtual_account_group.created, virtual_account_group.updated data is the group exactly as Get Virtual Account Group returns it, including requirements and the agreements summary.
  • Creation: virtual_account_group.created is sent for every group opened on your accounts. A group you open through the API echoes your own request; a group Hop opens for you is announced only here. Match it by data.id.
  • Updates: virtual_account_group.updated is sent whenever the group’s status, onboarding_status or requirements change: onboarding needs more information (onboarding_status is action_needed and requirements lists what to provide), moves to review, is approved or rejected, or Hop enables or disables the group. Your own changes, such as updating the account, uploading a document or accepting agreements, emit it once they change the group’s status or requirements.
  • Order and duplicates: deliveries can repeat or arrive out of order. Each one carries the group as it stood when the event was sent, so keep the one with the latest updated for each data.id, and read the group when in doubt.
  • First events: opening a group usually sends virtual_account_group.created followed shortly by virtual_account_group.updated as the first requirements arrive.