Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 20 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ Polls Soroban RPC for SmartDrop contract events and stores decoded event state i
Registers subscriber endpoints for SmartDrop lifecycle events and delivers signed JSON payloads with retry tracking.

**Events:**
- `airdrop.created`
- `airdrop.executing`
- `airdrop.completed`
- `airdrop.failed` — fired automatically when an airdrop expires (see below), in addition to any other failure path
- `recipient.claimed`
- `airdrop.failed` — **(Active)** fired automatically when an airdrop expires (see below), in addition to any other failure path
- `airdrop.created` — *(Planned, not yet implemented)*
- `airdrop.executing` — *(Planned, not yet implemented)*
- `airdrop.completed` — *(Planned, not yet implemented)*
- `recipient.claimed` — *(Planned, not yet implemented)*

**Features:**
- Webhook endpoint CRUD with secrets kept out of list responses
Expand Down Expand Up @@ -486,19 +486,20 @@ curl http://localhost:4000/health

## Webhooks

Register endpoints that receive HTTP POST callbacks when SmartDrop indexes farming/pool events.
Register endpoints that receive HTTP POST callbacks when SmartDrop indexes contract lifecycle events or price alerts.

### Supported event types

| Event | Description |
|-------|-------------|
| `pool.created` | A new farming pool was created on-chain |
| `pool.assets_locked` | Assets were locked into a pool |
| `pool.assets_unlocked` | Assets were unlocked from a pool |
| `pool.rewards_distributed` | Pool distributed rewards to participants |
| `pool.closed` | Pool was closed |
| `price.alert` | Existing price-alert event |
| `*` | Wildcard — subscribe to every known event |
| Event | Status | Description |
|-------|--------|-------------|
| `airdrop.failed` | **Active** | Fired automatically when an airdrop expires |
| `price.alert` | **Active** | Existing price-alert event |
| `pool.created` | *Planned* | A new farming pool was created on-chain (not yet implemented) |
| `pool.assets_locked` | *Planned* | Assets were locked into a pool (not yet implemented) |
| `pool.assets_unlocked` | *Planned* | Assets were unlocked from a pool (not yet implemented) |
| `pool.rewards_distributed` | *Planned* | Pool distributed rewards to participants (not yet implemented) |
| `pool.closed` | *Planned* | Pool was closed (not yet implemented) |
| `*` | - | Wildcard — subscribe to every known event |

### API

Expand All @@ -509,7 +510,7 @@ Content-Type: application/json

{
"url": "https://example.com/webhooks/smartdrop",
"events": ["pool.assets_locked", "pool.rewards_distributed"],
"events": ["airdrop.failed", "price.alert"],
"secret": "whsec_at_least_16_chars", // optional, generated if omitted
"description": "Production webhook" // optional
}
Expand All @@ -531,6 +532,9 @@ POST /api/v1/webhooks/:id/test
```
Sends a synthetic `pool.assets_locked` payload to the registered URL and returns the resulting delivery summary. Limited to 5 calls/min/IP by default.

> [!IMPORTANT]
> A successful test delivery only confirms that the webhook URL can successfully receive deliveries from the service. It does **not** imply that the subscribed event type will ever fire for real. For example, `pool.assets_locked` is a planned event and is not yet implemented (it will never fire in production). Only `airdrop.failed` and `price.alert` are actively dispatched today.

#### Inspect deliveries (admin dashboard feed)
```
GET /api/v1/webhooks/:id/deliveries?limit=50
Expand Down
53 changes: 40 additions & 13 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,8 @@ paths:
example:
url: https://example.com/webhooks/smartdrop
events:
- airdrop.completed
- recipient.claimed
- airdrop.failed
- price.alert
secret: whsec_myverys3cretkey
x-rate-limit:
window: 60s
Expand All @@ -198,8 +198,8 @@ paths:
id: wh_a1b2c3d4e5f6g7h8
url: https://example.com/webhooks/smartdrop
events:
- airdrop.completed
- recipient.claimed
- airdrop.failed
- price.alert
active: true
secret_preview: whse...key
created_at: '2026-06-27T12:00:00.000Z'
Expand Down Expand Up @@ -241,7 +241,7 @@ paths:
- id: wh_a1b2c3d4e5f6g7h8
url: https://example.com/webhooks/smartdrop
events:
- airdrop.completed
- airdrop.failed
active: true
secret_preview: whse...key
created_at: '2026-06-27T12:00:00.000Z'
Expand Down Expand Up @@ -291,7 +291,7 @@ paths:
id: wh_a1b2c3d4e5f6g7h8
url: https://example.com/webhooks/smartdrop
events:
- airdrop.completed
- airdrop.failed
active: false
secret_preview: whse...key
created_at: '2026-06-27T12:00:00.000Z'
Expand All @@ -304,10 +304,15 @@ paths:
/api/v1/webhooks/{id}/test:
post:
operationId: testWebhookEndpoint
summary: Send a test ping to a webhook endpoint
summary: Send a test event to a webhook endpoint
description: |
Queues a test `ping` event delivery to the specified webhook endpoint.
Queues a test `pool.assets_locked` event delivery to the specified webhook endpoint.
The delivery is processed asynchronously.

⚠️ **Warning:** A successful test delivery only confirms that the webhook URL can successfully
receive deliveries. It does not imply that the subscribed event type will ever fire for real,
as pool events are not yet implemented. Only `airdrop.failed` and `price.alert` are actively
dispatched today.
tags:
- Webhooks
parameters:
Expand All @@ -322,7 +327,7 @@ paths:
max: 10
responses:
'202':
description: Test ping queued for delivery
description: Test payload queued for delivery
content:
application/json:
schema:
Expand All @@ -334,9 +339,9 @@ paths:
delivery:
id: dlv_x1y2z3
endpoint_id: wh_a1b2c3d4e5f6g7h8
event: ping
event: pool.assets_locked
payload:
event: ping
event: pool.assets_locked
timestamp: '2026-06-27T12:00:00.000Z'
status: pending
attempt_count: 0
Expand Down Expand Up @@ -720,8 +725,30 @@ components:
- airdrop.completed
- airdrop.failed
- recipient.claimed
- ping
description: SmartDrop lifecycle events
- pool.created
- pool.assets_locked
- pool.assets_unlocked
- pool.rewards_distributed
- pool.closed
- price.alert
- '*'
description: |
SmartDrop webhook events.

**Active / implemented events:**
- `airdrop.failed` (fired when an airdrop expires)
- `price.alert` (fired when a price alert triggers)

**Planned / not yet implemented events:**
- `airdrop.created`
- `airdrop.executing`
- `airdrop.completed`
- `recipient.claimed`
- `pool.created`
- `pool.assets_locked`
- `pool.assets_unlocked`
- `pool.rewards_distributed`
- `pool.closed`

WebhookDelivery:
type: object
Expand Down
5 changes: 5 additions & 0 deletions src/services/webhookEvents.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
'use strict';

// None of the pool events are actually dispatched by the codebase today (the
// event indexer only parses airdrop lifecycle events). They are registered
// here as forward-looking placeholders, but subscribing to them will not
// trigger any deliveries until the corresponding indexer and dispatch logic
// are implemented.
const POOL_EVENTS = Object.freeze([
'pool.created',
'pool.assets_locked',
Expand Down