KashierDevelopersKashier Developers
Webhooks

Webhook management

Configure multiple named webhooks per merchant, each scoped to a mode and a set of event types, and inspect, resend, or test deliveries

Kashier's webhook management module lets you configure multiple named webhooks per merchant instead of a single fixed URL. Each webhook is scoped to a mode (test or live) and subscribed to a specific set of event types, so different endpoints can receive different slices of your traffic — for example, a production endpoint that only cares about refund and transfer status changes, and a separate test endpoint your staging environment listens to.

This page covers creating and managing webhooks, looking up delivery records for a transaction or transfer, resending a delivery, and sending a test payload to verify an endpoint is reachable. For the shape of the event payload itself and signature verification, see Webhook payloads and the Webhooks guide.

All endpoints on this page are merchant-authenticated — send your secret key in the Authorization header, the same as everywhere else in the API.

The delivery-records endpoints need a permission too

Reading delivery records requires the webhook.view_webhook permission, and resending a delivery requires webhook.resend_webhook. These are dedicated webhook permissions rather than a side effect of your transaction or transfer access, and they're enforced for merchant users, not only for Kashier staff — a request without them is rejected with 403 even when the key itself is valid. Permission changes are cached for roughly 24 hours, so a newly granted permission may not take effect straight away. The CRUD endpoints (list, create, update, delete) are merchant-gated too, but which privilege they check wasn't pinned down at design time — if one returns 403 on a valid key, ask your Kashier contact which permission your user is missing.

If you used the old single-URL webhook

If your account was set up with Kashier's older single stored webhook URL, there's nothing to re-create. That URL is migrated into webhook management for you, so List your webhooks returns it as a webhook named Legacy webhook, subscribed to every event type, with isActive carrying over whether the old webhook was enabled and mode derived from your account's current mode. It keeps receiving exactly what it received before the migration.

From there it's an ordinary webhook. Rename it, narrow its events to the ones you actually handle, add custom headers, or switch it off with Update a webhook. If you want separate test and live destinations, create a second webhook for the other mode rather than flipping this one's mode back and forth.

List your webhooks

Returns every webhook configured for your merchant, across both modes.

Full parameter and response reference → List your webhooks.

curl --location 'https://test-api.kashier.io/v2/webhooks' \
  --header 'Authorization: YOUR_TEST_SECRET_KEY'

Headers

KeyDescription
AuthorizationThe Authorization is a secret key that is used to identify the merchant. You can obtain it from Kashier's dashboard. Learn more about Authorization.

Response

[
  {
    "_id": "66a1f2c3e4b0a1234567890a",
    "merchantId": "MID-10293",
    "name": "Orders production endpoint",
    "url": "https://shop.example.com/kashier/webhook",
    "headers": {
      "X-Shop-Token": "whsec_live_abc123",
      "X-Env": "production"
    },
    "events": ["pay", "refund", "void", "TRANSFERRED"],
    "mode": "live",
    "isActive": true,
    "createdAt": "2026-06-22T12:00:00.000Z",
    "updatedAt": "2026-06-22T12:00:00.000Z"
  }
]
FieldDescription
_idUnique identifier for the webhook — this is the webhookId used in Update a webhook and Delete a webhook.
merchantIdThe merchant the webhook belongs to.
nameYour label for the webhook.
urlThe endpoint Kashier delivers to.
headersCustom headers Kashier sends with every delivery to this webhook, as a flat string-to-string map.
eventsThe event types this webhook is subscribed to — see Create a webhook for the full list.
modetest or live — the webhook only receives events emitted in this mode.
isActiveWhether the webhook currently receives deliveries.
createdAt / updatedAtTimestamps.

Create a webhook

Creates a new webhook for your merchant.

Full parameter and response reference → Create a webhook.

Body parameters

KeyDescription
nameYour label for the webhook.
urlThe endpoint Kashier delivers to. Must be a public https URL — non-https schemes, localhost, loopback, link-local, and private (RFC-1918) addresses are rejected.
headersOptional. Custom headers to send with every delivery to this webhook, as a flat string-to-string map. Defaults to {}.
eventsArray of event types to subscribe this webhook to — see the table below.
modetest or live. The webhook only receives events emitted in this mode.
isActiveOptional. Whether the webhook is enabled. Defaults to true.

Max 5 webhooks per mode

You can configure up to 5 webhooks per mode (5 test + 5 live = 10 total). Creating a 6th webhook for a mode that's already at the limit is rejected. This limit is taken from Kashier's webhook-management design documentation rather than a live API response, and is flagged there as still-to-confirm (per-mode vs. global was open at design time). If a 6th webhook is unexpectedly accepted or rejected, treat your account's actual behavior as authoritative.

events accepts any of the following values:

CategoryValues
Transaction operationspay, authorize, capture, refund, void, reversal
Transfer statusesINITIATED, IN_TRANSIT, TRANSFERRED, FAILED

