Webhooks
Webhooks allow you to receive real-time HTTP notifications when events occur in your Price2b account. Instead of polling the API, your server receives a POST request when something happens.
Available events
| Event | Description |
|---|---|
order.created | New order received |
order.updated | Order status changed |
order.cancelled | Order was cancelled |
shipment.created | Label generated, tracking available |
shipment.delivered | Package delivered |
inventory.low_stock | Stock below threshold |
manifest.processed | Courier manifest completed |
Configuring webhooks
- Go to Account Settings → Webhooks
- Click Add Webhook Endpoint
- Enter your endpoint URL
- Select the events to subscribe to
- Save and note your signing secret
Webhook payload
All webhooks are sent as POST requests with a JSON body:
{
"id": "evt_abc123",
"type": "order.created",
"created_at": "2026-01-20T14:30:00Z",
"data": {
"id": 5001,
"order_number": "ORD-2026-001234",
"channel": "amazon",
"status": "pending",
"totals": {
"total": 114.98,
"currency": "USD"
}
}
}
| Field | Description |
|---|---|
id | Unique event identifier |
type | Event type |
created_at | When the event occurred |
data | Event-specific payload |
Verifying signatures
All webhooks include a signature for verification:
X-Price2b-Signature: sha256=abc123...
X-Price2b-Timestamp: 1706284200
Verify the signature to ensure the webhook is authentic:
Signature verification
const crypto = require('crypto')
function verifyWebhook(payload, signature, timestamp, secret) {
const signedPayload = `${timestamp}.${payload}`
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex')
return `sha256=${expectedSignature}` === signature
}
// In your webhook handler
app.post('/webhooks/price2b', (req, res) => {
const signature = req.headers['x-price2b-signature']
const timestamp = req.headers['x-price2b-timestamp']
const payload = JSON.stringify(req.body)
if (!verifyWebhook(payload, signature, timestamp, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature')
}
// Process the webhook
handleEvent(req.body)
res.status(200).send('OK')
})
Event payloads
order.created
{
"id": "evt_abc123",
"type": "order.created",
"data": {
"id": 5001,
"order_number": "ORD-2026-001234",
"channel": "amazon",
"channel_order_id": "114-1234567-1234567",
"status": "pending",
"customer": {
"name": "Juan Pérez",
"email": "juan@example.com"
},
"items": [...],
"totals": {
"subtotal": 99.98,
"shipping": 15.00,
"total": 114.98,
"currency": "USD"
},
"created_at": "2026-01-20T14:30:00Z"
}
}
shipment.created
{
"id": "evt_def456",
"type": "shipment.created",
"data": {
"shipment_id": 12345,
"order_id": 5001,
"carrier": "fedex",
"service": "INTERNATIONAL_ECONOMY",
"tracking_number": "794644790303",
"label_url": "https://app.price2b.com/api/v1/shipping/shipments/12345/label",
"estimated_delivery": "2026-02-05",
"created_at": "2026-01-20T15:00:00Z"
}
}
inventory.low_stock
{
"id": "evt_ghi789",
"type": "inventory.low_stock",
"data": {
"product_id": 12345,
"sku": "PROD-001",
"name": "Wireless Headphones",
"current_stock": 5,
"threshold": 10,
"warehouse_id": 1,
"warehouse_name": "Miami Warehouse"
}
}
Best practices
- Respond quickly - Return 200 within 5 seconds, process async
- Handle duplicates - Use
idto detect and ignore duplicates - Verify signatures - Always validate before processing
- Log everything - Keep records for debugging
- Handle failures - We retry failed webhooks with exponential backoff
Retry policy
Failed webhooks (non-2xx response or timeout) are retried:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 24 hours |
After 6 failed attempts, the webhook is marked as failed and no more retries are attempted. You can manually retry from the dashboard.
Testing webhooks
Use the Send Test button in the webhook configuration to send a test payload to your endpoint. This helps verify your integration before going live.
Webhook Subscriptions API
Manage webhook subscriptions programmatically via the API.
Base Path: /api/webhook-subscriptions
Authentication: Bearer token (Sanctum)
List available events
Get all events available for webhook subscription.
Available events
| Event | Description |
|---|---|
manifest.processing_completed | Manifest processing completed successfully |
manifest.invalid_cuit_detected | Invalid CUITs detected (quota=0 or validation error) |
manifest.total_due | Total due amount calculated for manifest |
manifest.processing_failed | Manifest processing failed critically |
document.uploaded | Transport document uploaded |
document.validated | Transport document passed validation |
document.approved | Transport document approved |
document.rejected | Transport document rejected |
document.superseded | Transport document replaced by newer version |
document.downloaded | Transport document downloaded |
Request
curl https://app.price2b.com/api/webhook-subscriptions/available-events \
-H "Authorization: Bearer {token}"
Response
{
"success": true,
"data": [
{
"event": "manifest.processing_completed",
"type": "manifest",
"action": "processing_completed",
"description": "Fired when manifest processing completes successfully"
},
{
"event": "manifest.invalid_cuit_detected",
"type": "manifest",
"action": "invalid_cuit_detected",
"description": "Fired when invalid CUITs detected"
},
{
"event": "document.uploaded",
"type": "document",
"action": "uploaded",
"description": "Fired when a transport document is uploaded"
}
],
"count": 10
}
List subscriptions
Get all webhook subscriptions for the authenticated user.
Request
curl https://app.price2b.com/api/webhook-subscriptions \
-H "Authorization: Bearer {token}"
Response
{
"success": true,
"data": [
{
"id": 1,
"endpoint_url": "https://partner.example.com/webhooks/price2b",
"events": ["manifest.processing_completed", "manifest.invalid_cuit_detected"],
"active": true,
"health_status": "healthy",
"health_status_color": "green",
"failed_deliveries": 0,
"last_delivery_at": "2026-01-20T18:30:00+00:00",
"last_success_at": "2026-01-20T18:30:00+00:00",
"created_at": "2026-01-01T12:00:00+00:00",
"updated_at": "2026-01-20T18:30:00+00:00"
}
],
"count": 1
}
Create subscription
Create a new webhook subscription. An HMAC secret is generated automatically and shown only once.
Required attributes
- Name
endpoint_url- Type
- string
- Description
URL where webhooks will be sent. Max 500 characters.
- Name
events- Type
- array
- Description
List of events to subscribe to.
Optional attributes
- Name
active- Type
- boolean
- Description
Activate subscription immediately (default: true).
Save the hmac_secret securely. It is only shown once during creation and is required for verifying webhook signatures.
Request
curl -X POST https://app.price2b.com/api/webhook-subscriptions \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"endpoint_url": "https://partner.example.com/webhooks/price2b",
"events": [
"manifest.processing_completed",
"manifest.invalid_cuit_detected",
"document.approved"
],
"active": true
}'
Response (201 Created)
{
"success": true,
"message": "Webhook subscription created successfully",
"data": {
"id": 1,
"endpoint_url": "https://partner.example.com/webhooks/price2b",
"hmac_secret": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6",
"events": [
"manifest.processing_completed",
"manifest.invalid_cuit_detected",
"document.approved"
],
"active": true,
"created_at": "2026-01-20T18:00:00+00:00"
},
"important": "Save the hmac_secret securely. It will not be shown again."
}
Get subscription
Get details of a specific subscription including health status and delivery history.
Request
curl https://app.price2b.com/api/webhook-subscriptions/1 \
-H "Authorization: Bearer {token}"
Response
{
"success": true,
"data": {
"id": 1,
"endpoint_url": "https://partner.example.com/webhooks/price2b",
"events": ["manifest.processing_completed"],
"active": true,
"health_status": "healthy",
"health_status_color": "green",
"failed_deliveries": 0,
"last_delivery_at": "2026-01-20T18:30:00+00:00",
"last_success_at": "2026-01-20T18:30:00+00:00",
"created_at": "2026-01-01T12:00:00+00:00",
"updated_at": "2026-01-20T18:30:00+00:00"
}
}
Update subscription
Update an existing subscription. Can change endpoint URL, events, or active status.
Optional attributes
- Name
endpoint_url- Type
- string
- Description
New endpoint URL.
- Name
events- Type
- array
- Description
New list of events to subscribe to.
- Name
active- Type
- boolean
- Description
Activate or deactivate the subscription.
Request
curl -X PUT https://app.price2b.com/api/webhook-subscriptions/1 \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"events": [
"manifest.processing_completed",
"document.approved",
"document.rejected"
],
"active": true
}'
Response
{
"success": true,
"message": "Webhook subscription updated successfully",
"data": {
"id": 1,
"endpoint_url": "https://partner.example.com/webhooks/price2b",
"events": [
"manifest.processing_completed",
"document.approved",
"document.rejected"
],
"active": true,
"updated_at": "2026-01-20T18:45:00+00:00"
}
}
Delete subscription
Delete a webhook subscription. This action cannot be undone.
Request
curl -X DELETE https://app.price2b.com/api/webhook-subscriptions/1 \
-H "Authorization: Bearer {token}"
Response
{
"success": true,
"message": "Webhook subscription deleted successfully"
}
Test subscription
Send a test webhook to verify your endpoint is correctly configured.
Request
curl -X POST https://app.price2b.com/api/webhook-subscriptions/1/test \
-H "Authorization: Bearer {token}"
Response (Success)
{
"success": true,
"message": "Test webhook sent successfully",
"data": {
"status_code": 200,
"response_time_ms": 245
}
}
Response (Failure)
{
"success": false,
"message": "Test webhook delivery failed",
"data": {
"status_code": 500,
"error": "Connection refused"
}
}
Health status
Subscriptions have a health status based on delivery success:
| Status | Color | Description |
|---|---|---|
healthy | green | No failed deliveries |
degraded | yellow | 1-5 consecutive failures |
unhealthy | red | 6-9 consecutive failures |
disabled | gray | 10+ failures (auto-disabled) |
After 10 consecutive failures, the subscription is automatically deactivated. Reactivate it after fixing your endpoint.