Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.fluveo.com/llms.txt

Use this file to discover all available pages before exploring further.

Webhooks let your application react to events as they happen — a payment succeeds, a refund is created, an order is placed, a fulfillment ships. Instead of polling the API, you register a URL and we send you an HTTP POST with the event data.

Setting up a webhook endpoint

Create an endpoint by specifying a URL and the event types you want to receive:
curl -X POST https://api.leanrails.com/v1/webhook_endpoints \
  -u "sk_test_xxx:" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks",
    "enabled_events": [
      "payment_intent.succeeded",
      "payment_intent.payment_failed",
      "charge.refunded",
      "order.created",
      "order.paid",
      "fulfillment.shipped",
      "fulfillment.delivered",
      "inventory_level.updated"
    ]
  }'
The webhook secret (whsec_...) is only returned once — when you create the endpoint. Store it securely. You’ll need it to verify webhook signatures.

Verifying signatures

Every webhook delivery includes a Fluveo-Signature header (Stripe-style namespaced) and an X-Signature header (legacy alias) — both carry the same value. Always verify the signature before processing the event to ensure it came from us and hasn’t been tampered with. We additionally send a Webhook-Id header containing the event id so you can deduplicate without parsing the body, and a User-Agent: Fluveo-Webhooks/1.0 header so deliveries are easy to identify in your logs. The signature header format is:
Fluveo-Signature: t=1709913600,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
The signature is computed as:
HMAC-SHA256(webhook_secret, "v1=" + timestamp + "." + raw_request_body)
# Manually verify a webhook signature:
TIMESTAMP="1709913600"
PAYLOAD='{"id":"evt_123","type":"payment_intent.succeeded"}'
SECRET="whsec_your_secret"

SIGNED_PAYLOAD="v1=${TIMESTAMP}.${PAYLOAD}"
EXPECTED=$(echo -n "$SIGNED_PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')

echo "Expected signature: $EXPECTED"
# Compare with the v1= value from the Fluveo-Signature header
Reject any delivery whose t timestamp is more than 5 minutes in the past — that’s the standard replay protection guardrail.

Retry schedule

If your endpoint returns a non-2xx response or times out, we retry the delivery with increasing delays:
AttemptDelayCumulative Time
1Immediate0
21 minute1 minute
35 minutes6 minutes
430 minutes36 minutes
52 hours~2.5 hours
68 hours~10.5 hours
724 hours~1.5 days
872 hours~4.5 days
After 8 failed attempts, the delivery is marked as exhausted and no further retries are attempted.

Endpoint requirements

  • Must accept POST requests with Content-Type: application/json
  • Must return a 2xx status code within 30 seconds
  • Must verify the Fluveo-Signature (or legacy X-Signature) header before processing

Best practices

Respond quickly. Return a 200 response immediately, then process the event asynchronously. If your handler takes longer than 30 seconds, the delivery will be marked as failed and retried.
  • Verify signatures on every request to prevent spoofing
  • Deduplicate by event ID — the same event may be delivered more than once during retries
  • Process asynchronously — use a message queue to handle events outside the request cycle
  • Subscribe selectively — only subscribe to event types you need to reduce load
  • Handle test events — in test mode, events include "livemode": false

Event types

See Event Types for the complete list of events you can subscribe to.