The mixed casing is deliberate — each value matches the producing service's own literal, so transaction operations are lowercase and transfer statuses uppercase. Send them exactly as written.

Subscribing to refund does not cover partial refunds

The subscribable list holds base operations only, and matching is exact. A transaction whose operation is partial_refund does not match a refund subscription, so a webhook subscribed to refund won't be delivered that transaction. A per-request destination is not event-filtered and still receives it. The same applies to any other operation outside the list above.

Not every transfer status is subscribable

The subscribable list is narrower than the full transfer lifecycle. PENDING (and, for batches, PARTIALLY_TRANSFERRED) appear in the payout status values but aren't subscribable here, so a transfer entering one of them won't trigger a delivery. If you need a status that isn't listed, use the create-webhook "Try it" panel below to confirm the accepted set against your own account before assuming it's unavailable.

One webhook, two signing keys

Transaction events and transfer events are signed with different keys and different serializations, so a webhook subscribed to both — like the example below — needs two verifiers and has to pick one based on the event it received.

  • Transaction events are signed with your Payment API Key for the webhook's mode: your test key signs test-mode deliveries, your live key signs live ones. The signed string sorts signatureKeys alphabetically and URL-encodes each value. See signature verification.
  • Transfer events are signed with your Transfer API Key, over that payload's own signatureKeys in array order with raw, un-encoded values. See Payout webhooks.

Reusing one verifier for both produces the wrong hash for one of them.

curl --location 'https://test-api.kashier.io/v2/webhooks' \
  --header 'Authorization: YOUR_TEST_SECRET_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "Orders staging endpoint",
    "url": "https://your-website.com/kashier-webhook",
    "headers": {
      "X-Shop-Token": "replace-with-your-own-token",
      "X-Env": "staging"
    },
    "events": ["pay", "refund", "void", "TRANSFERRED"],
    "mode": "test",
    "isActive": true
  }'

For a production webhook, send the same body with "mode": "live" to https://api.kashier.io/v2/webhooks using your live secret key — keep mode and the host you call in step rather than creating a live webhook from the test host.

This panel creates a real webhook

Unlike the read-only panels on this page, sending this one leaves persistent state on the account whose keys you saved: a webhook that stays there until you delete it, and that counts against the 5-per-mode limit above. The body below is deliberately defanged — mode is test, isActive is false, and the URL is a placeholder — so nothing is ever delivered anywhere. Delete it afterwards with Delete a webhook, using the _id from the response.

Headers

KeyDescription
AuthorizationThe Authorization is a secret key that is used to identify the merchant. You can obtain it from Kashier's dashboard. Learn more about Authorization.

Response

Returns the created webhook, in the same shape as an entry in List your webhooks.

Update a webhook

Updates an existing webhook. Send only the fields you want to change.

Full parameter and response reference → Update a webhook.

Body parameters

Same fields as Create a webhookname, url, headers, events, mode, isActive — all optional on update. The https-only URL rule applies here too. Changing mode re-checks the 5-per-mode limit against the target mode.

curl --location --request PUT 'https://test-api.kashier.io/v2/webhooks/:webhookId' \
  --header 'Authorization: YOUR_TEST_SECRET_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "isActive": false
  }'

Headers

KeyDescription
AuthorizationThe Authorization is a secret key that is used to identify the merchant. You can obtain it from Kashier's dashboard. Learn more about Authorization.

Response

Returns the updated webhook, in the same shape as an entry in List your webhooks.

Delete a webhook

Removes a webhook. There's no undo — deliveries already recorded against it stay visible in delivery records.

Full parameter and response reference → Delete a webhook.

curl --location --request DELETE 'https://test-api.kashier.io/v2/webhooks/:webhookId' \
  --header 'Authorization: YOUR_TEST_SECRET_KEY'

Headers

KeyDescription
AuthorizationThe Authorization is a secret key that is used to identify the merchant. You can obtain it from Kashier's dashboard. Learn more about Authorization.

Get delivery records for a transaction

Returns the delivery log for a given transaction — every webhook attempt made for it, across all your configured webhooks, plus any per-session webhook you passed directly on that transaction.

Full parameter and response reference → Get delivery records for a transaction.

curl --location 'https://test-api.kashier.io/v2/webhooks/records/transaction/:transactionId' \
  --header 'Authorization: YOUR_TEST_SECRET_KEY'

Not live on the test environment yet

This route isn't registered on test-api.kashier.io today — calling it there returns a 404 rather than a records payload, while the webhook CRUD endpoints on the same router answer normally. It's documented here because it's designed and ticketed, but there's no "Try it" panel for it until it ships. Check with your Kashier contact before building against it.

Headers

KeyDescription
AuthorizationThe Authorization is a secret key that is used to identify the merchant. You can obtain it from Kashier's dashboard. Learn more about Authorization.

Response

