Skip to main content
Version: 0.62.0-rc.1267

Real-time Subscriptions

The admin API provides GraphQL subscriptions over Server-Sent Events (SSE). Subscribe to entity changes instead of repeatedly polling for updates.

Transport and Authentication​

  • Endpoint: POST /graphql/stream on the admin origin, for example http://admin.lana-bank.localhost:1355/graphql/stream in a checkout named lana-bank, or https://admin.your-instance.com/graphql/stream in production.
  • Protocol: GraphQL over SSE, using a separate HTTP request for each subscription (distinct-connection mode).
  • Headers: Authorization: Bearer <access-token>, Content-Type: application/json, and Accept: text/event-stream.
  • Body: A GraphQL request containing query, optional variables, and optional operationName.

The server emits next SSE events containing GraphQL responses and a complete event when the stream ends. Cancel the HTTP request to unsubscribe. It does not expose a graphql-transport-ws WebSocket endpoint or a WebSocket connection_init handshake.

The admin panel uses the graphql-sse client. For example:

import { createClient } from 'graphql-sse';

const client = createClient({
url: 'https://admin.your-instance.com/graphql/stream',
headers: async () => ({
Authorization: `Bearer ${await getAccessToken()}`,
}),
});

const unsubscribe = client.subscribe(
{
query: `subscription CustomerChanges($customerId: CustomerId!) {
customerUpdated(customerId: $customerId) {
customerId
status
level
}
}`,
variables: { customerId },
},
{
next: (response) => console.log(response),
error: (error) => console.error(error),
complete: () => console.log('Subscription completed'),
},
);

// Call unsubscribe() when the view is disposed.

Supply your own getAccessToken() and customerId. Refresh tokens before opening or reconnecting a stream, just as you would for regular API requests.

Entity Change Subscriptions​

Most subscriptions listen to the persisted outbox and return the current entity, not the raw domain-event payload. Select the fields your view needs. Input variables use the schema's entity-specific scalars (CustomerId, CreditFacilityId, etc.), not a generic UUID scalar.

SubscriptionArgumentResult
customerUpdatedcustomerId: CustomerId!Customer!
customerDepositAccountsUpdatedcustomerId: CustomerId!Customer!
customerCreditFacilitiesUpdatedcustomerId: CustomerId!Customer!
customerPendingCreditFacilitiesUpdatedcustomerId: CustomerId!Customer!
customerCreditFacilityProposalsUpdatedcustomerId: CustomerId!Customer!
prospectUpdatedprospectId: ProspectId!Prospect!
verificationUpdatedverificationId: VerificationId!Verification!
pendingCreditFacilityUpdatedpendingCreditFacilityId: PendingCreditFacilityId!PendingCreditFacility!
creditFacilityProposalUpdatedcreditFacilityProposalId: CreditFacilityProposalId!CreditFacilityProposal!
creditFacilityUpdatedcreditFacilityId: CreditFacilityId!CreditFacility!
withdrawalUpdatedwithdrawalId: WithdrawalId!Withdrawal!
disbursalUpdateddisbursalId: CreditFacilityDisbursalId!CreditFacilityDisbursal!
eodProcessUpdatedNoneEodProcess!
pipelineRunUpdatedNonePipelineRun!

Verification Status​

KYC status changes are delivered through verificationUpdated, not customerKycUpdated:

subscription VerificationChanges($verificationId: VerificationId!) {
verificationUpdated(verificationId: $verificationId) {
verificationId
status
}
}

Credit Facility Changes​

creditFacilityUpdated includes lifecycle, collateral, payment, and obligation changes. Pending facilities use pendingCreditFacilityUpdated for collateralization and completion updates:

subscription PendingFacilityChanges($id: PendingCreditFacilityId!) {
pendingCreditFacilityUpdated(pendingCreditFacilityId: $id) {
pendingCreditFacilityId
creditFacilityId
status
collateralizationState
collateral {
amount
currency {
code
}
}
}
}

Warehouse Pipeline Runs​

pipelineRunUpdated reports warehouse pipeline state changes and returns a PipelineRun, rather than a report-run identifier:

subscription PipelineUpdates {
pipelineRunUpdated {
pipelineRunId
status
failureReason
}
}

Ephemeral Subscriptions​

These streams listen to transient outbox notifications rather than the persisted event stream. Notifications that occur while disconnected are not replayed.

Ledger Account CSV Export Uploaded​

subscription CsvExportReady($accountId: LedgerAccountId!) {
ledgerAccountCsvExportUploaded(ledgerAccountId: $accountId) {
documentId
}
}

Use documentId to request a download link through the corresponding API operation.

Exchange Rate Updated​

exchangeRateUpdated returns an ExchangeRate, not a fixed BTC/USD price payload. See the Admin API Reference for its current fields.

Delivery and Reconnection​

Persisted storage of the source events is not a durable delivery guarantee for an external GraphQL client. The subscription API exposes no consumer acknowledgement, replay cursor, or resume argument. Reconnecting does not let a client request every event missed while disconnected, and entity responses describe current state rather than historical snapshots.

  • Reconnect with a valid access token and handle duplicate notifications safely.
  • Query the current entity state after reconnecting to reconcile the view.
  • Use these subscriptions as change notifications, not an exactly-once financial event feed.
  • For a durable integration, design around the public event/outbox system with explicit checkpointing rather than assuming GraphQL subscriptions provide it.