Knowledge center

Run billing with Floatless.

Product guides, billing concepts, operational playbooks, developer references, and security notes for teams running subscription revenue.

Webhooks

Webhooks deliver event notifications from Floatless to your application when billing state changes.

Webhook documentation belongs in the website docs. Webhook endpoints and signing secrets can be managed in the product console or through the Public API.

Endpoint management API

Method Endpoint Description
POST /webhook-endpoints Create a webhook endpoint
GET /webhook-endpoints List webhook endpoints
GET /webhook-endpoints/{endpoint_id} Retrieve a webhook endpoint
PATCH /webhook-endpoints/{endpoint_id} Update URL, event subscriptions, status, or description
DELETE /webhook-endpoints/{endpoint_id} Delete a webhook endpoint

Delivery history API

Method Endpoint Description
GET /webhook-events List webhook delivery events
GET /webhook-events/{event_id} Retrieve one webhook delivery event
POST /webhook-events/{event_id}/retry Retry a failed or retrying webhook event

Create a webhook endpoint

curl https://api.floatless.com/api/public/v1/webhook-endpoints \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/floatless",
    "events": ["invoice.paid", "subscription.*"],
    "description": "Production billing events"
  }'

The creation response includes the signing secret once:

{
  "data": {
    "id": "0d75f867-0f90-4d32-aeb6-2df8136bdfc2",
    "object": "webhook_endpoint",
    "url": "https://example.com/webhooks/floatless",
    "events": ["invoice.paid", "subscription.*"],
    "status": "ACTIVE",
    "description": "Production billing events",
    "created_at": "2026-07-07T10:00:00Z",
    "updated_at": "2026-07-07T10:00:00Z",
    "secret": "whsec_..."
  }
}

Store the secret securely. It is not returned by list, retrieve, or update responses.

URL requirements

Production webhook URLs must use HTTPS.

Local development URLs using http://localhost or http://127.0.0.1 are allowed for testing.

Event subscriptions

You can subscribe to specific events or family wildcards.

Event Description Currently emitted
invoice.posted Invoice issued Yes
invoice.paid Invoice paid Yes
invoice.created Invoice created Reserved
invoice.overdue Invoice moved overdue Reserved
invoice.payment_failed Invoice payment failed Reserved
subscription.created Subscription created Yes
subscription.canceled Subscription canceled Yes
subscription.updated Subscription changed Reserved
subscription.activated Subscription activated Reserved
order.created Order created Reserved
order.completed Order completed Reserved
payment.succeeded Payment succeeded Yes
payment.failed Payment failed Reserved
customer.created Customer created Reserved
customer.updated Customer updated Reserved

Supported wildcards:

  • invoice.*
  • subscription.*
  • order.*
  • payment.*
  • customer.*
ℹ️
"Reserved" events are valid subscription targets — you can register them today and your endpoint will receive them once emission is enabled — but current Floatless flows do not emit them yet. Design your handler defensively: unknown event types may appear over time and should be acknowledged (2xx) and ignored.

List webhook events

curl "https://api.floatless.com/api/public/v1/webhook-events?status=FAILED&limit=25" \
  -H "Authorization: Bearer sk_live_..."

Query parameters

Parameter Type Description
endpoint_id UUID Filter by endpoint
status string PENDING, SUCCESS, FAILED, or RETRYING
event_type string Filter by event type
limit integer Number of results, 1 to 100
offset integer Pagination offset

Retry a webhook event

curl https://api.floatless.com/api/public/v1/webhook-events/64d63c4a-3817-4d5d-bd38-bad334be58aa/retry \
  -H "Authorization: Bearer sk_live_..." \
  -X POST

Successful events cannot be retried.

Retry policy

Delivery is at-least-once with automatic retries:

Setting Value
Attempts Up to 5 per event
Backoff 1s, 2s, 4s, 8s, 16s between attempts
Timeout 10 seconds per attempt
Failure recording Timeout recorded as response code 408; connection error as 0
Response body Last 1000 characters retained

An event in RETRYING can also be retried manually from the delivery history API or the console's Developer page.

Payload shape

{
  "id": "5b4c33b4-6a86-4e5d-9fd9-2d3a7f09d874",
  "type": "invoice.paid",
  "created": 1783400400,
  "data": {
    "id": "5b4c33b4-6a86-4e5d-9fd9-2d3a7f09d874",
    "object": "invoice",
    "customer_id": 123,
    "status": "PAID"
  }
}

created is a Unix timestamp (integer seconds). id is the event's delivery ID — store it for duplicate suppression.

Delivery headers

Webhook deliveries include:

Header Description
X-Floatless-Signature HMAC SHA-256 signature
X-Floatless-Event Event type
X-Floatless-Delivery Delivery event ID
User-Agent Floatless-Webhooks/1.0

Endpoint requirements

  • Use HTTPS in production.
  • Return a 2xx response after receiving and validating the event.
  • Process expensive work asynchronously.
  • Store processed event IDs to handle duplicate delivery.
  • Treat webhook delivery as at-least-once.

Signature verification

Every endpoint has a signing secret. Use it to verify incoming webhook requests before trusting the payload.

The signature value is generated with HMAC SHA-256 over the exact raw JSON body (compact serialization) and delivered in the X-Floatless-Signature header as sha256=<hex_digest>. Verify by computing HMAC over the raw request body bytes (not re-serialized JSON) and comparing with a constant-time comparison:

import crypto from "node:crypto";

export function verifyWebhook(rawBody, secret, signatureHeader) {
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  try {
    return crypto.timingSafeEqual(
      Buffer.from(expected),
      Buffer.from(signatureHeader),
    );
  } catch {
    return false;
  }
}

Next steps