Skip to main content
Webhooks push an event to your server when something happens on your account, such as a deposit landing, a payout settling or a card being charged. You register one HTTPS URL, and every event is sent to it.
The signature header is named Signature. It is not X-Yativo-Signature or X-Signature. Always verify it before you process an event.

Register your endpoint

Create or replace your webhook

string
required
The HTTPS endpoint that receives events.
  • One endpoint per account. Calling POST /business/webhook again replaces your URL and issues a new secret.
  • Save the secret. You need it to verify signatures.
  • Every event goes to your URL. There’s no per-event subscription, so ignore event types you don’t handle.
  • Right after you save a webhook, Yativo sends a webhook_updated test event to the new URL.

Other webhook endpoints


Verify the signature

Every request carries a Signature header:
Compute the HMAC over the exact raw request body. Don’t parse and re-serialize the JSON first, because whitespace or key order changes break the match. Compare with a constant-time function. Requests also carry a Timestamp header (Unix seconds). It isn’t part of the signature.
The signed body has no timestamp, so pair signature checks with deduplication.

Payload envelope

Every event uses this envelope:
event.type names the event. payload holds the event data. The envelope has no delivery ID or timestamp, so use fields inside payload, such as id, event_id, status and created_at, to deduplicate and order events.

The customer object

Every payload includes customer_id and a customer object, so you know whose event it is without another API call:
customer_type is individual or business. customer is null when the event isn’t about one of your customers, for example user.updated. Only these five fields are sent.

Event catalog

New event types are added over time. Ignore ones you don’t recognize.

Payments

deposit.created and deposit.updated

status is pending, processing, success, failed, cancelled or expired. Deposits with a currency conversion also include currency_from, currency_to, amount_from, amount_to and deposit_currency. Only these fields and the ones above are sent.

payout.updated

Sent whenever a payout changes status. There’s no payout.created event. The first event you see is usually pending or processing.
status is pending, processing, completed, failed, cancelled or refunded. beneficiary fields vary by payout rail and currency. reference is the payment reference you sent, or null.

virtual_account.deposit

amount is the net amount credited. amount_recevied is the gross amount received, before fees.

virtual_account.created and virtual_account.updated

customer is null when the account belongs to your business.

virtual_account.deleted

Sent when you delete a virtual account. The account no longer accepts funds. The payload has the same fields as virtual_account.created.

metadata.created, metadata.updated and tracking.created

Only metadata and tracking updates meant for you are sent.

Customers

customer.created and customer.updated

Only the fields above are sent. KYC documents and identity numbers are never included. customer.updated is sent when one of these fields or the customer’s KYC status changes.

endorsement.updated

endorsements is the customer’s full current list, not only the one that changed. Compare it with your stored copy. This is the only event that reports KYC and KYB results. A service is available when its endorsement is approved. See Track verification with endorsements.

Stablecoin deposits

crypto.wallet.created and wallet.deleted

When is_customer is false, the address belongs to your business and customer is null. wallet.deleted has the same payload with wallet_status: "deleted". Funds sent to a deleted address can’t be recovered.

crypto.deposit.completed

Match a deposit to its on-chain transfer with transaction_hash. transaction_id is Yativo’s reference and isn’t always the hash. transaction_hash is null if the hash isn’t available. The older crypto_deposit event has the same payload plus user_id. Handle both names.

Virtual cards

Every virtualcard.* payload includes:
  • event_id: unique per event, and the same on every redelivery. Use it to deduplicate
  • card_id: the card ID you pass to the card endpoints
  • customer_id and customer: the cardholder
Amounts are decimal USD (4.5 means $4.50).

KYC

kyc_status is under_review, approved or rejected. Show rejection_details[].message to your customer and branch on code.

Card lifecycle

When you terminate a card through the API, the remaining balance reaches your wallet when virtualcard.terminated.refund arrives, not in the terminate response.

Funding

reference matches the reference returned by your top-up or withdrawal request.

Card transactions

Branch on event.type first, then on transaction_type. A cross-border purchase sends virtualcard.transaction.crossborder twice with the same reference. The first, cross_border_pending, includes fee_amount, which is debited from your USD wallet. The second, cross_border_settled, moves the purchase amount off the card.

Declines

After 2 failed charges, the card is frozen and you receive virtualcard.deactivated.

Corporate cards

business_spend_card.* events cover corporate cards. card_id is your card ID. Events not tied to one of your customers have "customer": null.
transaction_id is the ID of the record in GET /business/spend-card/transactions, which you use to attach notes and invoices.

Account and platform events

Use webhook_updated to confirm your endpoint receives events and your signature check works. In user.updated, business has only business_legal_name, business_operating_name, business_type and country, each when it’s set. Documents, directors, shareholders and verification results are never included. Signing in or changing security settings doesn’t send user.updated.

Respond to a webhook

  • Return a 2xx status as soon as you’ve stored or queued the event. Do slow work afterwards.
  • Respond within 3 seconds. Some events are sent while the API call that caused them is still running, so a slow endpoint can slow that call down.
  • Yativo doesn’t read your response body. An empty 200 is enough.
  • A non-2xx status, a connection error or a timeout is a failed delivery. Failed deliveries are retried up to 3 times in total, with exponential backoff, then dropped. Resend any event you missed.
Node.js

Delivery log

Every delivery attempt is logged:
number
Results per page.
Logged payloads are shown with the fields events carry today. Older entries may show fewer fields than were sent at the time. GET /business/events/show/{eventId} returns 404 Webhook event not found when the ID isn’t in your log.

Resend an event

eventId is the id from the delivery log, not an ID from inside the event’s payload.
  • The original payload is resent, signed with your current secret, to the URL you have registered now. Fields that events no longer carry are left out.
  • The resend appears as a new entry in the delivery log.
  • 404 Webhook event not found means the ID isn’t in your log. 400 No webhook endpoint is configured for your account means you have no webhook.

Test in sandbox

Send sample events to your registered URL without creating real records:
type is one of payin, payout, virtual_account, virtual_account_created, virtual_account_updated, customer, customer_updated, endorsement, metadata_created, metadata_updated, webhook_updated, user, tracking, payin_method_enabled, payin_method_disabled, payout_method_enabled, payout_method_disabled, crypto_wallet, crypto_deposit or wallet_deleted.
Sample events are signed like real ones. For example, payin sends deposit.created and then deposit.updated. Their customer may be null. To settle a real sandbox record — a payin, payout, virtual-account deposit, or card event — use the helpers in Sandbox simulation endpoints.

Best practices

  • Verify every signature before trusting the contents.
  • Respond fast, process later. Return 2xx immediately and do the work in a background job.
  • Deduplicate. The same event can arrive more than once. Use event_id where present, otherwise the record’s id, transaction_id or deposit_id with its status.
  • Don’t rely on order. deposit.created can arrive after deposit.updated. Use status and timestamps.
  • Ignore unknown events. New types are added over time.
  • Keep your secret safe, and rotate it with regenerate-secret if it may have leaked.
  • Use HTTPS.