Weav Docsv1

API reference / Webhooks

Set your webhook URL

PUT/v1/webhook-endpoint

Sets or replaces this mode's webhook URL, and optionally a separate URL for withdrawal events. They can only be set here, not in the dashboard, so nobody changes them by accident; every change is emailed to your Owners and Developers. Each webhook goes to exactly one URL: a payment's own webhook_url if it has one, else withdrawals_url for withdrawal events when set, else url (limited to events). The URL must be public HTTPS: private and internal addresses are refused. Webhooks are signed with the signing key you create in the dashboard, and none are sent until one exists (has_signing_key).

Where each webhook goes

Every webhook goes to exactly one URL, so one event never goes to two of your URLs. Weav picks it in this order:

  1. The payment’s webhook_url. A payment created with one sends all its events there: completed, failed, canceled, reversed and refunded. Your url and its events list don’t apply to that payment.
  2. withdrawals_url, for withdrawal events. When it’s set, WITHDRAWAL_COMPLETED and WITHDRAWAL_FAILED go there, and url no longer gets them.
  3. url, for everything else, limited to the events you chose.
Event Payment has a webhook_url withdrawals_url is set Goes to
A payment event Yes (doesn’t matter) webhook_url only
A payment event No (doesn’t matter) url, if it’s in events
A withdrawal event (not a payment) Yes withdrawals_url only
A withdrawal event (not a payment) No url, if it’s in events
  • Every URL follows the same rules: public HTTPS only (private and internal addresses are refused when you set it, and checked again before each delivery), signed with your signing key (see Verifying signatures), retried the same way, and shown under Developers in the dashboard with its deliveries.
  • Live and test keys each set their own mode’s URLs.
  • Every change to url or withdrawals_url is emailed to your account’s Owners and Developers, with the API key that made it.
  • In live mode, if a URL keeps failing, your Owners, Developers and technical contact get an email: when nothing has gotten through for 10 minutes, when it’s down for an hour, a reminder if it’s still down a day later, and one when it works again. If about a quarter of webhooks fail while others get through, they get one email about that too. A short blip sends nothing. Set the technical contact on the Developers page of your dashboard.
  • Nothing is sent until you create a signing key in the dashboard under Developers. Until then, has_signing_key is false.

Headers

  • AuthorizationstringRequired
    Bearer plus your API key. See Authentication.

Body

JSON, with Content-Type: application/json.

  • urlstring (uri)Required
    Required. A public HTTPS URL, up to 2,048 characters. Its host must resolve. Private and internal addresses (such as localhost or 10.x) are refused.
    Up to 2048 characters
  • eventsarray of strings
    Which events url gets. Leave out for all of them. Doesn't limit withdrawals_url or a payment's webhook_url.
    One of: PAYMENT_COMPLETED PAYMENT_FAILED PAYMENT_CANCELLED PAYMENT_REVERSED PAYMENT_REFUNDED CHECKOUT_COMPLETED CHECKOUT_EXPIRED PAYMENT_METHOD_SAVED PAYMENT_METHOD_FAILED PAYMENT_METHOD_REMOVED WITHDRAWAL_COMPLETED WITHDRAWAL_FAILED
  • withdrawals_urlstring | null
    Optional. Withdrawal events go here instead of url (url stops getting them). Null removes it; leaving it out keeps it.
    Up to 2048 characters

Responses

200 OK

Saved (code 2000000).

What data holds
  • webhook_endpointobject
    7 fields in webhook_endpoint
    • urlstring
    • eventsarray of strings
    • healthstring
      One of: UNKNOWN HEALTHY FAILING DOWN
    • withdrawals_urlstring | null
      Where withdrawal events go, when not url.
    • has_signing_keyboolean
      Webhooks go out only once a signing key exists. Create it in the dashboard under Developers.
    • last_success_atstring (date-time) | null
    • updated_atstring (date-time) | null

401 Unauthorized

Missing, malformed or unknown API key (code 4010000).

403 Forbidden

The key can't make this request (code 4030000): an address outside the key's IP allowlist, a live key before your live mode is on, or an account that isn't active. A live key sent from a browser is refused (code 4030001): keep live keys on your server.

422 Unprocessable Content

One or more fields are invalid (code 4220000), or the channel is off (4220101) or doesn't take the currency (4220102).

PUT

Try it: Set your webhook URL

Test mode
Authorization
Body
Valid JSON
cURL

Uses $WEAV_API_KEY, never your key. Paste it into a terminal, or import it into Postman.

Response

Send the request to see Weav's answer here.

curl -X PUT https://api.weavpayments.com/v1/webhook-endpoint \
    -H "Authorization: Bearer $WEAV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "url": "https://example.com/webhooks/weav",
    "events": [
        "PAYMENT_COMPLETED"
    ],
    "withdrawals_url": null
}'
{
    "code": "2000000",
    "success": true,
    "message": "Webhook endpoint saved",
    "data": {
        "webhook_endpoint": {
            "url": "https://example.com/webhooks/weav",
            "events": [
                "PAYMENT_COMPLETED",
                "PAYMENT_FAILED",
                "PAYMENT_CANCELLED",
                "PAYMENT_REVERSED",
                "PAYMENT_REFUNDED",
                "CHECKOUT_COMPLETED",
                "CHECKOUT_EXPIRED",
                "WITHDRAWAL_COMPLETED",
                "WITHDRAWAL_FAILED"
            ],
            "withdrawals_url": null,
            "health": "UNKNOWN",
            "has_signing_key": true,
            "last_success_at": null,
            "updated_at": "2026-10-26T10:00:00+08:00"
        }
    }
}