{
  "success": true,
  "body": [
    {
      "resourceType": "transaction",
      "resourceId": "TX-249893963",
      "webhookId": "66a1f2c3e4b0a1234567890a",
      "eventType": "pay",
      "mode": "live",
      "url": "https://shop.example.com/kashier/webhook",
      "status": "delivered",
      "attempts": 1,
      "payload": { "...": "allow-listed fields only" },
      "responses": [
        { "status": 200, "body": "OK", "date": "2026-06-22T12:00:01.000Z" }
      ],
      "isServerWebhook": false,
      "isTest": false,
      "createdAt": "2026-06-22T12:00:00.000Z",
      "updatedAt": "2026-06-22T12:00:01.000Z"
    }
  ]
}
FieldDescription
resourceTypetransaction for records returned here.
resourceIdThe transaction ID this delivery relates to.
webhookIdThe webhook this delivery came from, or null if it was a per-session webhook passed directly on the transaction.
eventTypeThe event that triggered this delivery.
modetest or live.
urlThe destination URL the delivery was sent to.
statusDelivery status — pending, delivered, retrying, or failed. A record sits in retrying between backoff attempts and only reaches failed once the attempt cap is exhausted.
attemptsNumber of delivery attempts recorded.
payloadThe request body sent, projected to a fixed, always-visible field set. Custom headers you configured are never included in this response.
responsesOne entry per attempt: the receiving endpoint's status, body, and date.
isServerWebhooktrue if this delivery went to a per-session webhook rather than a configured webhook.
isTesttrue if this record came from Test a webhook rather than a real delivery.

Get delivery records for a transfer

Same records lookup as above, scoped to a transfer instead of a transaction.

Full parameter and response reference → Get delivery records for a transfer.

curl --location 'https://test-api.kashier.io/v2/webhooks/records/transfer/:transferId' \
  --header 'Authorization: YOUR_TEST_SECRET_KEY'

Not live on the test environment yet

Like its transaction counterpart, this route isn't registered on test-api.kashier.io today — calling it there returns a 404. It's documented because it's designed and ticketed, but there's no "Try it" panel for it until it ships.

Headers

KeyDescription
AuthorizationThe Authorization is a secret key that is used to identify the merchant. You can obtain it from Kashier's dashboard. Learn more about Authorization.

Response

Same shape as Get delivery records for a transaction, except resourceType is transfer, resourceId is the transfer ID, and eventType is one of the transfer status values (INITIATED, IN_TRANSIT, TRANSFERRED, FAILED).

Resend a delivery

Re-sends a previously recorded delivery — the original stored request, byte-for-byte, including its original signature — to the same URL. Useful after fixing an endpoint that was down or returning errors. The outcome is written to a new record; the original record is never modified, so your delivery history stays intact.

A record can be resent from any status, including delivered. The endpoint takes no request body — everything it needs comes from recordId in the path.

Full parameter and response reference → Resend a delivery.

30-second cooldown

Resending the same record again within 30 seconds returns 429. Wait for the cooldown to clear before retrying.

curl --location --request POST 'https://test-api.kashier.io/v2/webhooks/records/:recordId/resend' \
  --header 'Authorization: YOUR_TEST_SECRET_KEY'

Headers

KeyDescription
AuthorizationThe Authorization is a secret key that is used to identify the merchant. You can obtain it from Kashier's dashboard. Learn more about Authorization.

Response

{
  "recordId": "66a20a1ce4b0a1234567891b",
  "clonedFromRecordId": "66a1f2c3e4b0a1234567890c",
  "status": "delivered",
  "attempts": 1,
  "lastResponseStatus": 200
}
FieldDescription
recordIdID of the new delivery record created for this resend.
clonedFromRecordIdID of the original record this resend replayed.
statusOutcome of the resend, e.g. delivered or failed.
attemptsAttempts recorded on the new record (always 1 immediately after a resend).
lastResponseStatusHTTP status your endpoint returned for the resend attempt.

Test a webhook

Sends a signed sample payload to a webhook's configured URL so you can confirm the endpoint is reachable and your signature verification works — without waiting for a real event. The test delivery is recorded like any other delivery, but flagged isTest: true so it's distinguishable in your delivery history.

Full parameter and response reference → Test a webhook.

curl --location --request POST 'https://test-api.kashier.io/v2/webhooks/:id/test' \
  --header 'Authorization: YOUR_TEST_SECRET_KEY'

Not live on the test environment yet

This route isn't registered on test-api.kashier.io today — calling it there returns a 404, so there's no "Try it" panel for it. Until it ships, verify an endpoint by running a real test-mode payment against it and reading the outcome from your own server logs.

Headers

KeyDescription
AuthorizationThe Authorization is a secret key that is used to identify the merchant. You can obtain it from Kashier's dashboard. Learn more about Authorization.

Response

{
  "success": true,
  "statusCode": 200,
  "responseBody": "OK",
  "latencyMs": 184
}
FieldDescription
successtrue if your endpoint returned a 2xx status.
statusCodeHTTP status your endpoint returned.
responseBodyA truncated snippet of your endpoint's response body.
latencyMsRound-trip time for the test delivery, in milliseconds.

Next steps

On this page