Skip to content
Last updated

Juniper sends real-time notifications to your server when order status changes. Configure your webhook URL in Partner Settings to receive these updates automatically.


Configuration

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"
  }'

Webhook Payload

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"
  }
}

Payload Fields

FieldTypeDescription
order.orderIdstringUnique order identifier
order.statusstringCurrent order status (see Status Events)
order.fulfillmentInfoobjectShipping details (present when shipped)
order.fulfillmentInfo.carrierstringShipping carrier name (e.g., "UPS", "USPS", "FedEx")
order.fulfillmentInfo.trackingNumberstringCarrier tracking number
order.fulfillmentInfo.trackingStatusstringCurrent tracking status
order.fulfillmentInfo.trackingUrlstringURL to track the shipment
order.fulfillmentInfo.checkpointsarrayTracking checkpoint history
order.createdAtstringOrder creation timestamp (ISO 8601)
order.updatedAtstringOrder last update timestamp (ISO 8601)

Checkpoint Fields

FieldTypeDescription
checkpointTimestringTimestamp of the checkpoint event (ISO 8601)
locationstringLocation of the shipment at this checkpoint
messagestringStatus message from the carrier
tagstringStatus tag (e.g., "InTransit", "OutForDelivery", "Delivered")

Status Events

Webhooks are triggered for the following order status changes:

StatusDescription
NEWOrder received and validated
PROCESSINGOrder is being prepared
DISPENSINGOrder is being dispensed/filled
FILLEDOrder has been filled
FULFILLEDOrder fulfillment complete
AWAITING_SHIPMENTOrder is packaged and ready to ship
SHIPPEDOrder has shipped (includes fulfillmentInfo with tracking)
CANCELLEDOrder was cancelled

Handling Webhooks

Best Practices

  1. Return 2xx quickly - Respond with a 2xx status code within 30 seconds to acknowledge receipt
  2. Process asynchronously - Queue webhook payloads for background processing
  3. Handle idempotently - The same event may be sent multiple times; use orderId and updatedAt to deduplicate
  4. Verify the source - Validate requests originate from Juniper's IP ranges

Example Handler (Node.js)

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...
  }
});

Example Handler (Python)

@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'])

Retry Policy

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.