Skip to main content

Events

Events notify an application when an invoice or payment changes state. The dashboard uses them to refresh balances and transaction history without polling for settlement. REST remains the source of truth for the current state.

SwissKnife currently delivers events through server-sent events (SSE). Webhook delivery is planned.

Subscribe over SSE​

Open GET /v1/me/events with a Bearer token or an Api-Key header, just as for other authenticated API requests:

curl --no-buffer --include https://your-instance:3000/v1/me/events \
-H 'Accept: text/event-stream' \
-H "Authorization: Bearer $SWISSKNIFE_TOKEN"

For an API key, replace the Authorization header with Api-Key: YOUR_API_KEY. No administrative permission scope is required. The stream includes all wallets owned by the authenticated account, and every event identifies its wallet with wallet_id.

A successful response has content type text/event-stream and stays open. An idle stream sends a keep-alive comment every 15 seconds; keep-alives are not durable events and have no event ID. Stop the curl command with Ctrl+C.

Use a client that supports SSE and authentication headers. Browser applications can use a fetch-based SSE client; the native EventSource API cannot set the Authorization or Api-Key header. Keep credentials in headers rather than URLs.

Event types and payloads​

EventMeaning
invoice.pendingThe provider reported an unconfirmed on-chain deposit.
invoice.paidAn incoming invoice or deposit was credited.
payment.settledAn outgoing payment settled.
payment.failedAn outgoing payment was marked failed.

The SSE id is the replay cursor, and the SSE event is one of the names above. The SSE data contains a JSON ClientEvent object:

FieldContents
idEvent ID as a string, matching the SSE id.
event_typeEvent name, matching the SSE event.
wallet_idWallet whose state changed.
resource_idInvoice or payment ID.
dataFull public invoice or payment snapshot when the event was committed.
created_atEvent timestamp.

Each event is committed with its resource and balance update. Its snapshot describes that transition; fetch the REST resource when you need its latest state.

An on-chain deposit keeps the same invoice ID as it moves from invoice.pending to invoice.paid. LND reports unconfirmed deposits. CLN currently discovers deposits after confirmation, so its first notification is invoice.paid.

Reconnect and replay​

A connection without a cursor starts after the account's latest retained event. It listens for new events rather than replaying all previous activity.

Persist the last successfully handled event ID and send it on reconnect:

curl --no-buffer --include https://your-instance:3000/v1/me/events \
-H 'Accept: text/event-stream' \
-H "Authorization: Bearer $SWISSKNIFE_TOKEN" \
-H 'Last-Event-ID: 42'

The server replays events strictly after that ID, in increasing ID order. For deliberate replay, ?after=42 is an alternative; Last-Event-ID takes precedence when both are supplied. Event IDs are not consecutive within one account.

Delivery is at least once. Deduplicate by event ID and persist the cursor only after handling the event. Reconnect after a network error or a clean stream end, and refresh REST state after each connection is established. Refreshing after the connection opens also covers changes between an initial REST read and subscribing.

Open streams recheck authentication every 15 seconds. If a token expires or an API key is revoked, the stream closes. Obtain a valid credential before reconnecting.

Expired cursors and errors​

Events are retained for 30 days by default and pruned hourly. If a requested cursor is older than retained history, the endpoint returns HTTP 409 before opening the stream. Refresh REST state, discard that cursor, and reconnect without Last-Event-ID or after. Pruning can also close an already-open stream, so handle clean stream endings as reconnects.

Malformed or negative cursors return HTTP 400. Missing or invalid credentials return HTTP 401.

Configuration​

SSE is available with the default configuration. The optional client-event settings control retention and cleanup; no separate event service is required.

When using a reverse proxy, allow streaming responses, disable response buffering, and set an idle timeout longer than the 15-second heartbeat interval.