ReGenerate
v1 עברית

New here? Start here: from the invitation to your first codes, on the sandbox →

ReGenerate Retailer API

Buy digital codes at your negotiated price, from your own systems.

Production
https://api.regenerate.co.il/v1 · rg_live_…
Sandbox
https://api-sandbox.regenerate.co.il/v1 · rg_test_…

Getting started

End to end: a key, a token, a catalogue, an order, and the codes. Five requests. Everything else in this document is detail on one of them. Build against the sandbox first — https://api-sandbox.regenerate.co.il/v1, keys that start rg_test_, suppliers' test systems and no real money — then switch the base URL to production, https://api.regenerate.co.il/v1, with a key that starts rg_live_. A key works only in the environment that issued it. The examples below use production.

1. Get your key

Your account manager issues it, and the ReGenerate portal shows it once at that moment. It is not recoverable afterwards — we store a hash, not the key — so put it straight into whatever your systems use for secrets.

If it is ever lost or exposed, rotate it from the portal. The outgoing key keeps working for 24 hours so you can roll the new one out across your estate without an outage. If it must stop now — a key in the wrong hands — revoke it instead: it is refused from that moment, and so are the tokens issued with it.

2. Trade the key for a token

The key goes to exactly one endpoint. Every other call carries a token that expires within the hour, which keeps the long-lived secret off the wire.

Every example below sends $TOKEN, so keep the value where the rest of your session can read it.

curl -s -X POST https://api.regenerate.co.il/v1/auth/token \
  -H 'content-type: application/json' \
  -d '{"apiKey":"rg_live_4XK9ABCD_p7Qm2ZtVx8Ln4Rc9Wd3Yf6Hs"}'

# {"accessToken":"eyJpc3M...","tokenType":"Bearer","expiresIn":3600}

# Keep it for the rest of this walkthrough:
TOKEN=$(curl -s -X POST https://api.regenerate.co.il/v1/auth/token \
  -H 'content-type: application/json' \
  -d '{"apiKey":"rg_live_4XK9ABCD_p7Qm2ZtVx8Ln4Rc9Wd3Yf6Hs"}' \
  | sed -n 's/.*"accessToken":"\([^"]*\)".*/\1/p')

3. Read the catalogue

One entry per SKU. The price is yours: your account’s margin is already in it, so what you see is what the order will cost.

A SKU is either fixed-value or open-value. A fixed one has a face value and you order a quantity of it. An open one has pricingMode of range, and you send requestedAmount inside minAmount…maxAmount, as a multiple of stepAmount. faceValue is in the card's own currency, faceCurrency; price is always in shekels, and faceValueInIls is the face value in shekels at the same rate.

Cache it for minutes rather than hours. Prices move with the exchange rate.

curl -s https://api.regenerate.co.il/v1/catalog \
  -H "authorization: Bearer $TOKEN"

4. Place an order

The reference is yours and it is required. Make it unique per order and keep it identical across retries of that order — that is what makes a till safe to retry when a request times out.

Order whatever quantity your account allows. Supplier batch limits are ours to deal with, not yours.

Use a sku from step 3. The one below is an illustration and will answer 404 against your own catalogue.

curl -s -X POST https://api.regenerate.co.il/v1/orders \
  -H "authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d '{"sku":"RG-7KQ4MX","quantity":40,"reference":"TILL-4471-20260914-0007"}'

# 201 {"orderId":"0f8b4a2c-...","status":"created","totalPrice":392400,...}

5. Collect the codes

The order answers immediately, before the supplier has finished. Register a callback and wait to be told, or poll the order every few seconds.

Keep polling while the status is created or processing. Stop on delivered and fetch the codes; stop on failed or refunded too — those are final, and the wallet has already been put back. Treating anything other than processing as final is the mistake to avoid: the first response is created.

Store what you receive. This is a retrieval endpoint, not a vault.

curl -s https://api.regenerate.co.il/v1/orders/TILL-4471-20260914-0007 \
  -H "authorization: Bearer $TOKEN"
# {"status":"delivered","codesAvailable":true,...}

curl -s https://api.regenerate.co.il/v1/orders/TILL-4471-20260914-0007/codes \
  -H "authorization: Bearer $TOKEN"
# {"codes":[{"cardNumber":"5312-8841-9027-4410","pin":"884120",...}]}

