# Webhook

Webhooks let your systems receive real-time notifications when something happens in The Wallet Crew. You register an HTTPS endpoint, select events, and The Wallet Crew sends a `POST` request each time one of those events occurs.

This is the simplest way to keep your CRM, analytics, or operational systems in sync without polling APIs.

<details>

<summary><strong>Real-world examples</strong></summary>

* Send a “pass installed” event to your CRM to measure adoption per campaign.
* Trigger a customer welcome journey when a customer profile is upserted.
* Log pass lifecycle events (`Pass:Created`, `Pass:Updated`) to your data warehouse.
* Track in-store QR usage by listening to redirect events (`Redirect:Redirected`).

</details>

{% hint style="info" %}
If you also enforce network restrictions, you can allowlist The Wallet Crew outgoing IPs. Do not rely on IPs only. Always validate `x-neostore-signature`.

See [Infrastructure](https://docs.thewalletcrew.io/develop/architecture/infrastructure).
{% endhint %}

## Create and manage a webhook

You manage webhooks from the API endpoint `POST /api/{tenantId}/webhooks`. A webhook defines three things: where to send requests, which events to send, and whether the webhook is enabled.

### Create a webhook (API)

When creating a webhook, send:

* `endpoint`: the HTTPS URL that will receive `POST` requests.
* `events`: the events you want to subscribe to.
* `enabled`: whether delivery is active.

You can subscribe to multiple events in one webhook. You can also use `*` to subscribe to all sub-events under a category.

Example: subscribe to all customer events and to `Pass:Created`.

```json
{
  "events": ["Customer:*", "Pass:Created"]
}
```

{% hint style="warning" %}
Treat `signatureSecret` like a password. Store it securely. Use it only server-side.
{% endhint %}

```bash
curl -X 'POST' \
  'https://app.neostore.cloud/api/{tenantId}/webhooks' \
  -H 'X-API-KEY: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "description": "Sync The Wallet Crew events to our CRM",
  "events": [
    "Customer:*",
    "Pass:Created"
  ],
  "endpoint": "https://your-endpoint-url.com/webhook",
  "enabled": true
}'
```

If creation succeeds, the API returns the webhook object, including `id` and `signatureSecret`.

```json
{
  "id": "UniqueWebhookID",
  "signatureSecret": "GeneratedSignatureSecret",
  "description": "Sync The Wallet Crew events to our CRM",
  "events": ["Customer:*", "Pass:Created"],
  "endpoint": "https://your-endpoint-url.com/webhook",
  "enabled": true
}
```

### Update, list, and delete

You can manage webhooks using `GET`, `PATCH`, and `DELETE` on the same resource.

For the full API spec, see the [API reference](https://docs.thewalletcrew.io/develop/api-reference).

<div data-with-frame="true"><figure><img src="https://3566051324-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWLc8AHXW4tdrAXUBfrYF%2Fuploads%2Fgit-blob-fe3004196dd6482c17306a982a89f8ee2caba07d%2Fwebhook_2.png?alt=media" alt="Webhook configuration screen showing event subscriptions and endpoint URL."><figcaption><p>Configure which events are delivered to which endpoint.</p></figcaption></figure></div>

## What The Wallet Crew sends (headers and payload)

Each webhook delivery is an HTTP `POST` request with headers and a JSON body. The body depends on the event type. Every payload includes the event metadata fields prefixed with `__`.

### HTTP headers

* `x-neostore-signature`: HMAC SHA-256 signature of the request body, generated using your `signatureSecret`.
* `x-neostore-eventname`: event name that triggered the webhook (example: `Customer:Upserted`).
* `x-neostore-tenantid`: tenant identifier in The Wallet Crew.

### Example payload

```json
{
  "passId": "ExamplePassID",
  "passType": "user",
  "identifiers": {
    "shopifyId": "ExampleShopifyID"
  },
  "metadata": null,
  "__id": "UniqueEventID",
  "__creationDate": "2025-02-17T12:27:31.6316643Z",
  "__eventName": "Pass:Created"
}
```

### How to process events reliably

Use `__id` as an idempotency key. If your endpoint receives the same payload twice, you can safely ignore the duplicate.

Keep your handler fast. A common pattern is to validate the signature, enqueue the event internally, then return `2xx`.

## Verify webhook authenticity (signature)

Validate every webhook request using `x-neostore-signature`. This ensures the request body was sent by The Wallet Crew and was not modified in transit.

To validate it, compute an HMAC SHA-256 of the raw request body using your `signatureSecret`, then compare it with the header value.

{% hint style="warning" %}
Signature validation must use the exact raw body bytes you received. Do not re-serialize JSON before hashing.
{% endhint %}

## Common events

The event list evolves. Use the API reference as the source of truth for event names and payload shapes: [API reference](https://docs.thewalletcrew.io/develop/api-reference).

Below are the most common events teams integrate with.

<details>

<summary><strong>Customer events</strong></summary>

`Customer:Upserted` is sent when a customer is created or updated.

```json
{
  "__id": "string",
  "__creationDate": "YYYY-MM-DDTHH:mm:ssZ",
  "__eventName": "Customer:Upserted",
  "flowName": "string",
  "identifiers": {},
  "attributes": {}
}
```

</details>

<details>

<summary><strong>Pass lifecycle events</strong></summary>

Typical pass lifecycle events:

* `Pass:Created`
* `Pass:Installed`
* `Pass:Uninstalled`
* `Pass:Updated`
* `Pass:UpdateSent`

Example: `Pass:Installed` includes device fields.

```json
{
  "__id": "string",
  "__creationDate": "YYYY-MM-DDTHH:mm:ssZ",
  "__eventName": "Pass:Installed",
  "passId": "string",
  "passType": "string",
  "identifiers": {},
  "metadata": {},
  "device": "string",
  "deviceIdentifier": "string"
}
```

</details>

<details>

<summary><strong>Redirect events</strong></summary>

`Redirect:Redirected` is sent when a user opens a minified URL (redirect).

```json
{
  "__id": "string",
  "__creationDate": "YYYY-MM-DDTHH:mm:ssZ",
  "__eventName": "Redirect:Redirected",
  "redirectId": "string",
  "newUri": "string"
}
```

</details>

## FAQ

<details>

<summary><strong>Can I subscribe to all events?</strong></summary>

Yes. Use the `*` wildcard in the `events` array, for example `Customer:*`, to subscribe to all customer sub-events.

Use this carefully. You may receive more events than you need.

</details>

<details>

<summary><strong>Should my endpoint be public?</strong></summary>

Yes. The endpoint must be reachable from The Wallet Crew over HTTPS.

If you restrict inbound traffic, allowlist The Wallet Crew outgoing IPs and still validate the signature.

</details>

<details>

<summary><strong>Where do I find the exact payload schema for an event?</strong></summary>

Use the API reference. It is the source of truth for event names and payload shapes.

Start here: [API reference](https://docs.thewalletcrew.io/develop/api-reference).

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.thewalletcrew.io/develop/guides/webhook.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
