Skip to content

Webhooks

Webhooks let a workspace receive HTTP callbacks when loyalty events occur (transactions, point changes, tier promotions, pass lifecycle, etc.). Each webhook has a signing secret, an event-type subscription, optional custom headers, and a delivery log with per-attempt detail and retry support.

Auth: Every endpoint is workspace-scoped — pass a Bearer token for a user who is a member of the workspace in the path (createWorkspaceGuard, source: "path").

All paths are mounted under the /wallethero-api prefix. Base URL: https://api.wallethero.app.

A workspace may have at most 25 active webhooks.


List Event Types

GET /wallethero-api/workspace/:workspaceId/webhooks/event-types

Returns the catalog of event types you can subscribe to plus the category names. Subscriptions accept any concrete event name, a category wildcard <category>.*, or the global wildcard *.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/webhooks/event-types" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": {
    "event_types": [
      "field_change.field_updated",
      "transaction.purchase",
      "transaction.refund",
      "engagement.check_in",
      "loyalty.points_earned",
      "loyalty.tier_promoted",
      "pass_lifecycle.pass_installed"
    ],
    "categories": [
      "field_change",
      "transaction",
      "engagement",
      "loyalty",
      "pass_lifecycle"
    ]
  }
}

The full event_types list also includes: field_change.field_increment, field_change.field_decrement, engagement.check_out, engagement.visit, engagement.scan, loyalty.points_spent, loyalty.points_expired, loyalty.points_adjusted, loyalty.tier_demoted, loyalty.reward_redeemed, loyalty.reward_cancelled, loyalty.reward_used, pass_lifecycle.pass_uninstalled, pass_lifecycle.pass_registered, pass_lifecycle.template_changed.

SDK

typescript
const { data } = await wh.webhooks.listEventTypes(workspaceId);

List Webhooks

GET /wallethero-api/workspace/:workspaceId/webhooks

Returns all active (non-deleted) webhooks for the workspace, newest first. Secrets are never included in list responses.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/webhooks" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": [
    {
      "id": "9d1f8e2a-1234-4c5b-9a0e-abc123456789",
      "workspace_id": "WORKSPACE_ID",
      "name": "Orders sync",
      "url": "https://example.com/hooks/wallethero",
      "event_types": ["transaction.purchase", "loyalty.*"],
      "enabled": true,
      "description": null,
      "headers": {},
      "max_attempts": 5,
      "date_created": "2026-06-01T10:00:00.000Z",
      "date_updated": "2026-06-01T10:00:00.000Z"
    }
  ]
}

SDK

typescript
const { data } = await wh.webhooks.list(workspaceId);

Create Webhook

POST /wallethero-api/workspace/:workspaceId/webhooks

Creates a webhook and generates its signing secret. The secret is returned once in this response (and again only on rotation) — store it securely. Responds 201.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Request Body

FieldTypeRequiredDescription
namestringYes1–120 chars
urlstring (URL)YesMust be https://. http:// is only accepted when the server has WALLETHERO_ALLOW_INSECURE_WEBHOOK_URLS=true
event_typesstring[]Yes1–50 entries. Each must be a known event name, a <category>.* wildcard, or *
enabledbooleanNoDefaults to true
descriptionstring | nullNoUp to 500 chars
headersRecord<string, string>NoCustom request headers (max 20)
max_attemptsnumberNoInteger 1–10. Defaults to 5

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/webhooks" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Orders sync",
    "url": "https://example.com/hooks/wallethero",
    "event_types": ["transaction.purchase", "loyalty.*"],
    "max_attempts": 5
  }'

Response (201)

json
{
  "data": {
    "id": "9d1f8e2a-1234-4c5b-9a0e-abc123456789",
    "workspace_id": "WORKSPACE_ID",
    "name": "Orders sync",
    "url": "https://example.com/hooks/wallethero",
    "event_types": ["transaction.purchase", "loyalty.*"],
    "enabled": true,
    "description": null,
    "headers": {},
    "max_attempts": 5,
    "secret": "f3a9...c21e",
    "date_created": "2026-06-01T10:00:00.000Z",
    "date_updated": "2026-06-01T10:00:00.000Z"
  }
}

