API Reference
@decentrl/sdk
Declarative SDK wrapping identity, crypto, event store, and transport into a single typed API.
npm install @decentrl/sdk zoddefineDecentrlApp(config)
Creates a typed Decentrl app configuration.
import { defineDecentrlApp } from '@decentrl/sdk'
function defineDecentrlApp<TEvents, TState>(
config: DecentrlAppConfig<TEvents, TState>,
): DecentrlApp<TEvents, TState>Returns: { config, createClient } — an inert configuration plus a factory method.
interface DecentrlAppConfig<TEvents, TState> {
events: TEvents // Record<string, { schema: ZodType; tags: string[] }>
state: TState // Record<string, StateSliceDefinition>
}Event Definitions
type EventDefinitions = Record<string, {
schema: z.ZodType // validated at publish time
tags: string[] // template strings like "chat:${chatId}"
}>State Definitions
interface StateSliceDefinition<TSlice, TEvents> {
initial: TSlice
reduce: {
[K in keyof TEvents]?: (
state: TSlice,
data: z.infer<TEvents[K]['schema']>,
meta: EventMeta,
) => TSlice
}
}DecentrlClient
createClient(clientConfig)
Creates a client instance from an app definition.
const client = app.createClient({
mediatorDid: 'did:web:mediator.decentrl.io',
persist: { key: 'myapp' }, // optional localStorage persistence
transport: customTransport, // optional custom transport
})interface DecentrlClientConfig {
mediatorDid: string
persist?: { key: string }
transport?: DecentrlTransport
}Publishing
await client.publish<K extends keyof TEvents>(
eventType: K,
data: z.infer<TEvents[K]['schema']>,
options?: { recipient?: string; ephemeral?: boolean },
): Promise<void>Full type safety — the data parameter is inferred from the Zod schema of the event type.
State
client.getState(): InferState<TState>
client.subscribe(listener: (state) => void): () => voidQuerying
client.query(options?: QueryOptions): Promise<PaginatedResult<EventEnvelope>>
client.queryAndProcess(options?: QueryOptions): Promise<{ processed: number; total: number }>
client.onEvent(listener: (envelope: EventEnvelope) => void): () => voidSync
client.startSync(options?: SyncOptions): void
client.stopSync(): void
client.sync(): Promise<void>
client.getConnectionStatus(): ConnectionStatus
client.onConnectionStatusChange(listener: (status) => void): () => voidinterface SyncOptions {
intervalMs?: number
fallbackIntervalMs?: number
onError?: (error: unknown) => void
websocket?: boolean
autoRenew?: { enabled: boolean; threshold?: number }
}
type ConnectionStatus = 'disconnected' | 'connecting' | 'authenticating' | 'connected'Lifecycle
client.reset(): voidIdentityManager
Accessible via client.identity.
client.identity.getIdentity(): IdentityState | null
client.identity.requireIdentity(): IdentityState
client.identity.create({ alias, mediatorDid }): Promise<IdentityState>
client.identity.load(serialized: SerializedIdentity): IdentityState
client.identity.serialize(): SerializedIdentity
client.identity.reset(): void
client.identity.onChange(listener): () => voidContractManager
Accessible via client.contracts.
client.contracts.request(recipientDid, expiresIn?): Promise<void>
client.contracts.getPending(): Promise<PendingContractRequest[]>
client.contracts.accept(pendingId, encryptedPayload, ephemeralPubKey): Promise<void>
client.contracts.refresh(): Promise<void>
client.contracts.getActiveContracts(): StoredSignedContract[]
client.contracts.getContractByDid(did): StoredSignedContract | undefined
client.contracts.processAutoRenewals(threshold?): Promise<void>
client.contracts.processContractCleanup(): Promise<void>
client.contracts.onChange(listener): () => voidTypes
Event Types
interface EventMeta {
senderDid: string
timestamp: number
eventId: string
ephemeral?: boolean
}
interface EventEnvelope {
type: string
data: unknown
meta: EventMeta
tags?: string[]
_mediatorEventId?: string
}Identity Types
interface IdentityState {
did: string
alias: string
mediatorDid: string
mediatorEndpoint: string
keys: DecentrlIdentityKeys
mediatorContract: SignedCommunicationContract | null
}
interface SerializedIdentity {
did: string
alias: string
mediatorDid: string
mediatorEndpoint: string
keys: { signing: SerializedKeyPair; encryption: SerializedKeyPair; storageKey: string }
mediatorContract: SignedCommunicationContract | null
}Contract Types
interface StoredSignedContract {
id: string
participantDid: string
participantAlias?: string
signedCommunicationContract: SignedCommunicationContract
rootSecret: string
createdAt: number
status: 'active' | 'expired' | 'superseded'
}
interface PendingContractRequest {
id: string
senderDid: string
encryptedPayload: string
requestorEphemeralPublicKey: string
}Error Handling
class DecentrlSDKError extends Error {
code: DecentrlSDKErrorCode
details?: unknown
}
type DecentrlSDKErrorCode =
| 'IDENTITY_NOT_INITIALIZED'
| 'IDENTITY_ALREADY_EXISTS'
| 'CONTRACT_NOT_FOUND'
| 'SCHEMA_VALIDATION_FAILED'
| 'UNKNOWN_EVENT_TYPE'
| 'SYNC_FAILED'
| 'PUBLISH_FAILED'
| 'QUERY_FAILED'
| 'QUERY_NOT_SUPPORTED'
| 'MEDIATOR_ERROR'
| 'NO_TRANSPORT'Transport
The DecentrlTransport interface is pluggable. The default DirectTransport uses HTTP + WebSocket. See Transport Layer for custom implementations.