Getting Started
Webhook Setup

Webhook Setup

This page covers the management side. The contract for an incoming delivery (headers, signature, retries) is on its own page: Webhooks.

You can manage endpoints from the panel or the API; both reach the same control plane.

Access requirements

  • The Bearer principal must hold the webhooks:manage permission.
  • The organization must have the webhook entitlement enabled — otherwise every call returns 403 webhook.entitlement.not_enabled.

Endpoints

All are relative to https://api-gateway.lunixi.com and require a Bearer token.

MethodPathPurpose
POST/api/v1/organizations/{orgId}/webhook-endpointsCreate an endpoint
GET/api/v1/organizations/{orgId}/webhook-endpointsList endpoints
GET/api/v1/organizations/{orgId}/webhook-endpoints/{endpointId}One endpoint
PUT/api/v1/organizations/{orgId}/webhook-endpoints/{endpointId}Update
DELETE/api/v1/organizations/{orgId}/webhook-endpoints/{endpointId}Disable
GET/api/v1/organizations/{orgId}/webhook-deliveriesDelivery history
POST/api/v1/organizations/{orgId}/webhook-deliveries/{deliveryId}/replayReplay a delivery

DELETE disables rather than deletes. The endpoint and its delivery history stay on record, to preserve the audit trail. To use the same address again, create a new endpoint.

Endpoint fields

FieldMeaning
nameThe name you see in the panel (max 160 characters)
urlWhere deliveries go — must be reachable from the public internet
productCodeWhose events it receives (e.g. PAYMENT_GATEWAY)
environmentEnvironment separation — TEST or LIVE; live and test endpoints never mix
secretRefRequired. The Secret Vault reference of the signing secret: vault:<secretId> — the secret itself never travels through the API
eventTypesRequired. The subscribed event types; wildcards are supported (e.g. ["payment.*"])
statusACTIVE · FAILED (set automatically after consecutive failures)
failureCountConsecutive-failure counter — reset on a successful delivery
lastSuccessAt / lastFailureAtLast success / last failure timestamps

Event subscriptions belong to the endpoint and are per event type; you subscribe an endpoint only to the events it cares about. The DELETE behaviour is unchanged: it disables rather than deletes.

For bank account events, create the endpoint with productCode: "BANK_CONNECT" and eventTypes: ["bank_account.*"]; add "bank_transfer.*" to also receive transfer events. → Bank Accounts › Webhook Events

For insurance events, create the endpoint with productCode: "INSURANCE_SERVICE" and eventTypes: ["insurance.*"]. → Insurance › Webhook Events

Setup flow

Prepare your endpoint

A publicly reachable address that accepts POST. In the first version give it one job: verify the signature, write the event to your own queue, return 2xx. Do not run business logic synchronously — the delivery timeout defaults to 10 seconds.

Create the signing secret

The API does not generate or return a secret — the signing secret lives in the Secret Vault, and the endpoint only references it via secretRef. Two paths:

Panel path. When creating an endpoint, the panel generates a whsec_… secret client-side, stores it in the Secret Vault and sets secretRef automatically. The secret is shown only once — put it in your own vault; if you lose it you cannot read it back, only replace it.

API path. Generate the secret yourself, write it to the vault first, and pass the returned id to the endpoint create call:

curl -X POST "https://api-gateway.lunixi.com/api/v1/secrets" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "merchantId": "<orgId>",
    "secretKey": "webhook/<orgId>/LIVE/PAYMENT_GATEWAY/<name>",
    "payload": { "signingSecret": "whsec_<your own random value, ≥32 chars>" }
  }'

You will use the returned secretId as secretRef: "vault:<secretId>". The signing secret must be at least 16 characters — in practice, generate a random value of ≥ 32 characters.

🚫

The secret is held server-side in an encrypted vault and resolved at delivery time — it is never returned by list calls. So "I'll look it up later" is not an option.

Create the endpoint

curl -X POST "https://api-gateway.lunixi.com/api/v1/organizations/$ORG_ID/webhook-endpoints" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Payment events — live",
    "url": "https://merchant.example.com/webhooks/lunixi",
    "productCode": "PAYMENT_GATEWAY",
    "environment": "LIVE",
    "secretRef": "vault:<secretId>",
    "eventTypes": ["payment.*"]
  }'

environment accepts only TEST or LIVE; secretRef and eventTypes are required.

Write and test verification

Take the verification code from Webhooks. The v2 signature is an HMAC of the raw body — you verify over {eventId}.{timestamp}.{rawBody} without parsing the body.

Go live

After the first real event arrives, confirm it in the delivery history.

Delivery history

curl "https://api-gateway.lunixi.com/api/v1/organizations/$ORG_ID/webhook-deliveries?status=DEAD_LETTER" \
  -H "Authorization: Bearer $TOKEN"

Filters: endpointId · eventType · status

Each delivery record carries attemptCount, lastErrorCode, lastError, nextRetryAt and deliveredAt. Attempts are also logged individually — you can see which HTTP status you returned on which attempt.

Replaying a failed delivery

curl -X POST "https://api-gateway.lunixi.com/api/v1/organizations/$ORG_ID/webhook-deliveries/$DELIVERY_ID/replay" \
  -H "Authorization: Bearer $TOKEN"

This is how a DEAD_LETTER delivery is recovered. If your endpoint was down for a period, list the deliveries in that window and replay them in bulk.

A replay is sent with the same x-lunixi-event-id and resets the attempt budget — the delivery re-enters the queue with a fresh 12 attempts. If you keyed your idempotency on that header, you will not re-process an event you already handled — which is the intended behaviour.

Rotating the secret

After you rotate, the platform dual-signs deliveries with both the old and the new secret for 24 hours (the "grace window"): the x-lunixi-signature header carries two v2= items. A verifier that accepts the signature when any v2= item matches — like the example on Webhooks — needs no changes at all. After 24 hours the old secret stops being used.

The sequence is simple:

  1. Rotate, and write the new value to your vault.
  2. During the grace window deliveries arrive with both signatures — if your verifier accepts any matching v2= item, there is no gap.
  3. After 24 hours signing with the old secret stops; nothing further to do on your side.

If your endpoint was disabled

When the consecutive-failure threshold is crossed (default 20) the endpoint's status becomes FAILED and it stops receiving deliveries. After fixing your server, re-enable it from the panel; you can then replay the deliveries that accumulated meanwhile.

Related

  • Webhooks — headers, signature, retries, delivery statuses
  • Authentication — how you mint the Bearer token