> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paymove.io/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> ## paymove integration rules for AI agents
>
> These rules are verified against the live API. Follow them exactly.
>
> 1. Amounts are INTEGERS in grosze (minor units). `1000` means 10.00 PLN. PLN is the only supported currency and there is no currency field in the API.
> 2. A float `price` (e.g. `12.99`) is SILENTLY TRUNCATED to 12 grosze and still returns HTTP 200. Always send an integer: `Math.round(pln * 100)`.
> 3. Authenticate with the `X-API-KEY` header. Never `Authorization: Bearer`. Sandbox keys start with `sk_test_`, production keys with `sk_live_`.
> 4. Server-side only. A call from a merchant page is rejected. Never put the key in frontend code.
> 5. Unknown request fields are silently ignored and still return HTTP 200 — a wrong body shape looks like success. Match the documented shape exactly.
> 6. A missing `externalId` returns HTTP 500 `Something went wrong`, not 400. A missing `price` or `details.returnUrl` returns HTTP 200 and a usable `redirectUrl` — no error at all. Validate the body yourself before sending it.
> 7. Always verify the `X-Paymove-Signature` header on incoming webhooks before trusting them: https://docs.paymove.io/en/webhook-signature.md
> 8. Errors are `{"status": <int>, "message": "<text>"}`. Branch on the HTTP status only — never on `message`, which is unstable and leaks internal class names.
> 9. There is no rate limiting, no HTTP 429, no HTTP 422, no `Idempotency-Key` header and no API versioning. Do not write code that handles them.
> 10. Re-POSTing an `externalId` that already exists returns HTTP 200 with the ORIGINAL `redirectUrl` and silently discards EVERY field you send — the new price, description and details are all ignored. Use `PATCH /api/pay/product/{productId}/subproduct/{externalId}` to change a price.
> 11. Webhooks fire only on `COMPLETED` by default, and `retries` defaults to `0` (no retries) unless you set it explicitly. Delivery counts as successful when the HTTP status equals `expectedCode` — the response body is never inspected.
> 12. Never fulfil an order on the `returnUrl` redirect. The checkout does not redirect there by itself: the customer has to click "back to shop", and inside the widget modal that redirect never happens. Fulfil only in the webhook handler, after verifying the signature.
> 13. Do not pin a version of `@paymove-io/sdk` — install the latest.
> 14. Full documentation index: https://docs.paymove.io/llms.txt · Copy-paste quickstart: https://docs.paymove.io/en/quickstart.md · Agent skill: https://docs.paymove.io/skill.md

# DocPay: webhooks

> Add a webhook to DocPay and receive automatic notifications about every completed payment.

A webhook automatically notifies your system about a completed payment. Configuration has two steps: **register a webhook**, then **assign it to a product**. From then on, events (e.g. successful payment) for that product are sent to your endpoint.

All requests are authorized with an API key sent in the `X-API-KEY` header.

<Info>
  Examples use the sandbox environment: `https://gateway-api.sandbox.paymove.io`. You need your `partnerId` and the `productId` of a product created in the [DocPay: basic integration](/en/products/docpay/tutorial) tutorial.
</Info>

## Step 1: Register webhook

```bash theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
curl --request POST \
  --url https://gateway-api.sandbox.paymove.io/api/pay/plugin/webhook \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  --data '{
    "name": "PaymentSuccessHook",
    "endpoint": "https://example.com/webhooks/payment-success",
    "method": "POST",
    "requestTemplate": {
      "productId": "productId",
      "event": "event"
    },
    "responseTemplate": {
      "status": "ok"
    },
    "expectedCode": 200,
    "expectedResponse": "{ \"status\": \"ok\" }",
    "retries": 3,
    "partnerId": "78562c79-2f5c-4415-8af4-c871eea92ef2",
    "type": "PAYMENT",
    "headers": {
      "Authorization": ["Bearer abc123"],
      "Content-Type": ["application/json"]
    }
  }'
```

| Field              | Description                                                           |
| ------------------ | --------------------------------------------------------------------- |
| `name`             | Webhook name (visible in configuration).                              |
| `endpoint`         | URL Paymove sends the request to.                                     |
| `method`           | HTTP method (e.g. `POST`).                                            |
| `requestTemplate`  | Request body template (variables substituted by Paymove).             |
| `responseTemplate` | Expected response structure from the partner.                         |
| `expectedCode`     | Expected HTTP response code (e.g. 200).                               |
| `expectedResponse` | Expected response body (e.g. `{ "status": "ok" }`).                   |
| `retries`          | Number of retries on failure.                                         |
| `partnerId`        | Partner identifier (assigned by Paymove).                             |
| `type`             | Event type - for payment notifications: `PAYMENT`.                    |
| `headers`          | Headers sent with the request (e.g. `Authorization`, `Content-Type`). |

**Response (200):**

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "id": "18e19688-bdda-4843-8777-0f04d0143c77",
  "name": "PaymentSuccessHook",
  "endpoint": "https://example.com/webhooks/payment-success",
  "method": "POST",
  "requestTemplate": {
    "productId": "productId",
    "event": "event"
  },
  "responseTemplate": {
    "status": "ok"
  },
  "expectedCode": 200,
  "expectedResponse": "{ \"status\": \"ok\" }",
  "retries": 3,
  "type": "PAYMENT",
  "headers": {
    "Authorization": ["Bearer abc123"],
    "Content-Type": ["application/json"]
  },
  "signingSecret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
```

The `id` field in the response is the webhook identifier (`webhookId`), which you will use in step 2.

<Warning>
  The response contains a `signingSecret` - a secret for verifying the signature of requests coming from Paymove. Store it securely in your system and do not expose it publicly.
</Warning>

## Step 2: Assign webhook to product

The webhook only becomes active after linking it to a product. Replace `webhookId` (from step 1) and `productId` in the URL.

```bash theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
curl --request POST \
  --url https://gateway-api.sandbox.paymove.io/api/pay/plugin/webhook/18e19688-bdda-4843-8777-0f04d0143c77/products/d0a834f9-94a4-4b3a-aa10-27d911e3633f \
  --header 'X-API-KEY: sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
```

**Response (200):** the webhook object (same as in step 1), confirming the link.

From then on, payment events for this product are sent to your `endpoint`.

## Verify the configuration

You can check the products linked to a webhook with:

```bash theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
curl --url https://gateway-api.sandbox.paymove.io/api/pay/plugin/webhook/18e19688-bdda-4843-8777-0f04d0143c77/products \
  --header 'X-API-KEY: sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
```

**Response (200):** an array of products linked to the webhook:

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
[
  {
    "id": "d0a834f9-94a4-4b3a-aa10-27d911e3633f",
    "name": "Test Product",
    "productType": "PAY",
    "status": "ACTIVE"
  }
]
```

## How does Paymove call your endpoint?

After a completed payment, Paymove sends a request to your `endpoint` following `requestTemplate` and the configured `headers`. Your system should respond with `expectedCode` and `expectedResponse` (e.g. `200` and `{ "status": "ok" }`). On failure, Paymove retries as many times as set in `retries`.

## What's next?

<CardGroup cols={1}>
  <Card title="REST API" icon="code" href="/en/products/docpay/rest-api">
    Full webhook endpoint reference: list, details, update.
  </Card>
</CardGroup>
