Webhooks

Get a signed event on your server when something happens in your business, covering adding an endpoint, all 43 events by product, the envelope and payloads, checking the signature, replies and retries, the automatic switch-off, the delivery log, catching up on missed events and changing the signing secret.

Overview

A webhook tells your server what happened, as it happens: a payment succeeded, a refund went through, a dispute opened, an invoice was paid. You add an https endpoint once, pick the events it should get, and Vezmo sends each one as a signed JSON POST.

Webhooks are how you ship orders: a redirect or a browser event can be faked, a signed event can't.

Add an endpoint

You need the webhooks permissions; the Manager role has them.

  1. Open Webhooks

    Go to Payments › VezmoPay, then Developers, and open the Webhooks tab. Select Add endpoint.

  2. Enter the endpoint URL

    The Endpoint URL must start with https://, in test and in live. Addresses that point to a private network are refused, and we never follow redirects. To receive events on your own computer, use an https tunnel.

  3. Select the events

    Under Select Events, tick each event you want. Search narrows the list, and Select all ticks every event. An endpoint only gets the events ticked here: payment.success doesn't bring payment.failed with it.

    Add New Webhook: Endpoint URL https://shop.example.com/webhooks/vezmo, Select Events with 2 selected, the search narrowed to the two ticked events payment.failed and payment.success with their descriptions, and the Create Webhook button.
    A new endpoint with two events ticked.
  4. Confirm with the emailed code

    Select Create Webhook. We email you a 6-digit code: type it in. Adding an endpoint, or changing its URL or events, always needs one.

  5. Save the signing secret

    The Signing secret (whsec_ and 64 characters) is shown once. Store it on your server, then select I've saved it — done. You need it to check every event.

Endpoints belong to your business, not to the teammate who added them: they keep working when that person leaves, and everyone with webhook access sees and manages them. One endpoint gets both test and live events.

Developers, Webhooks tab: the Endpoints card with one endpoint, https://shop.example.com/webhooks/vezmo, Enabled, 43 events, created Nov 09, 2026, with the Deliveries, edit and delete buttons, and the Add endpoint button.
Your endpoints. Edit changes the URL or the events; the signing secret stays the same.

Events

There are 43 events. Payloads that aren't shown here are in the webhooks reference and on each product's reference page.

Payments

EventWhen it is sent
payment.successA payment succeeded: a card or wallet payment at once, a bank payment when it settles. method says how it was paid.
payment.processingA payment is on its way, like a US bank payment that is settling. payment.success follows when it settles, usually a few business days later.
payment.awaiting_verificationA bank payment entered by hand waits for the customer to confirm small test deposits. It carries a verification block with the link to share.
payment.failedA payment was declined or failed, or a bank payment was returned. It carries the decline details.
payment.canceledA bank payment still on its way was canceled before the debit went out. No money moved.
payment.refundedA refund went through, in full or in part. Each refund is its own event.
payment.refund_failedA refund couldn't be completed. The amount is back in your balance; pay the customer back another way.
payment_method.savedA customer saved their card at checkout for later payments with you.

Disputes

EventWhen it is sent
dispute.createdA customer disputed a payment (a chargeback or an inquiry). It carries the reason and evidenceDueBy.
dispute.updatedThe dispute's status changed, for example when evidence was submitted. Each change is its own event.
dispute.closedThe dispute reached its final status: won, lost or closed.

Payouts

EventWhen it is sent
payout.paidA payout reached your bank.
payout.failedA payout failed. A bank can return a payout days after it was paid: the same payout then sends payout.failed after payout.paid. Keep the latest.

Invoices

EventWhen it is sent
invoice.createdAn invoice was created.
invoice.sentAn invoice was sent to the client.
invoice.paidAn invoice was paid: by the client at checkout, or when a bank payment settled.
invoice.cancelledYou canceled an invoice.

Proposals

