Webhook deliveries
When you register a webhook subscription, UGiftMe POSTs JSON to your HTTPS URL whenever a subscribed event occurs. This page documents those inbound deliveries to your server — not the subscription CRUD endpoints.Delivery contract
| Item | Detail |
|---|---|
| Method | POST |
| URL | The url you registered on the subscription |
| Body | Content-Type: application/json — event-specific object (see below) |
| Success | Your endpoint returns HTTP 2xx within 5 seconds (production) |
| Failure | Non-2xx, timeout, or network error triggers retries (see Retries) |
POST /webhooks/test) use 10 s and include X-Webhook-Delivery: test.
Headers
| Header | Always | Description |
|---|---|---|
Content-Type | Yes | application/json |
X-Webhook-Event | Yes | Event name, e.g. order.succeeded |
X-Webhook-Signature | Yes | HMAC-SHA256 hex digest of the JSON body (see Verify signatures) |
X-Webhook-Delivery | Test only | test when sent via POST /webhooks/test |
X-Webhook-Event to the payload shape you parse. The event name is not repeated inside most order payloads (except webhook.test, which includes type).
Verify signatures
When you create a webhook, the API returns a secret once. Store it securely. UGiftMe signs each delivery as:Node.js example
Retries
Failed deliveries (non-2xx, timeout, or connection error) are retried up to 5 attempts total, with backoff approximately:| Attempt | Delay before retry |
|---|---|
| 1 → 2 | 1 minute |
| 2 → 3 | 5 minutes |
| 3 → 4 | 15 minutes |
| 4 → 5 | 1 hour |
| 5 → 6 | 4 hours |
deliveryStats on your subscription (visible via GET /webhooks) tracks success and failure counts.
Design your handler to be idempotent: the same event may be delivered more than once if a retry occurs after your server already processed the first attempt but returned a non-2xx or timed out.
Order lifecycle (async)
When async order processing is enabled, subscribedorder.* events typically fire in sequence:
201 Created) does not emit order.queued / order.processing / order.succeeded / order.failed. It may still emit wallet.updated when the wallet balance changes.
Poll GET /orders/order-requests/{id} if you need full request state in addition to webhooks.
Event payloads
order.queued
Emitted when an async order request is accepted (202).
Single order:
orderCount:
order.processing
Worker picked up the order request:
type is "single" or "bulk".
order.succeeded
Fulfillment completed. result shape depends on type:
Single — one created order id:
GET /orders/{orderId} for full order details; the webhook carries ids and summary only.
order.failed
Processing failed after retries exhausted:
wallet.updated
Balance changed on your business wallet (orders, top-ups, holds, etc.).
| Field | Notes |
|---|---|
userId | Business user id (same as your integration account) |
availableBalance | balance minus active wallet holds |
source | e.g. api_order_sync, api_order_async, or other internal sources |
transaction | Optional; present when the update is tied to a wallet transaction |
product.updated
Full product catalog document as stored internally (same fields you see from the products API). Shape varies by product; treat as a catalog record update notification and refresh via GET /products or search as needed.
webhook.test (test deliveries only)
Not subscribable via events. Sent only through POST /webhooks/test:
X-Webhook-Event is webhook.test. Header X-Webhook-Delivery is test.
Test your endpoint
Use the Business API (withX-API-Key) after registering a webhook:
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/business/webhooks/test-sample | Returns { event, payload } sample for webhook.test (fresh sentAt when actually sent) |
| POST | /api/v1/business/webhooks/test | Body { "webhookId": "<id>" } — POSTs a signed test payload to that subscription’s URL |
ok is false and errorMessage describes the problem (e.g. non-2xx from your URL).
Related
- API Guide — webhook subscriptions
- UGiftMe API Reference — subscription CRUD, test endpoints, and outbound event schemas