SDK

typescript
const { data } = await wh.webhooks.create(workspaceId, {
  name: "Orders sync",
  url: "https://example.com/hooks/wallethero",
  event_types: ["transaction.purchase", "loyalty.*"],
});

Get Webhook

GET /wallethero-api/workspace/:workspaceId/webhooks/:id

Returns a single webhook (without its secret).

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
idstring (UUID)Webhook identifier

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/webhooks/WEBHOOK_ID" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": {
    "id": "WEBHOOK_ID",
    "workspace_id": "WORKSPACE_ID",
    "name": "Orders sync",
    "url": "https://example.com/hooks/wallethero",
    "event_types": ["transaction.purchase"],
    "enabled": true,
    "description": null,
    "headers": {},
    "max_attempts": 5,
    "date_created": "2026-06-01T10:00:00.000Z",
    "date_updated": "2026-06-01T10:00:00.000Z"
  }
}

SDK

typescript
const { data } = await wh.webhooks.get(workspaceId, webhookId);

Update Webhook

PATCH /wallethero-api/workspace/:workspaceId/webhooks/:id

Partially updates a webhook. Only the fields you send are changed. The secret is not returned and cannot be set here (use rotate-secret).

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
idstring (UUID)Webhook identifier

Request Body

All fields optional; same constraints as Create.

FieldTypeDescription
namestring1–120 chars
urlstring (URL)Must be https:// (see Create)
event_typesstring[]1–50 valid entries
enabledbooleanEnable/disable delivery
descriptionstring | nullUp to 500 chars
headersRecord<string, string>Max 20
max_attemptsnumberInteger 1–10

Example Request

bash
curl -X PATCH "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/webhooks/WEBHOOK_ID" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": false }'

Response (200)

json
{
  "data": {
    "id": "WEBHOOK_ID",
    "name": "Orders sync",
    "enabled": false,
    "event_types": ["transaction.purchase"],
    "max_attempts": 5,
    "date_updated": "2026-06-02T09:00:00.000Z"
  }
}

SDK

typescript
const { data } = await wh.webhooks.update(workspaceId, webhookId, {
  enabled: false,
});

Delete Webhook

DELETE /wallethero-api/workspace/:workspaceId/webhooks/:id

Soft-deletes the webhook (sets deleted_at). It stops receiving deliveries and disappears from list/get.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
idstring (UUID)Webhook identifier

Example Request

bash
curl -X DELETE "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/webhooks/WEBHOOK_ID" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{ "data": { "success": true } }

SDK

typescript
await wh.webhooks.delete(workspaceId, webhookId);

Rotate Secret

POST /wallethero-api/workspace/:workspaceId/webhooks/:id/rotate-secret

Generates a new signing secret and returns it. The previous secret is immediately invalidated, so update your verification key.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
idstring (UUID)Webhook identifier

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/webhooks/WEBHOOK_ID/rotate-secret" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": {
    "id": "WEBHOOK_ID",
    "name": "Orders sync",
    "secret": "a17b...9f02",
    "event_types": ["transaction.purchase"],
    "max_attempts": 5,
    "date_updated": "2026-06-02T09:05:00.000Z"
  }
}

SDK

typescript
const { data } = await wh.webhooks.rotateSecret(workspaceId, webhookId);

Test Webhook

POST /wallethero-api/workspace/:workspaceId/webhooks/:id/test

Sends a synthetic webhook.test event to the webhook's URL (signed with the current secret, including any custom headers) and returns the immediate result. A delivery row is recorded in a terminal state so the test shows up in the deliveries list. Rate-limited to one test per webhook every 10 seconds.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
idstring (UUID)Webhook identifier

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/webhooks/WEBHOOK_ID/test" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": {
    "success": true,
    "response_status": 200,
    "response_body": "{\"ok\":true}",
    "duration_ms": 142,
    "error": null
  }
}

