Juniper sends real-time notifications to your server when order status changes. Configure your webhook URL in Partner Settings to receive these updates automatically.
Set your webhook endpoint using the Partner Settings API:
curl -X PATCH https://api.fulfillment.sandbox.juniperhealth.com/v1/partner-settings \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://your-server.com/webhooks/juniper"
}'When an order status changes, Juniper sends an HTTP POST request to your webhook URL with the following payload structure:
{
"order": {
"orderId": "2vSGym0bH8qVEwCIGlyFoRgJq1A",
"status": "SHIPPED",
"fulfillmentInfo": {
"carrier": "UPS",
"trackingNumber": "1Z9999999999999999",
"trackingStatus": "InTransit",
"trackingUrl": "https://www.ups.com/track?tracknum=1Z9999999999999999",
"checkpoints": [
{
"checkpointTime": "2023-10-02T08:00:00Z",
"location": "Los Angeles, CA, USA",
"message": "Shipment picked up",
"tag": "InTransit"
},
{
"checkpointTime": "2023-10-03T14:00:00Z",
"location": "Chicago, IL, USA",
"message": "Package arrived at facility",
"tag": "InTransit"
}
]
},
"createdAt": "2023-10-01T12:00:00Z",
"updatedAt": "2023-10-05T14:30:00Z"
}
}| Field | Type | Description |
|---|---|---|
order.orderId | string | Unique order identifier |
order.status | string | Current order status (see Status Events) |
order.fulfillmentInfo | object | Shipping details (present when shipped) |
order.fulfillmentInfo.carrier | string | Shipping carrier name (e.g., "UPS", "USPS", "FedEx") |
order.fulfillmentInfo.trackingNumber | string | Carrier tracking number |
order.fulfillmentInfo.trackingStatus | string | Current tracking status |
order.fulfillmentInfo.trackingUrl | string | URL to track the shipment |
order.fulfillmentInfo.checkpoints | array | Tracking checkpoint history |
order.createdAt | string | Order creation timestamp (ISO 8601) |
order.updatedAt | string | Order last update timestamp (ISO 8601) |
| Field | Type | Description |
|---|---|---|
checkpointTime | string | Timestamp of the checkpoint event (ISO 8601) |
location | string | Location of the shipment at this checkpoint |
message | string | Status message from the carrier |
tag | string | Status tag (e.g., "InTransit", "OutForDelivery", "Delivered") |
Webhooks are triggered for the following order status changes:
| Status | Description |
|---|---|
NEW | Order received and validated |
PROCESSING | Order is being prepared |
DISPENSING | Order is being dispensed/filled |
FILLED | Order has been filled |
FULFILLED | Order fulfillment complete |
AWAITING_SHIPMENT | Order is packaged and ready to ship |
SHIPPED | Order has shipped (includes fulfillmentInfo with tracking) |
CANCELLED | Order was cancelled |
- Return 2xx quickly - Respond with a 2xx status code within 30 seconds to acknowledge receipt
- Process asynchronously - Queue webhook payloads for background processing
- Handle idempotently - The same event may be sent multiple times; use
orderIdandupdatedAtto deduplicate - Verify the source - Validate requests originate from Juniper's IP ranges
app.post('/webhooks/juniper', async (req, res) => {
// Acknowledge receipt immediately
res.status(200).send('OK');
// Process asynchronously
const { order } = req.body;
switch (order.status) {
case 'SHIPPED':
await notifyCustomer(order.orderId, order.fulfillmentInfo);
break;
case 'CANCELLED':
await handleCancellation(order.orderId);
break;
// Handle other statuses...
}
});@app.route('/webhooks/juniper', methods=['POST'])
def handle_webhook():
payload = request.get_json()
order = payload.get('order', {})
# Queue for async processing
queue.enqueue(process_order_update, order)
return 'OK', 200
def process_order_update(order):
if order['status'] == 'SHIPPED':
notify_customer(order['orderId'], order.get('fulfillmentInfo'))
elif order['status'] == 'CANCELLED':
handle_cancellation(order['orderId'])If your endpoint doesn't respond with a 2xx status code, Juniper will retry the webhook:
- Retry attempts: 3
- Retry intervals: 1 minute, 5 minutes, 30 minutes
- Timeout: 30 seconds per attempt
After all retries are exhausted, the webhook is marked as failed. You can retrieve missed updates by polling the Get Order endpoint.