Rules that apply everywhere

Money is in minor units

Every amount is an integer of agorot. 1234 is ₪12.34. There are no floating-point amounts anywhere in this API, in either direction, because a rounding error in a price is a rounding error in a payment.

Your key reaches your account and nothing else

There is no parameter that widens it. An order id belonging to another account answers 404, not 403: whether it exists is not something this API will tell you.

Administrative endpoints are not here

They are not hidden behind a role. They are not part of this API at all, so there is nothing to find by guessing paths.

Times are UTC

Every timestamp is ISO 8601 in UTC. Date filters are calendar dates in Israel time, because that is the day a shop means when it says today.

Fields get added

A field may appear in a response that was not there last week. Parse leniently and ignore what you do not know. Nothing will be removed or change type without a new version prefix.

You choose where the API may be called from

In the portal, under your API credentials, you list the addresses your integration calls from — single addresses, or blocks no wider than /24 (IPv4) or /64 (IPv6). Each environment has its own portal, and each portal manages its own environment only: production at https://app.regenerate.co.il, the sandbox at https://sandbox.regenerate.co.il. Production requires a list: until you save one, every production token request is refused with forbidden and a message that says where to save it, and a saved production list can be replaced but not emptied. The sandbox list is optional; without it the sandbox is not restricted. With a list in place, a token is issued only to those addresses and every call is checked against them; a call from anywhere else is refused with forbidden and recorded, so a refused integration has an answer waiting. Each environment also has an Active switch: switched off, every call with that environment's keys is refused with forbidden until it is switched on again. Every change — to a list, to a switch, a key rotation or a revocation — needs a fresh one-time code from the authenticator.

Branch on the code, not the message

Every error carries a stable error.code. The message is for a human reading a log and may be reworded at any time.

Errors

Every failure has the same shape. code is stable and safe to branch on; requestId is worth logging, because quoting it lets us find the exact request.

