# Webhook events

> Headers, signatures, retries and the payload of each event AdQuest sends to your endpoint.

Source: https://dashboard.adquest.batriders.ir/docs/api/webhook-events · Audience: Developers · Section: API reference

## The request

AdQuest sends a POST with a JSON body to your HTTPS endpoint. Answer with any 2xx status within 10 seconds.

| Header | Value |
| --- | --- |
| X-AdQuest-Event | The event type, such as quest.completed. |
| X-AdQuest-Event-Id | A stable ID for this event. Use it to ignore duplicates. |
| X-AdQuest-Timestamp | Unix time when this attempt was sent. |
| X-AdQuest-Signature | sha256= and the hex HMAC-SHA256 of the raw body. |
| X-AdQuest-Signature-V2 | t=<unix>,v1=<hex>. The HMAC covers the timestamp and the body, so you can reject old requests. |

## Verify the timestamped signature

Prefer X-AdQuest-Signature-V2. It signs the string timestamp.body with your webhook signing secret. Reject a request whose timestamp is more than 5 minutes old.

**Node.js**

```typescript
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyWebhookV2(rawBody: string, header: string, secret: string, toleranceSeconds = 300) {
  const match = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header);
  if (!match) return false;
  if (Math.abs(Date.now() / 1000 - Number(match[1])) > toleranceSeconds) return false;
  const expected = createHmac("sha256", secret).update(match[1] + "." + rawBody).digest("hex");
  return timingSafeEqual(Buffer.from(match[2]), Buffer.from(expected));
}
```

- [Webhooks guide](https://dashboard.adquest.batriders.ir/docs/webhooks): Registering an endpoint and handling retries.

## Retries

- A delivery that does not get a 2xx is retried, up to 8 attempts in all.
- The wait grows between attempts: 1 minute, 5 minutes, 30 minutes, 2 hours, then 12 hours.
- After the last attempt the delivery is marked dead. The publisher can replay it from the delivery history in the dashboard.
- A retried delivery keeps the same X-AdQuest-Event-Id.

## Events AdQuest sends

| Event | When |
| --- | --- |
| quest.completed | A participant’s quest completion is settled. |
| lottery.winner | A participant wins a lottery prize. |
| fulfillment.requested | A reward must be delivered by you. HTTPS endpoints only. |

> **Other event names.** These three are the only events AdQuest sends. Any other event name is rejected when you save a webhook.

## quest.completed

**Payload**

```json
{
  "quest_id": 23,
  "platform_id": 7,
  "platform_user_id": 55,
  "external_user_id": "player-42",
  "tokens": 10,
  "completed_at": "2026-01-01T12:00:00+00:00"
}
```

tokens is the participant’s share. The Event-Id is quest.completed:{completion id}.

## lottery.winner

**Payload**

```json
{
  "lottery_id": 4,
  "claim_id": 91,
  "external_user_id": "player-42",
  "ticket_number": "1G2736",
  "award_name": "Gift card",
  "claim_expires_at": "2026-02-01T00:00:00+00:00"
}
```

The payload has no email or phone. To send the winner’s email, post it to the winner contact route.

- [Post a winner’s contact](https://dashboard.adquest.batriders.ir/docs/api/server-routes#winner-contact): POST /platform/lottery-claims/{claim}/contact

## fulfillment.requested

**Payload for a quest reward**

```json
{
  "quest_id": 23,
  "completion_id": 301,
  "external_user_id": "player-42",
  "occurred_at": "2026-01-01T12:00:00+00:00",
  "fulfillment_id": 12,
  "idempotency_key": "fulfillment:...",
  "amount": 10,
  "asset": "ADQ_TOKEN",
  "kind": "quest_reward"
}
```

**Payload for a lottery prize**

```json
{
  "lottery_id": 4,
  "claim_id": 91,
  "award_detail_id": 17,
  "occurred_at": "2026-01-01T12:00:00+00:00",
  "fulfillment_id": 13,
  "idempotency_key": "fulfillment:lottery_claim:91",
  "amount": 1,
  "asset": "PRIZE",
  "kind": "lottery_prize"
}
```

Deliver the reward, then confirm it with the acknowledge route. Repeating the same external_reference is safe.

- [Acknowledge a fulfillment](https://dashboard.adquest.batriders.ir/docs/api/server-routes#acknowledge-fulfillment): POST /platform/fulfillments/{id}/acknowledge
