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/streamon the admin origin, for examplehttp://admin.lana-bank.localhost:1355/graphql/streamin a checkout namedlana-bank, orhttps://admin.your-instance.com/graphql/streamin 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, andAccept: text/event-stream. - Body: A GraphQL request containing
query, optionalvariables, and optionaloperationName.
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.
| Subscription | Argument | Result |
|---|---|---|
customerUpdated | customerId: CustomerId! | Customer! |
customerDepositAccountsUpdated | customerId: CustomerId! | Customer! |
customerCreditFacilitiesUpdated | customerId: CustomerId! | Customer! |
customerPendingCreditFacilitiesUpdated | customerId: CustomerId! | Customer! |
customerCreditFacilityProposalsUpdated | customerId: CustomerId! | Customer! |
prospectUpdated | prospectId: ProspectId! | Prospect! |
verificationUpdated | verificationId: VerificationId! | Verification! |
pendingCreditFacilityUpdated | pendingCreditFacilityId: PendingCreditFacilityId! | PendingCreditFacility! |
creditFacilityProposalUpdated | creditFacilityProposalId: CreditFacilityProposalId! | CreditFacilityProposal! |
creditFacilityUpdated | creditFacilityId: CreditFacilityId! | CreditFacility! |
withdrawalUpdated | withdrawalId: WithdrawalId! | Withdrawal! |
disbursalUpdated | disbursalId: CreditFacilityDisbursalId! | CreditFacilityDisbursal! |
eodProcessUpdated | None | EodProcess! |
pipelineRunUpdated | None | PipelineRun! |
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.