On failure success is false, response_status may be null (network/timeout), and error carries the reason (e.g. "HTTP 500").

SDK

typescript
const { data } = await wh.webhooks.test(workspaceId, webhookId);

List Deliveries

GET /wallethero-api/workspace/:workspaceId/webhooks/:id/deliveries

Returns the delivery log for a single webhook, newest first, with paging and optional filters. Unlike most list endpoints, the response is { data, meta } at the top level (no extra wrapping).

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
idstring (UUID)Webhook identifier

Query Parameters

ParameterTypeRequiredDefaultDescription
statusstringNoOne of pending, processing, delivered, retrying, abandoned
event_typestringNoFilter by event type
limitnumberNo25Integer 1–200
offsetnumberNo0Integer ≥ 0

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/webhooks/WEBHOOK_ID/deliveries?status=abandoned&limit=50" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": [
    {
      "id": "d1e2...",
      "workspace_id": "WORKSPACE_ID",
      "webhook_id": "WEBHOOK_ID",
      "event_type": "transaction.purchase",
      "event_id": "evt_123",
      "target_url": "https://example.com/hooks/wallethero",
      "status": "delivered",
      "attempt_count": 1,
      "max_attempts": 5,
      "scheduled_for": "2026-06-02T09:00:00.000Z",
      "next_attempt_at": "2026-06-02T09:00:00.000Z",
      "delivered_at": "2026-06-02T09:00:01.000Z",
      "last_error": null,
      "attempts": [
        {
          "attempted_at": "2026-06-02T09:00:01.000Z",
          "status": "delivered",
          "response_status": 200,
          "duration_ms": 120,
          "error": null
        }
      ],
      "date_created": "2026-06-02T09:00:00.000Z",
      "date_updated": "2026-06-02T09:00:01.000Z"
    }
  ],
  "meta": { "total": 1, "limit": 50, "offset": 0 }
}

SDK

typescript
const { data, meta } = await wh.webhooks.listDeliveries(
  workspaceId,
  webhookId,
  { status: "abandoned", limit: 50 },
);

Get Delivery

GET /wallethero-api/workspace/:workspaceId/webhooks/deliveries/:deliveryId

Returns a single delivery with its full payload and per-attempt detail (request/response headers, bodies, durations, errors).

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
deliveryIdstring (UUID)Delivery identifier

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/webhooks/deliveries/DELIVERY_ID" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": {
    "id": "DELIVERY_ID",
    "webhook_id": "WEBHOOK_ID",
    "event_type": "transaction.purchase",
    "event_id": "evt_123",
    "payload": { "...": "full event body" },
    "target_url": "https://example.com/hooks/wallethero",
    "target_headers": {},
    "status": "abandoned",
    "attempt_count": 5,
    "max_attempts": 5,
    "last_error": "HTTP 500",
    "attempts": [
      {
        "attempted_at": "2026-06-02T09:00:01.000Z",
        "status": "failed",
        "request_headers": { "WalletHero-Event": "transaction.purchase" },
        "response_status": 500,
        "response_headers": {},
        "response_body": "internal error",
        "duration_ms": 230,
        "error": "HTTP 500"
      }
    ],
    "date_created": "2026-06-02T09:00:00.000Z",
    "date_updated": "2026-06-02T09:30:00.000Z"
  }
}

SDK

typescript
const { data } = await wh.webhooks.getDelivery(workspaceId, deliveryId);

Retry Delivery

POST /wallethero-api/workspace/:workspaceId/webhooks/deliveries/:deliveryId/retry

Re-queues a delivery (e.g. one that was abandoned) for another attempt and returns the updated delivery record.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
deliveryIdstring (UUID)Delivery identifier

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/webhooks/deliveries/DELIVERY_ID/retry" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": {
    "id": "DELIVERY_ID",
    "status": "pending",
    "attempt_count": 5,
    "max_attempts": 5,
    "next_attempt_at": "2026-06-02T09:31:00.000Z"
  }
}

SDK

typescript
const { data } = await wh.webhooks.retryDelivery(workspaceId, deliveryId);

WalletHero Documentation