WEBHOOKS

Webhooks

Portfolwright delivers signed event payloads via HTTP POST to your endpoint. Configure your webhook URL in the dashboard.


Event types

EventTrigger
drift.detectedPosition drifts beyond configured threshold (before plan creation)
rebalance.plan_readyRebalance plan computed and ready for execution
rebalance.executingPlan confirmed and orders submitted to exchange
order.filledIndividual exchange order receives a fill (may fire multiple times per order)
rebalance.completeAll orders in the plan have been fully filled
rebalance.failedExchange rejection, connectivity failure, or cost_budget exceeded

drift.detected

Payload
{
  "event": "drift.detected",
  "portfolio_id": "pf_77423_a8c4",
  "max_drift": 0.047,
  "breached_isin": "IE00B4L5Y983",
  "timestamp": "2025-01-03T09:14:02Z"
}

rebalance.plan_ready

Payload
{
  "event": "rebalance.plan_ready",
  "rebalance_id": "rb_20250103_a7c2",
  "portfolio_id": "pf_77423_a8c4",
  "order_count": 3,
  "estimated_cost_eur": 451.20,
  "timestamp": "2025-01-03T09:14:14Z"
}

order.filled

Payload
{
  "event": "order.filled",
  "rebalance_id": "rb_20250103_a7c2",
  "order_id": "ord_XAMS_0041",
  "isin": "IE00B4L5Y983",
  "side": "buy",
  "fill_quantity": 6.2500,
  "fill_price_eur": 72.11,
  "exchange": "XAMS",
  "timestamp": "2025-01-03T09:17:33Z"
}

Signature verification

Each request includes a Pwt-Signature header. Verify with HMAC-SHA256 using your endpoint signing key (separate from your API key, found in the dashboard).

Python
import hmac, hashlib

def verify_signature(payload_bytes, signature_header, signing_key):
    """Verify Pwt-Signature header."""
    expected = hmac.new(
        signing_key.encode(), payload_bytes, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)

Respond with HTTP 200 to acknowledge. Portfolwright retries failed deliveries up to 5 times with exponential backoff (1s, 2s, 4s, 8s, 16s).