# Webhooks Guide

This guide provides instructions for managing webhook subscriptions to item-related events in Wallapop.

## About Webhooks

### Webhook Endpoint Requirements

- The webhook endpoint must accept POST requests.
- It must return a `2xx` HTTP status code to confirm successful receipt of notifications.


### Security & Signature Verification

To ensure the integrity and authenticity of the notifications, each request includes two headers:

- `X-Wallapop-Signature`: HMAC-SHA256 signature of the payload.
- `X-Wallapop-Timestamp`: Timestamp in epoch milliseconds.


You must verify the signature using the shared secret that is provided when the webhook is created.

**Signature Calculation:**

1. Concatenate the JSON payload and the timestamp with a colon (`:`).
2. Hash the resulting string using HMAC-SHA256 with the shared secret.
3. Compare the generated signature with the `X-Wallapop-Signature` header to validate authenticity.


The timestamp is updated with every request to prevent replay attacks.

### Webhook Notification Payload

Each notification contains a JSON payload with the following structure:

```json
{
    "id": "$unique_notification_id",
    "type": "$event_name",
    "occurred_on": "$event_timestamp",
    "data": { ... }  // Event-specific information
}
```

- **`id`**: A unique identifier for the notification, which remains the same across retries.
- **`occurred_on`**: The timestamp (in epoch milliseconds) of when the event occurred.
- **`data`**: Event-specific information (the structure varies based on the event type).


### Example Payloads

The following examples show the payload structure your endpoint will receive for each type of event.
Select an example below to see how the payload structure changes for each event type.

Examples
```shell cURL
curl -i -X POST \
  '' \
  -H 'Content-Type: application/json' \
  -d '{
    "id": "318a05ce-7653-4eeb-9a69-5b425e2edb24",
    "type": "SALE_COMPLETED",
    "occurred_on": 1752820579294,
    "data": {
      "item_id": "9nz0m00eejok"
    }
  }'
```

## Create a Webhook

To create a new webhook, send a **POST** request to `/webhooks`:

Example
```shell cURL
curl -i -X POST \
  https://connect.wallapop.com/webhooks \
  -H 'Authorization: Bearer <YOUR_TOKEN_HERE>' \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "string",
    "events": [
      "SALE_COMPLETED"
    ]
  }'
```

Request Body Schema
```json
{
  "$ref": "#/components/schemas/WebhookRequest",
  "components": {
    "schemas": {
      "WebhookUrl": {
        "type": "string",
        "description": "The URL where webhook notifications will be sent. This endpoint must accept POST requests and handle incoming event notifications securely. It should validate HMAC signatures, return a `2xx` HTTP status code upon successful receipt, and process event payloads accordingly.\n"
      },
      "EventType": {
        "type": "string",
        "description": "Item events that can be subscribed to via webhooks. These events trigger notifications to the webhook when they occur.\n",
        "enum": [
          "SALE_COMPLETED",
          "ITEM_BANNED",
          "ITEM_OUT_OF_STOCK",
          "ITEM_INACTIVATED",
          "ITEM_LISTED",
          "ITEM_RETURNED",
          "DELIVERY_REQUEST_STARTED",
          "DELIVERY_REQUEST_CANCELLED",
          "DELIVERY_REQUEST_FAILED",
          "DELIVERY_REQUEST_EXPIRED",
          "TRANSACTION_CREATED",
          "DISPUTE_CREATED",
          "DISPUTE_QUALITY_CHECK_STARTED",
          "DISPUTE_QUALITY_CHECK_EXPIRED",
          "DISPUTE_QUALITY_CHECK_APPROVED_BY_SELLER",
          "DISPUTE_ISSUE_REPORTED_BY_SELLER",
          "DISPUTE_CANCELLED_BY_WALLAPOP",
          "CHAT_LEAD_CREATED"
        ],
        "example": "SALE_COMPLETED"
      },
      "WebhookRequest": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "$ref": "#/components/schemas/WebhookUrl"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EventType"
            }
          }
        }
      }
    }
  }
}
```

Response Body Schema
```json
{
  "$ref": "#/components/schemas/CreateWebhookResponse",
  "components": {
    "schemas": {
      "WebhookToken": {
        "type": "string",
        "description": "A secure HMAC token linked to the webhook, used to sign requests sent to the webhook URL. This token must be used to verify the authenticity and integrity of incoming webhook requests.\n",
        "example": "KVXQK63TysQsPgjUQiA8nEVN9Og5bGm5AnFGdecuOks175l894ogW3qFsfAQYIfq"
      },
      "CreateWebhookResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the newly created webhook."
          },
          "token": {
            "$ref": "#/components/schemas/WebhookToken"
          }
        }
      }
    }
  }
}
```

A successful request returns a `201 Created` status with the webhook `token`.

## Update a Webhook

Easily update your webhook by changing the endpoint `url` and the list of `events` to which the webhook is subscribed:

Example
```shell cURL
curl -i -X PUT \
  https://connect.wallapop.com/webhooks/5d5ae7a4-2d0d-4be1-a5da-b49e3291e5cd \
  -H 'Authorization: Bearer <YOUR_TOKEN_HERE>' \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "string",
    "events": [
      "SALE_COMPLETED"
    ]
  }'
```