EventWhen it is sent
proposal.sentA proposal was sent.
proposal.acceptedA proposal was accepted: by the client from the link (acceptedBy: "client"), or recorded by you with POST /merchant/proposal/:id/mark-accepted ("business").
proposal.rejectedA proposal was rejected: by the client, or recorded by you with POST /merchant/proposal/:id/mark-rejected (rejectedBy).

Subscriptions

EventWhen it is sent
subscription.trial_startedA free trial started: the customer saved their card. Nothing is charged yet.
subscription.activatedThe first payment succeeded.
subscription.renewedA renewal was paid.
subscription.payment_failedA subscription charge failed. Each failed attempt is its own event.
subscription.updatedThe quantity or the plan changed.
subscription.pausedThe subscription was paused.
subscription.resumedA paused subscription started again.
subscription.cancel_scheduledA cancel was booked for the end of the period, by you or the customer. It stays active until then.
subscription.canceledThe subscription ended: canceled now, at the end of the period, or because its first payment was never made.
subscription.refundedA subscription payment was refunded, in full or in part.
subscription.disputedA subscription payment was disputed, or its dispute was won or lost.

Agreements

Agreements have no test mode: their events always carry livemode: true.

EventWhen it is sent
agreement.sentAn agreement was sent for signature.
agreement.viewedA recipient opened it for the first time. Once per recipient.
agreement.signedA recipient signed. Once per recipient.
agreement.completedEveryone signed and the signed PDF is ready.
agreement.declinedA recipient declined to sign.
agreement.voidedThe agreement was voided.
agreement.expiredIts deadline passed with someone still to sign.
agreement.paidThe payment attached to it was paid.
agreement.identity_checkedA signer's ID check passed, needs your review, was approved or rejected, or failed for the last time.
agreement.awaiting_approvalEveryone signed, and it waits for you to approve the signers' IDs.

Identity checks

EventWhen it is sent
kyc.verification.submittedA teammate's identity check went into review.
kyc.verification.decidedA teammate's identity check was approved or rejected.

These two carry only the check's id, userId, status, documentType, submittedAt and decidedAt (and your merchantId): never a score or a reason.

What we send

Every event is a POST with a JSON body and these headers:

HeaderWhat it holds
X-Webhook-Signature-V2The signature to check: see Check the signature.
X-Webhook-TimestampWhen this attempt was sent, in Unix seconds. It is new on every retry.
X-Webhook-EventThe event name, like payment.success.
X-Webhook-SignatureAn older signature over the body only, kept for integrations that already check it. New code checks V2.
Content-Typeapplication/json

The body is always the same envelope, with three fields. A full example of every event is in the Webhooks reference.

id
The event's id. The same on every retry of this event, and different for every other event: use it to spot repeats. Don't parse it.
event
The event name, as in the header.
data
The object the event is about. In payment.success and payment.failed, paymentId is the id you got when you created the checkout session, beside amount, currency, status, method and livemode. A payment.success example is in the quickstart.
  • Amounts are JSON numbers in major units: 49.99 is $49.99.
  • livemode is false for test events. Never ship a real order on one.
  • merchantId is your VezmoPay merchant id, once your business is approved.
  • When a customer paid in their own currency, amount and currency are your price, and charged holds what they paid. See Local currency.

Check the signature

Check every event before you trust it. The X-Webhook-Signature-V2 header is the HMAC-SHA256 of timestamp + "." + raw body, keyed with your signing secret, as lowercase hex. timestamp is the X-Webhook-Timestamp header, exactly as sent.

  1. Read the raw body bytes. Parsing the JSON and turning it back into text changes it, and the signature won't match.
  2. Compute the HMAC and compare it to the header in constant time.
  3. Refuse a timestamp more than 5 minutes from your clock, so an old event can't be replayed.

Answer 400 to a bad signature and 200 to a good one. A working example, with the raw body, the 5-minute check and the constant-time comparison, is in Verify signatures in the API reference.

Replies and retries

