# Setting up an endpoint (/en/integrate/webhooks/setting-up-an-endpoint)

Add a webhook endpoint in the dashboard, pick the events you care about, and store the signing secret on your server.

A webhook endpoint is a URL on your server that XPay POSTs JSON events to whenever something interesting happens: a Checkout Session completes, a Refund lands, a Customer is created. You add it once in the dashboard, pick the events you want, and copy the signing secret onto your server so you can verify every payload before acting on it.

<Callout type="info">
  Webhook endpoints are managed only in the dashboard, not over the API. The same is true for the
  signing secret. Do everything below from `app.xpay.app`.
</Callout>

## Open the Webhooks page [#open-the-webhooks-page]

In the dashboard, open the **Developers** page and click the **Webhooks** tab. The list shows every endpoint configured for the current mode.

Test mode and live mode keep separate endpoint lists. Use the **Test** / **Live** switch at the bottom of the sidebar to flip between them. An endpoint created in test mode does not exist in live mode, and vice versa.

## Create an endpoint [#create-an-endpoint]

<Steps>
  <Step>
    ### Click **Add endpoint** [#click-add-endpoint]

    Top-right of the Webhooks tab. If the list is empty, the same action is on the empty state as **Create webhook endpoint**.

    A side panel opens with two required fields: the URL and the events.
  </Step>

  <Step>
    ### Enter the endpoint URL [#enter-the-endpoint-url]

    Type a fully qualified URL into **Endpoint URL**. The form validates the shape, not reachability. The URL must be one your server actually serves.

    A few rules:

    * **Include the protocol** (`https://...`). The form rejects values that don't parse as URLs.
    * **Use HTTPS in production.** Plain HTTP is accepted by the form, but a reachable HTTPS URL with a valid certificate is what you want once you ship. `http://localhost:...` is fine in test mode while you build (see [Local development](/integrate/webhooks/local-development) for tunnels).
    * **One endpoint per URL.** If you need to fan out to two systems, create two endpoints with different URLs, or have one handler forward.
  </Step>

  <Step>
    ### Pick events to listen to [#pick-events-to-listen-to]

    Below the URL field is **Select events to listen to**. The picker is split into two sections:

    * A **Recommended** section at the top, surfacing the high-signal events most integrations subscribe to first (Checkout Session completion, refund lifecycle, customer lifecycle).
    * An **Advanced** collapsible below it that holds the rest, grouped by resource.

    Click any group to expand it and tick individual events. Each group has an in-group **Select all** checkbox if you want every event in that group at once. The same resource can appear in both sections when its events span both buckets. Checkout Sessions, for example, shows `checkout.session.completed` under Recommended and the rest of its lifecycle under Advanced.

    You must pick at least one. A common starting set:

    * `checkout.session.completed` for the lifecycle of the session you created.
    * `refund.created` and `refund.failed` to track reversals.
    * `customer.created`, `customer.updated`, `customer.deleted` to keep your customer table in sync.

    Add `charge.succeeded` / `charge.failed` (under Advanced → Charges) if your data model needs to react at the charge level. Subscribe only to the events your handler actually reads. every subscription costs a delivery, a retry budget, and a signature verification on your server.

    For the full list, see [Event reference](/integrate/webhooks/event-reference).
  </Step>

  <Step>
    ### Click **Add endpoint** to save [#click-add-endpoint-to-save]

    XPay creates the endpoint and immediately surfaces a **Webhook signing secret** dialog with a `whsec_*` value.

    **Copy it now.** The dashboard will not show it again. If you lose it, your only path is to delete the endpoint and create a new one.

    Store it on the server that runs your handler, typically as an environment variable like `XPAY_WEBHOOK_SECRET` loaded once at boot. Click **Done** to close the dialog and return to the list.
  </Step>
</Steps>

## The signing secret [#the-signing-secret]

Every endpoint has its own `whsec_*` secret. XPay signs every event delivered to that endpoint with HMAC-SHA256 using this secret, and your handler uses the same secret to confirm that an incoming event really came from XPay.

Three rules:

* **Never put the secret in client code.** It belongs on the server. A leaked secret lets anyone forge events into your handler.
* **Keep test and live secrets separate.** Each endpoint has its own. Don't reuse one environment variable for both.
* **Treat the secret as opaque.** It's a random string with a `whsec_` prefix. Don't parse it, don't truncate it.

If you suspect a secret leaked, delete the endpoint and recreate it. The dashboard does not surface a rotate-secret action today.

## Test mode and live mode endpoints [#test-mode-and-live-mode-endpoints]

Test and live are isolated environments with their own data and their own webhook endpoints. The mode switch decides which list the dashboard shows; the API key prefix (`sk_test_*` vs `sk_live_*`) decides which list events fire against.

You'll typically want at least two endpoints:

* A **test endpoint** pointing at a tunnel into your dev box, or at a staging server.
* A **live endpoint** pointing at the URL your production server actually serves.

Each gets its own signing secret. Build and verify against the test endpoint first, then add the live endpoint right before you flip your account into live mode.

## Manage your endpoints [#manage-your-endpoints]

The actions menu on each row (the `⋮` button at the right) has three options.

| Action                                   | What it does                                                                                                                       |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| **Edit webhook**                         | Change the URL, change the subscribed events, or both. The signing secret stays the same, so your verification code keeps working. |
| **Disable webhook** / **Enable webhook** | Stop or resume deliveries without losing the configuration. Useful while debugging a handler.                                      |
| **Delete webhook**                       | Permanently removes the endpoint. The signing secret becomes invalid. Pending deliveries to that endpoint stop.                    |

Disabled endpoints stay in the list with a **Disabled** badge. They receive no events and run no retries while disabled. Re-enabling resumes new deliveries; events that fired during the disabled window are not back-filled.

## Where to next [#where-to-next]

<Cards>
  <Card icon="<ShieldCheck />" title="Verifying signatures" href="/integrate/webhooks/verifying-signatures">
    Confirm every event came from XPay before you trust it. HMAC-SHA256 with replay protection, in
    Node and Python.
  </Card>

  <Card icon="<RefreshCw />" title="Replaying and retries" href="/integrate/webhooks/replaying-and-retries">
    The retry schedule when your handler returns non-2xx, and how to manually replay an event from
    the dashboard.
  </Card>

  <Card icon="<Terminal />" title="Local development" href="/integrate/webhooks/local-development">
    Tunnel webhook deliveries to your laptop while you build the handler.
  </Card>

  <Card icon="<Braces />" title="Event reference" href="/integrate/webhooks/event-reference">
    Every event you can subscribe to, when it fires, and the object it carries.
  </Card>

  <Card icon="<Network />" title="Object model" href="/integrate/object-model">
    What each event's `data.object` contains, and which IDs to keep on your order record.
  </Card>

  <Card icon="<FlaskConical />" title="Test mode and test cards" href="/get-started/test-mode">
    Run real test payments through your endpoint before going live.
  </Card>
</Cards>