Request Body Schema
```json
{
  "$ref": "#/components/schemas/WebhookRequest",
  "components": {
    "schemas": {
      "WebhookUrl": {
        "type": "string",
        "description": "The URL where webhook notifications will be sent. This endpoint must accept POST requests and handle incoming event notifications securely. It should validate HMAC signatures, return a `2xx` HTTP status code upon successful receipt, and process event payloads accordingly.\n"
      },
      "EventType": {
        "type": "string",
        "description": "Item events that can be subscribed to via webhooks. These events trigger notifications to the webhook when they occur.\n",
        "enum": [
          "SALE_COMPLETED",
          "ITEM_BANNED",
          "ITEM_OUT_OF_STOCK",
          "ITEM_INACTIVATED",
          "ITEM_LISTED",
          "ITEM_RETURNED",
          "DELIVERY_REQUEST_STARTED",
          "DELIVERY_REQUEST_CANCELLED",
          "DELIVERY_REQUEST_FAILED",
          "DELIVERY_REQUEST_EXPIRED",
          "TRANSACTION_CREATED",
          "DISPUTE_CREATED",
          "DISPUTE_QUALITY_CHECK_STARTED",
          "DISPUTE_QUALITY_CHECK_EXPIRED",
          "DISPUTE_QUALITY_CHECK_APPROVED_BY_SELLER",
          "DISPUTE_ISSUE_REPORTED_BY_SELLER",
          "DISPUTE_CANCELLED_BY_WALLAPOP",
          "CHAT_LEAD_CREATED"
        ],
        "example": "SALE_COMPLETED"
      },
      "WebhookRequest": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "$ref": "#/components/schemas/WebhookUrl"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EventType"
            }
          }
        }
      }
    }
  }
}
```

A successful request returns a `204 No Content` status.

## Retrieve Webhooks

To view your webhooks, send a **GET** request to `/webhooks`.

Example
```shell cURL
curl -i -X GET \
  https://connect.wallapop.com/webhooks \
  -H 'Authorization: Bearer <YOUR_TOKEN_HERE>'
```

Response Body Schema
```json
{
  "$ref": "#/components/schemas/FindWebhookResponse",
  "components": {
    "schemas": {
      "WebhookUrl": {
        "type": "string",
        "description": "The URL where webhook notifications will be sent. This endpoint must accept POST requests and handle incoming event notifications securely. It should validate HMAC signatures, return a `2xx` HTTP status code upon successful receipt, and process event payloads accordingly.\n"
      },
      "WebhookToken": {
        "type": "string",
        "description": "A secure HMAC token linked to the webhook, used to sign requests sent to the webhook URL. This token must be used to verify the authenticity and integrity of incoming webhook requests.\n",
        "example": "KVXQK63TysQsPgjUQiA8nEVN9Og5bGm5AnFGdecuOks175l894ogW3qFsfAQYIfq"
      },
      "EventType": {
        "type": "string",
        "description": "Item events that can be subscribed to via webhooks. These events trigger notifications to the webhook when they occur.\n",
        "enum": [
          "SALE_COMPLETED",
          "ITEM_BANNED",
          "ITEM_OUT_OF_STOCK",
          "ITEM_INACTIVATED",
          "ITEM_LISTED",
          "ITEM_RETURNED",
          "DELIVERY_REQUEST_STARTED",
          "DELIVERY_REQUEST_CANCELLED",
          "DELIVERY_REQUEST_FAILED",
          "DELIVERY_REQUEST_EXPIRED",
          "TRANSACTION_CREATED",
          "DISPUTE_CREATED",
          "DISPUTE_QUALITY_CHECK_STARTED",
          "DISPUTE_QUALITY_CHECK_EXPIRED",
          "DISPUTE_QUALITY_CHECK_APPROVED_BY_SELLER",
          "DISPUTE_ISSUE_REPORTED_BY_SELLER",
          "DISPUTE_CANCELLED_BY_WALLAPOP",
          "CHAT_LEAD_CREATED"
        ],
        "example": "SALE_COMPLETED"
      },
      "FindWebhookResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the webhook.",
            "example": "a1234b567c890"
          },
          "url": {
            "$ref": "#/components/schemas/WebhookUrl"
          },
          "token": {
            "$ref": "#/components/schemas/WebhookToken"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EventType"
            }
          }
        }
      }
    }
  }
}
```

If successful, you'll get a `200 OK` status with a list of your webhooks.

## Update a Webhook Token

Need to refresh your webhook token? Send a **PATCH** request to `/webhooks/{webhookId}/token`, including the `webhookId` in the path:

Example
```shell cURL
curl -i -X PATCH \
  https://connect.wallapop.com/webhooks/5d5ae7a4-2d0d-4be1-a5da-b49e3291e5cd/token \
  -H 'Authorization: Bearer <YOUR_TOKEN_HERE>'
```

Response Body Schema
```json
{
  "$ref": "#/components/schemas/WebhookTokenUpdatedResponse",
  "components": {
    "schemas": {
      "WebhookToken": {
        "type": "string",
        "description": "A secure HMAC token linked to the webhook, used to sign requests sent to the webhook URL. This token must be used to verify the authenticity and integrity of incoming webhook requests.\n",
        "example": "KVXQK63TysQsPgjUQiA8nEVN9Og5bGm5AnFGdecuOks175l894ogW3qFsfAQYIfq"
      },
      "WebhookTokenUpdatedResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the updated webhook."
          },
          "token": {
            "$ref": "#/components/schemas/WebhookToken"
          }
        }
      }
    }
  }
}
```

You’ll receive a `200 OK` status with your new webhook `token`.

## Delete a Webhook

To delete a webhook, send a **DELETE** request to `/webhooks/{webhookId}`, including the `webhookId` in the path:

```shell cURL
curl -i -X DELETE \
  https://connect.wallapop.com/webhooks/5d5ae7a4-2d0d-4be1-a5da-b49e3291e5cd \
  -H 'Authorization: Bearer <YOUR_TOKEN_HERE>'
```

A successful request will return a `204 No Content` status.