{
  "error": {
    "code": "insufficient_balance",
    "message": "wallet balance 120000 is below the required 392400",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
CodeHTTPWhat it means
validation_failed 400 The request body or query did not validate. `details` names the fields.
unauthenticated 401 No token, an expired token, or a key that is not valid.
forbidden 403 The account is blocked, the resource belongs to another account, the environment is switched off in the portal, production has no address list yet, or the call came from an address that is not on your allowlist for this environment. The message says which.
not_found 404 No such order, SKU or callback in your account.
conflict 409 The order is not in a state that allows this. Retrieving codes before delivery is the common case.
insufficient_balance 422 The order costs more than the wallet holds. Nothing was charged and no order was created.
quantity_limit_exceeded 422 Above the per-order quantity agreed for your account.
amount_out_of_range 422 A free-amount order outside the band the SKU allows.
product_unavailable 422 The SKU is not currently sellable.
supplier_unavailable 502 The upstream supplier could not be reached. Safe to retry with the same reference.
supplier_rejected 502 The supplier refused the order. The wallet was refunded automatically.
rate_limited 429 Above the per-key limit. Wait for `Retry-After` seconds.
internal_error 500 Something failed on our side. Safe to retry with the same reference.

What to retry

  • A 5xx or a network failure is safe to retry with the same reference. That is the whole point of the reference: the retry either creates the order or returns the one already created.
  • A 429 is safe to retry after Retry-After seconds.
  • A 4xx other than 429 will fail again. Fix the request.
  • Never retry with a new reference after a timeout. That is not a retry, it is a second order.

Rate limits

60 req / min / key

Sixty requests a minute, counted per key rather than per account. A nightly reconciliation job on its own key therefore cannot spend the allowance your tills need.

The window is fixed and one minute long. Every response carries the state of it, whether or not it was refused, so you can pace yourself instead of discovering the ceiling by hitting it.

The count is one count. It is shared across every server of ours that answers you, so the limit is the same figure whichever machine your request happens to reach, and it does not quietly rise when we add capacity.

If you need a higher limit, ask your account manager. It is a setting on your account, not a code change.

X-RateLimit-LimitRequests allowed in the current window.
X-RateLimit-RemainingHow many are left in it.
X-RateLimit-ResetUnix seconds at which the window resets.
Retry-AfterOn a 429 only: seconds to wait.

Pagination

Listing endpoints take page and pageSize. Pages are one-based and pageSize is capped at 50.

total counts everything matching the filter, not the rows on this page, so the number of pages is ceil(total / pageSize).

Results are newest first. If rows are being written while you page, prefer a closed date range over walking the pages, or you will see a row twice.

Webhooks

A large order finishes when the supplier finishes it, which may be minutes after the response. Polling works and is documented, but a callback means your system reacts to a delivery instead of asking about it.

  • A callback never contains a code. It says an order reached a state; the codes are fetched over the API afterwards. A misconfigured endpoint therefore cannot leak your inventory.
  • The URL must be https.
  • We retry a failed delivery six times with a widening gap, over about an hour. Answer 2xx as soon as you have the event; do your work afterwards.
  • Your endpoint may be called more than once for the same event. Make handling it idempotent, keyed on orderId and event.

What we send

{
  "event": "order.delivered",
  "orderId": "0f8b4a2c-9d31-4e77-b0a5-6c1e2d3f4a5b",
  "reference": "TILL-4471-20260914-0007",
  "status": "delivered",
  "quantity": 40,
  "codesAvailable": true,
  "occurredAt": "2026-09-14T08:14:22.000Z"
}

Verifying a call is ours

Each request carries X-Regenerate-Signature, X-Regenerate-Timestamp and X-Regenerate-Event. The signature is HMAC-SHA256 over timestamp + "." + rawBody, keyed with the signing secret you received at registration. Compare it in constant time, and refuse a timestamp more than five minutes old — the timestamp is inside the signed material precisely so that a captured call cannot be replayed at you forever.

import { createHmac, timingSafeEqual } from 'node:crypto';

function isOurs(rawBody, headers, secret) {
  const timestamp = Number(headers['x-regenerate-timestamp']);
  if (!Number.isFinite(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;

  const expected = createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  const a = Buffer.from(expected, 'utf8');
  const b = Buffer.from(headers['x-regenerate-signature'] ?? '', 'utf8');
  return a.length === b.length && timingSafeEqual(a, b);
}

Endpoint reference

Authentication

Trading a key for a short-lived token.

POST /v1/auth/token No token needed

Exchange an API key for a short-lived token

The only endpoint that accepts the API key itself. Everything else takes the token this returns, so the long-lived secret sitting in your points of sale crosses the wire as seldom as possible.

The token expires within the hour. Fetch a new one when it does, or shortly before; there is no refresh flow, because exchanging the key again is simpler than one.

The key may travel in the body or in an X-API-Key header, whichever suits your client.

Parameters
X-API-Key header string The key, as an alternative to sending it in the body.
Body
apiKey required string
{
  "apiKey": "rg_live_4XK9ABCD_p7Qm2ZtVx8Ln4Rc9Wd3Yf6Hs"
}
Responses
200 A token you may use until it expires.
accessToken required string Opaque. Send it back; do not parse it.
tokenType required string Bearer
expiresIn required integer Seconds until it expires.
{
  "accessToken": "eyJpc3MiOiJyZWdlbmVyYXRlIiwiYXVkIjoicmV0YWlsZXItYXBpIi4uLg.9Kx2...",
  "tokenType": "Bearer",
  "expiresIn": 3600
}
401 The API key is not valid.
error required object
{
  "error": {
    "code": "unauthenticated",
    "message": "The API key is not valid.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
429 Too many requests.
error required object
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}

Account

Your wallet.

GET /v1/balance

Corporate wallet balance

What your prepaid wallet holds right now. Top-ups are arranged with your account manager and are not part of this API.

Responses
200 The balance.
balance required integer (int64) What the wallet holds. Minor units: 1234 is ₪12.34.
currency required string ILS
asOf required string (date-time) When the balance was read. UTC, ISO 8601.
{
  "balance": 1854000,
  "currency": "ILS",
  "asOf": "2026-09-14T08:00:00.000Z"
}
401 The access token is not valid.
error required object
{
  "error": {
    "code": "unauthenticated",
    "message": "The access token is not valid.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}

Catalog

What you can buy, and what it costs you.

GET /v1/catalog

Products, denominations and your price

One entry per SKU, which is the unit you order. price is what this order will cost your wallet, with your account’s margin already applied.

A SKU is either fixed-value or open-value. A fixed one has a faceValue and you order a quantity of it. An open one has pricing.mode of range and a band, and you send requestedAmount inside that band when you order.

Cache it for minutes rather than hours: prices move with the exchange rate.

Parameters
search query string Substring match on the product name.
category query string console
Responses
200 The catalog as your account sees it.
items required object[]
generatedAt required string (date-time) When this snapshot was taken. UTC, ISO 8601.
{
  "items": [
    {
      "sku": "RG-7KQ4MX",
      "productName": "PlayStation Store",
      "category": "console",
      "fulfillmentType": "code",
      "faceValue": 10000,
      "faceCurrency": "ILS",
      "faceValueInIls": null,
      "price": 9810,
      "currency": "ILS",
      "available": true,
      "pricingMode": "fixed",
      "minAmount": null,
      "maxAmount": null,
      "stepAmount": null
    },
    {
      "sku": "RG-4HWN2C",
      "productName": "Roblox Robux",
      "category": "gaming",
      "fulfillmentType": "code",
      "faceValue": 0,
      "faceCurrency": "ILS",
      "faceValueInIls": null,
      "price": 0,
      "currency": "ILS",
      "available": true,
      "pricingMode": "range",
      "minAmount": 5000,
      "maxAmount": 50000,
      "stepAmount": 1000
    }
  ],
  "generatedAt": "2026-09-14T08:00:00.000Z"
}
401 The access token is not valid.
error required object
{
  "error": {
    "code": "unauthenticated",
    "message": "The access token is not valid.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
GET /v1/catalog/{sku}/availability

Availability before ordering

A cheap pre-check: whether the SKU is sellable, and the largest quantity your account may put in one order.

It is advisory. The binding check happens inside the order, in the same transaction that spends the money, so treat a true here as "worth trying" rather than as a reservation.

Parameters
sku required path string RG-7KQ4MX
Responses
200 Availability and your per-order ceiling.
sku required string
available required boolean
maxQuantityPerOrder required integer The per-order ceiling agreed for your account.
{
  "sku": "RG-7KQ4MX",
  "available": true,
  "maxQuantityPerOrder": 500
}
401 The access token is not valid.
error required object
{
  "error": {
    "code": "unauthenticated",
    "message": "The access token is not valid.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
404 No such SKU.
error required object
{
  "error": {
    "code": "not_found",
    "message": "No such SKU.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}

Orders

Placing orders and collecting codes.

POST /v1/orders

Place an order

reference is your own idempotency key and it is required. Send the same reference again and you get the same order back with status 200 instead of 201 — never a second charge. This is what makes a till safe to retry after a timeout, which is the case that matters: a retry without one is how a retailer buys three hundred cards twice.

Quantity is bounded only by the per-order limit agreed for your account. Our suppliers have batch limits of their own — one of them delivers five cards at a time — and we split large orders internally, so a three-hundred-card order behaves like a three-card one.

The response is immediate and the order comes back created, before the supplier has answered. Wait for the callback, or poll while the status is created or processing, and fetch the codes once it is delivered.

Body
sku required string
quantity required integer
reference required string Your idempotency key. Must be unique per order and stable across retries of that order.
requestedAmount integer Required on an open-value SKU, refused on a fixed one. Minor units, inside the SKU band.

A fixed-value SKU

{
  "sku": "RG-7KQ4MX",
  "quantity": 40,
  "reference": "TILL-4471-20260914-0007"
}

An open-value SKU, with the amount inside the band

{
  "sku": "RG-4HWN2C",
  "quantity": 2,
  "requestedAmount": 12000,
  "reference": "TILL-4471-20260914-0008"
}
Responses
200 This reference was used before. The original order is returned and nothing was charged again.
orderId required string (uuid)
reference required string | null The reference you sent.
sku required string
productName string
quantity required integer
status required string Non-terminal: `created`, which is what a new order comes back as, and `processing`. Terminal: `delivered`, `failed` and `refunded`, and the last two mean the wallet has already been put back. Poll while it is non-terminal. created processing delivered failed refunded
unitPrice integer (int64) Price for one card. Minor units: 1234 is ₪12.34.
totalPrice required integer (int64) What was taken from the wallet. Minor units: 1234 is ₪12.34.
requestedAmount integer | null
currency required string ILS
codesAvailable boolean Whether `GET /v1/orders/{id}/codes` will return them now.
createdAt string (date-time) When the order was accepted. UTC, ISO 8601.
completedAt string (date-time) | null
201 The order was created and your wallet was debited.
orderId required string (uuid)
reference required string | null The reference you sent.
sku required string
productName string
quantity required integer
status required string Non-terminal: `created`, which is what a new order comes back as, and `processing`. Terminal: `delivered`, `failed` and `refunded`, and the last two mean the wallet has already been put back. Poll while it is non-terminal. created processing delivered failed refunded
unitPrice integer (int64) Price for one card. Minor units: 1234 is ₪12.34.
totalPrice required integer (int64) What was taken from the wallet. Minor units: 1234 is ₪12.34.
requestedAmount integer | null
currency required string ILS
codesAvailable boolean Whether `GET /v1/orders/{id}/codes` will return them now.
createdAt string (date-time) When the order was accepted. UTC, ISO 8601.
completedAt string (date-time) | null
{
  "orderId": "0f8b4a2c-9d31-4e77-b0a5-6c1e2d3f4a5b",
  "reference": "TILL-4471-20260914-0007",
  "sku": "RG-7KQ4MX",
  "productName": "PlayStation Store",
  "quantity": 40,
  "status": "created",
  "unitPrice": 9810,
  "totalPrice": 392400,
  "requestedAmount": null,
  "currency": "ILS",
  "codesAvailable": false,
  "createdAt": "2026-09-14T08:14:00.000Z",
  "completedAt": null
}
400 The request did not validate.
error required object
{
  "error": {
    "code": "validation_failed",
    "message": "The request did not validate.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
401 The access token is not valid.
error required object
{
  "error": {
    "code": "unauthenticated",
    "message": "The access token is not valid.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
404 No such SKU.
error required object
{
  "error": {
    "code": "not_found",
    "message": "No such SKU.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
422 The wallet does not hold enough. No order was created.
error required object
{
  "error": {
    "code": "insufficient_balance",
    "message": "The wallet does not hold enough. No order was created.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
429 Too many requests.
error required object
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
GET /v1/orders

List your orders

Newest first, paginated. Use it to reconcile a day across your estate rather than polling orders one by one.

Parameters
page query integer One-based page number.
pageSize query integer Rows per page. The maximum is 50.
status query string
reference query string Your own reference, matched exactly.
from query string (date) Inclusive calendar date. 2026-09-01
to query string (date) Inclusive calendar date. 2026-09-14
Responses
200 One page of orders.
items required object[]
page required integer
pageSize required integer
total required integer Rows matching the filter, not on this page.
{
  "items": [
    {
      "orderId": "0f8b4a2c-9d31-4e77-b0a5-6c1e2d3f4a5b",
      "reference": "TILL-4471-20260914-0007",
      "sku": "RG-7KQ4MX",
      "productName": "PlayStation Store",
      "quantity": 40,
      "status": "delivered",
      "unitPrice": 9810,
      "totalPrice": 392400,
      "requestedAmount": null,
      "currency": "ILS",
      "codesAvailable": true,
      "createdAt": "2026-09-14T08:14:00.000Z",
      "completedAt": "2026-09-14T08:14:22.000Z"
    }
  ],
  "page": 1,
  "pageSize": 25,
  "total": 137
}
401 The access token is not valid.
error required object
{
  "error": {
    "code": "unauthenticated",
    "message": "The access token is not valid.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
GET /v1/orders/{id}

Order status

id may be our order id or the reference you sent. Both work, deliberately: a till whose request timed out knows only what it sent, and telling it to find our id first would be telling it to do the impossible.

Parameters
id required path string Our order id, or your own reference. TILL-4471-20260914-0007
Responses
200 The order.
orderId required string (uuid)
reference required string | null The reference you sent.
sku required string
productName string
quantity required integer
status required string Non-terminal: `created`, which is what a new order comes back as, and `processing`. Terminal: `delivered`, `failed` and `refunded`, and the last two mean the wallet has already been put back. Poll while it is non-terminal. created processing delivered failed refunded
unitPrice integer (int64) Price for one card. Minor units: 1234 is ₪12.34.
totalPrice required integer (int64) What was taken from the wallet. Minor units: 1234 is ₪12.34.
requestedAmount integer | null
currency required string ILS
codesAvailable boolean Whether `GET /v1/orders/{id}/codes` will return them now.
createdAt string (date-time) When the order was accepted. UTC, ISO 8601.
completedAt string (date-time) | null
401 The access token is not valid.
error required object
{
  "error": {
    "code": "unauthenticated",
    "message": "The access token is not valid.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
404 No such order in your account.
error required object
{
  "error": {
    "code": "not_found",
    "message": "No such order in your account.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
GET /v1/orders/{id}/codes

Retrieve delivered codes

Available once the order is delivered. Asking earlier returns 409, not an empty list, so a polling loop cannot mistake "not yet" for "none".

Every retrieval is recorded against the key that made it. Store what you receive: this is a retrieval endpoint, not a vault, and treat the response as you would a card number.

Parameters
id required path string Our order id, or your own reference.
Responses
200 One entry per card in the order.
orderId required string (uuid)
codes required object[]
{
  "orderId": "0f8b4a2c-9d31-4e77-b0a5-6c1e2d3f4a5b",
  "codes": [
    {
      "cardNumber": "5312-8841-9027-4410",
      "pin": "884120",
      "claimUrl": null,
      "expiresAt": "2027-09-14T00:00:00.000Z"
    }
  ]
}
401 The access token is not valid.
error required object
{
  "error": {
    "code": "unauthenticated",
    "message": "The access token is not valid.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
404 No such order in your account.
error required object
{
  "error": {
    "code": "not_found",
    "message": "No such order in your account.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
409 The order has not been delivered yet.
error required object
{
  "error": {
    "code": "conflict",
    "message": "The order has not been delivered yet.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}

Webhooks

Being told when an order finishes.

POST /v1/webhooks

Register an order-status callback

The signing secret is returned once, here, and never again. Keep it: it is what lets you tell our calls from anyone else’s.

The URL must be https.

Body
url required string (uri) Must be https.
events required string[]
{
  "url": "https://tills.example.co.il/hooks/regenerate",
  "events": [
    "order.delivered",
    "order.failed"
  ]
}
Responses
201 Registered. The signing secret is in this response only.
id required string (uuid)
url required string (uri)
events required string[]
status string active disabled
createdAt required string (date-time) When it was registered. UTC, ISO 8601.
signingSecret string Returned once, at registration. Not recoverable afterwards.
{
  "id": "7c2f1a9b-33de-4f0a-8b21-5d7e9c0a1b2c",
  "url": "https://tills.example.co.il/hooks/regenerate",
  "events": [
    "order.delivered",
    "order.failed"
  ],
  "status": "active",
  "createdAt": "2026-09-14T08:00:00.000Z",
  "signingSecret": "whsec_9Kx2Ld8Rm4Tn6Qp1Vs3Wy5Zb7Ce0Fg"
}
400 The URL is not https, or no events were given.
error required object
{
  "error": {
    "code": "validation_failed",
    "message": "The URL is not https, or no events were given.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
401 The access token is not valid.
error required object
{
  "error": {
    "code": "unauthenticated",
    "message": "The access token is not valid.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
GET /v1/webhooks

List your callbacks

Without the signing secrets, which are not recoverable.

Responses
200 Your active callbacks.
webhooks required object[]
{
  "webhooks": [
    {
      "id": "7c2f1a9b-33de-4f0a-8b21-5d7e9c0a1b2c",
      "url": "https://tills.example.co.il/hooks/regenerate",
      "events": [
        "order.delivered",
        "order.failed"
      ],
      "status": "active",
      "createdAt": "2026-09-14T08:00:00.000Z"
    }
  ]
}
401 The access token is not valid.
error required object
{
  "error": {
    "code": "unauthenticated",
    "message": "The access token is not valid.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
DELETE /v1/webhooks/{id}

Remove a callback

Parameters
id required path string (uuid)
Responses
204 Removed. No body.
401 The access token is not valid.
error required object
{
  "error": {
    "code": "unauthenticated",
    "message": "The access token is not valid.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}
404 No such callback in your account.
error required object
{
  "error": {
    "code": "not_found",
    "message": "No such callback in your account.",
    "requestId": "9f1c2b6e-4a10-4b93-9a0e-2f5a1c3d7e88"
  }
}