Answer with any 2xx status within 5 seconds: the 5 seconds cover connecting and your reply. We read only the first 2 KB of what you send back. Anything else (another status, a timeout, a redirect) is a failed delivery.

A failed event is tried again, 4 attempts in all: at once, then 6 hours after the first failure, 12 hours after the second and 24 hours after the third. Each attempt has a new timestamp and signature, and the same id. A retry goes to the endpoint's URL at that moment, signed with its secret at that moment, so once you fix your endpoint the pending retries land.

To stay inside 5 seconds, save the event and answer first, then do the slow work.

When an endpoint keeps failing

If every delivery to an endpoint fails for at least 3 days and at least 50 attempts, we switch it off: it shows as Disabled, and your team gets an email, "We paused one of your webhook endpoints". Its pending retries are dropped, and events that happen while it is off aren't kept for it.

The dashboard has no switch to turn a paused endpoint back on. Fix your server, delete the endpoint and add it again (you get a new signing secret), then catch up on what you missed.

Handle events well

  • Expect repeats. The same event can arrive more than once. Keep the ids you've handled and skip one you've seen. Each refund, each dispute status, each failed subscription charge and each agreement signer has its own id.
  • Don't count on the order. A retry can arrive hours after a later event. Before you act on an old one, read the current state from the API, for example GET /merchant/payment/:id.
  • Check livemode. Test and live events reach the same endpoint.
  • Match the money. Before you ship, check that data.amount and data.currency are what the order expects.

The delivery log

Developers › Webhooks › Deliveries lists every attempt: the event, your server's answer code, how long it took and when. It shows one endpoint at a time: choose it in the list at the top, or select Deliveries on its row. Open a row to see the request we sent and what your server replied.

The Deliveries card for https://shop.example.com/webhooks/vezmo: rows for payment.success, payment.failed, dispute.created, invoice.sent, proposal.accepted and proposal.sent, each with a 200 answer, the time it took and how long ago; the newest payment.success row is open on Request we sent, showing the event's JSON data.
Every attempt, with your server's answer.
  • A row with 4xx or 5xx: we delivered and your server refused it. A bad signature check is the usual cause.
  • No reply: your server didn't answer within 5 seconds, or couldn't be reached.
  • No row at all: the endpoint isn't subscribed to that event, or the endpoint is off.

Catch up on missed events

There is no button to resend an event, no test event, and no API to list past events. To catch up after an outage, read the current state from the API (Payments reference):

  1. Orders waiting for payment. For each one, read the payment id you saved with GET /merchant/payment/:id, and act on its state.

  2. Payments you have no record of. List the days you missed with GET /merchant/payment, using range=custom, startDate and endDate, and follow data.pagination.nextPage to the end.

    Bank payments still settling aren't in that list: list them with pending=true, and those waiting for the customer's deposit check with state=awaiting_verification. These two lists page with page=.

  3. Disputes. GET /merchant/dispute?from=2026-11-08&to=2026-11-09 (reference).

  4. Invoices and subscriptions. GET /merchant/invoice and GET /merchant/subscription (invoices, subscriptions).

Change the signing secret

A secret can't be changed in place: editing an endpoint keeps it. To get a new one, add a new endpoint and delete the old one.

  • Delete, then add the same URL: the simplest, but events in between are missed. Catch up as above.
  • Add, then delete: no gap. While both endpoints exist, each event goes to both, with a different id and each signed with its own endpoint's secret. For that time your handler has to accept both secrets, and spot repeats by the event name and the object in data (like data.paymentId), not by id.

If a secret leaks, do it at once.

Questions

How do I send a test event?

Make a test payment with a test key: see Take your first test payment. Its events reach your endpoint with livemode: false.

Can I add endpoints over the API?

No. Endpoints, like API keys, are managed in the dashboard only.

I get events for things a teammate did

Endpoints belong to the business, so they get events from the whole business, whoever caused them.

Facts checked against the product on 9 Nov 2026.

Was this page helpful?