Skip to main content
Register an HTTPS endpoint and Moove POSTs a signed JSON body to it every time one of your payment links is paid, and again when a payment completes the link.
Webhooks are the push half of Moove Payment Links. The pull half — GET /v1/payment-link — still works, and remains the way to reconcile state you may have missed.

Register an endpoint in the console

Endpoints are registered in the Moove Business console at moove.xyz/business/manage/webhooks, under Add Endpoint. There is no API call for this.
Registering an endpoint deliberately requires a signed-in session, not an API key. A key that could register a destination could quietly copy every settled payment to a host its holder controls. Keys create and read payment links; a person decides where notifications go.
Two fields:
string
required
A public https:// address with no explicit port — any port in the URL is rejected, even :443. Maximum 2048 characters.It cannot be changed later. To move to a new destination, add a second endpoint and delete the first — both fire until you delete the old one, so the swap has no gap.
string
required
Your label for the endpoint, so you can tell prod from staging. Maximum 100 characters.
The URL is validated before anything is saved. These are rejected: The hostname is resolved and every address it returns must be publicly routable. It is checked again at send time — a host that stops resolving publicly stops receiving deliveries.
You can hold up to 5 endpoints at once. Deleted endpoints do not count towards it.

The signing secret is shown once

Adding an endpoint returns its signing secret. It starts with whsec_, it is displayed exactly once, and it is never shown again — not in the list, not through the API. Store it before you dismiss the dialog. If you lose it, add a new endpoint and delete the old one; there is no rotation and no way to re-read it. The console lists a short prefix of the secret afterwards, which identifies the endpoint but cannot be used to sign anything.

Events

Every endpoint receives every event. There is no subscription picker. A final payment on a single-use link fires both: payment_link.transaction.succeeded for the money and payment_link.completed for the link. They are delivered separately and can arrive in either order. Deactivating a link or letting it expire sends no event. Pick those up from GET /v1/payment-link. Branch on type. Ignore any type you do not recognise rather than erroring — new types can be added.

The request Moove sends

POST to your URL, Content-Type: application/json, with three headers:

Body

string
required
The event id. Matches the Moove-Event-Id header.
string
required
payment_link.transaction.succeeded or payment_link.completed.
string
required
ISO 8601 timestamp of when the event occurred — not when this attempt was sent. It is identical on every retry.
object
required
The payment link’s state at send time.
payment_link.transaction.succeeded
The body on the wire is compact and its keys are sorted — no spaces, no newlines. The example above is formatted for reading. Verify the signature against the raw bytes you received; re-serialising the parsed object produces a different digest.
Nothing about the payer is included. The payer is a third party, and whatever your own checkout collected is already yours.The payload is a notification, not a replacement for the API. When you need more than it carries, read the link back with its paymentLinkId.

Verify every delivery

Your endpoint is a public URL. Anyone can POST to it, so treat an unsigned or badly signed request as hostile. The signature is v1= followed by the hex HMAC-SHA256 of {Moove-Timestamp}.{raw body}, keyed by your signing secret.
The key is the secret exactly as it was shown to you, including the whsec_ prefix. Do not strip it.
Four rules:
  1. Read the raw body, before any JSON middleware touches it. Most frameworks need to be told to keep it.
  2. Compare in constant time. A plain string comparison leaks the digest a byte at a time.
  3. Reject stale timestamps. The timestamp is inside the signed message, so it cannot be edited independently — a few minutes of tolerance is enough to stop a captured delivery being replayed later.
  4. De-duplicate on Moove-Event-Id. Delivery is at-least-once; see below. The id is per endpoint, so two endpoints receiving the same event see different ids.

Reply fast, then do the work

acknowledgement
required
The delivery is done. Nothing else is sent for that event.
Everything else is a failure and enters the retry schedule, including a 3xx. Redirects are not followed — a redirect on a signed POST would replay the body, and its signature, to a host you never registered. The connection times out after 10 seconds. Verify, enqueue, return 200. Do not settle an order, call a payment processor or send an email before replying.

Retries run for about 15 hours

A failed attempt is retried up to 6 attempts in total: After the sixth, the delivery is marked failed and abandoned. Nothing is re-sent, and the event is not replayed when your endpoint recovers — reconcile the gap with GET /v1/payment-link.
Delivery is at-least-once. An attempt whose response was lost after your server had already processed it will be retried, so the same Moove-Event-Id can arrive twice.Make your handler idempotent: record the event id and drop one you have already applied. This is the one piece of integration work webhooks genuinely require.
Disabling or deleting an endpoint abandons anything still queued for it. A disabled endpoint is a pause, not a buffer — re-enabling it does not replay what it missed.

Inspect what was sent

Each endpoint in the console has View deliveries: the event type, status, attempt count, the HTTP code your server returned, and the error text where there was one. The endpoint list also shows the last delivery outcome and a consecutive-failure count — enough to answer “is my webhook working” without opening the log. The response body your server returns is never stored, and never shown back to you.

What is not available

Registering, editing and deleting endpoints are console actions, not API calls — see above.There is no endpoint test button, no manual replay of a failed delivery, and no secret rotation. To change a secret or a URL, add a new endpoint and delete the old one.

Next

Moove Payment Links

The endpoints these events are about.

Errors

Branching on code.

Moove Agentic Payments

The wider integration.