Ably Specification: Features

See also:

This document outlines the complete feature set of both the REST and Realtime client libraries. It is expected that every client library developer refers to this document to ensure that their client library provides the same API and features as the existing Ably client libraries. In addition to this, it is essential that there is test coverage over all of the features described below. As an example, see the Ruby library test specification and coverage generated from the test suite.

We recommend you use the IDL and refer to other existing libraries that adhere to this spec as a reference when reviewing how the API has been implemented.

The key words “must”, “must not”, “required”, “shall”, “shall not”, “should”, “should not”, “recommended”, “may”, and “optional” (whether lowercased or uppercased) in this document are to be interpreted as described in RFC 2119 .

Please note we maintain a separate Google Sheet that keeps track of which features are implemented and matching test coverage for each client library. If you intend to work on an Ably client library, please contact us for access to this Google Sheet as it is useful as a reference and also needs to be kept up to date

Specification and Protocol Versions

Test guidelines

REST client library

RestClient

Auth

Channels

RestChannel

Plugins

PluginType

VCDiffDecoder

RestPresence

Encryption

Forwards compatibility

Realtime client library features

The Ably Realtime client libraries establish and maintain a persistent connection to Ably and provide methods to publish and subscribe to messages over a low latency realtime connection.

The Realtime library is a super-set of the REST library and as such all Realtime libraries provide the functionality available in the REST library in addition to Realtime-specific features.

The threading and/or asynchronous model for each realtime library will vary by language and it is therefore up to the developer to decide on the best approach for each given client library. For example, Node.js and Ruby (EventMachine) use a similar callback single threaded evented approach that ensures all public methods are non-blocking. Java and .NET use a threaded model whereby the Connection runs in its own thread. Go makes extensive use of goroutines and channels.

RealtimeClient

Connection

Channels

RealtimeChannel

RealtimePresence

EventEmitter mixin / interface

Incremental backoff and jitter

Forwards compatibility

State conditions and operations

Connection.state effects on realtime operations

Initialized Connecting Connected Disconnected Suspended Closing Closed Failed
connect RTN11a No-op No-op RTN11c RTN11c RTN11b RTN11a RTN11d
close No-op RTN12f RTN12a RTN12d RTN12d No-op No-op No-op
ping RTN13b RTN13c RTN13a RTN13c RTN13b RTN13b RTN13b RTN13b
RealtimeChannel attach RTL4h RTL4h See channel states table RTL4h RTL4b RTL4b RTL4b RTL4b
RealtimeChannel detach RTL5h RTL5h See channel states table RTL5h See channel states table RTL5g See channel states table RTL5g
RealtimeChannel publish RTL6c2 RTL6c2 See channel states table RTL6c2 RTL6c4 RTL6c4 RTL6c4 RTL6c4
Presence ops. RTP16b RTP16b See channel states table RTP16b RTP16c RTP16c RTP16c RTP16c

RealtimeChannel.state effects on channel operations

Initialized Attaching Attached Suspended Detaching Detached Failed
attach RTL4c RTL4h RTL4a RTL4c RTL4h RTL4c RTL4g
detach RTL5h RTL5i RTL5d RTL5j RTL5i RTL5a RTL5b
publish RTL6c2 RTL6c2 RTL6c1 RTL6c4 RTL6c4 RTL6c4 RTL6c3
Presence ops. RTP16b RTP16b RTP16a RTP16c RTP16c RTP16c RTP16c

Push notifications

Activation State Machine

Push device authentication

Push channels

LocalDevice

Types

Data types

Message

DeltaExtras

PresenceMessage

ProtocolMessage

PaginatedResult

HttpPaginatedResponse

TokenRequest

TokenDetails

Token string

AuthDetails

Stats

ErrorInfo

ConnectionStateChange

ChannelStateChange

Capability – API not defined yet

ConnectionDetails

ChannelProperties

ChannelDetails

ChannelStatus

ChannelOccupancy

ChannelMetrics

BatchResult

BatchPublishSpec

BatchPublishSuccessResult

BatchPublishFailureResult

BatchPresenceSuccessResult

BatchPresenceFailureResult

TokenRevocationTargetSpecifier

TokenRevocationSuccessResult

TokenRevocationFailureResult

MessageFilter

ReferenceExtras

Option types

ClientOptions

TokenParams

AuthOptions

ChannelOptions

DeriveOptions

CipherParams

CipherParamOptions

Push notifications

PushChannelSubscription

DeviceDetails

DevicePushDetails

Client library introspection

ClientInformation

Client Library defaults

The following default values are configured for the client library:

Interface Definition

The following bespoke IDL (Interface Definition Language) describes the types and classes present in the REST and Realtime client libraries.

Please note the following conventions:

Each type, method, and attribute is labelled with the name of one or more clauses from the preceding feature spec. An asterisk is used as a wildcard to mean all clauses with a given prefix.

class RestClient: // RSC*
  constructor(keyOrTokenStr: String) // RSC1
  constructor(ClientOptions) // RSC1
  auth: Auth // RSC5
  push: Push // RSC21
  device() => io LocalDevice // RSH8
  channels: Channels<RestChannel> // RSN1
  request(
    String method,
    String path,
    Int version,
    Dict<String, String> params?,
    JsonObject | JsonArray body?,
    Dict<String, String> headers?
  ) => io HttpPaginatedResponse // RSC19
  stats(
    start: Time api-default epoch(), // RSC6b1
    end: Time api-default now(), // RSC6b1
    direction: .Backwards | .Forwards api-default .Backwards, // RSC6b2
    limit: int api-default 100, // RSC6b3
    unit: .Minute | .Hour | .Day | .Month api-default .Minute // RSC6b4
  ) => io PaginatedResult<Stats> // RSC6a
  time() => io Time // RSC16
  batchPublish(BatchPublishSpec) => io BatchResult<BatchPublishSuccessResult | BatchPublishFailureResult> // RSC22
  batchPublish(BatchPublishSpec[]) => io BatchResult<BatchPublishSuccessResult | BatchPublishFailureResult>[] // RSC22
  batchPresence(string[]) => io BatchResult<BatchPresenceSuccessResult | BatchPresenceFailureResult> // RSC24

class RealtimeClient: // RTC*
  constructor(keyOrTokenStr: String) // RTC12
  constructor(ClientOptions) // RTC12
  auth: Auth // RTC4
  push: Push // RTC13
  device() => io LocalDevice // RSH8
  channels: Channels<RealtimeChannel> // RTC3, RTS1
  clientId: String? // RTC17
  connection: Connection // RTC2
  request(
    String method,
    String path,
    Dict<String, String> params?,
    JsonObject | JsonArray body?,
    Dict<String, String> headers?
  ) => io HttpPaginatedResponse // RTC9
  stats(
    start: Time api-default epoch(),
    end: Time api-default now(),
    direction: .Backwards | .Forwards api-default .Backwards,
    limit: int api-default 100,
    unit: .Minute | .Hour | .Day | .Month api-default .Minute
  ) => io PaginatedResult<Stats> // Same as RestClient.stats, RTC5
  close() // RTC16
  connect() // RTC15
  time() => io Time // RTC6
  batchPublish(BatchPublishSpec) => io BatchResult<BatchPublishSuccessResult | BatchPublishFailureResult> // RSC22
  batchPublish(BatchPublishSpec[]) => io BatchResult<BatchPublishSuccessResult | BatchPublishFailureResult>[] // RSC22
  batchPresence(string[]) => io BatchResult<BatchPresenceSuccessResult | BatchPresenceFailureResult> // RSC24

class ClientOptions: // TO*
  embeds AuthOptions // This is not currently documented in the spec and needs to be – see https://github.com/ably/docs/issues/1476
  autoConnect: Bool default true // RTC1b, TO3e
  clientId: String? // RSC17, RSA15, TO3a
  defaultTokenParams: TokenParams? // TO3j11
  echoMessages: Bool default true // RTC1a, TO3h
  environment: String? // RSC15e, TO3k1
  logHandler: // platform specific - TO3c
  logLevel: // platform specific - TO3b
  logExceptionReportingUrl: String default "[library specific]" // TO3m (deprecated)
  port: Int default 80 // TO3k4
  queueMessages: Bool default true // RTP16b, TO3g
  restHost: String default "rest.ably.io" // RSC12, TO3k2
  realtimeHost: String default "realtime.ably.io" // RTC1d, TO3k3
  fallbackHosts: String[] default nil // RSC15b, RSC15a, TO3k6
  fallbackHostsUseDefault: Bool default false // TO3k7 (deprecated)
  recover: String? // RTC1c, TO3i
  tls: Bool default true // RSC18, TO3d
  tlsPort: Int default 443 // TO3k5
  useBinaryProtocol: Bool default true // TO3f
  transportParams: [String: Stringifiable]? // TO3q, RTC1f
  addRequestIds: Bool default false // TO3p
  // configurable retry and failure defaults
  disconnectedRetryTimeout: Duration default 15s // TO3l1
  suspendedRetryTimeout: Duration default 30s // RTN14e, TO3l2
  channelRetryTimeout: Duration default 15s // RTL13b, TO3l7
  httpOpenTimeout: Duration default 4s // TO3l3
  httpRequestTimeout: Duration default 10s // TO3l4
  realtimeRequestTimeout: Duration default 10s // TO3l11
  httpMaxRetryCount: Int default 3 // TO3l5
  httpMaxRetryDuration: Duration default 15s // TO3l6
  maxMessageSize: Int default 65536 // TO3l8
  maxFrameSize: Int default 524288 // TO3l9
  fallbackRetryTimeout: Duration default 600s // TO3l10
  plugins: Dict<PluginType, Plugin> // TO3o
  idempotentRestPublishing: bool default true // RSL1k1, RTL6a1, TO3n
  agents: [String: String?]? // RSC7d6 - interface only offered by some libraries

class AuthOptions: // AO*
  authCallback: ((TokenParams) -> io (String | TokenDetails | TokenRequest | JsonObject))? // RSA4a, RSA4, TO3j5, AO2b
  authHeaders: [String: Stringifiable]? // RSA8c3, TO3j8, AO2e
  authMethod: .GET | .POST default .GET // RSA8c, TO3j7, AO2d
  authParams: [String: Stringifiable]? // RSA8c3, RSA8c1, TO3j9, AO2f
  authUrl: String? // RSA4a, RSA4, RSA8c, TO3j6, AO2c
  key: String? // RSA11, RSA14, TO3j1, AO2a
  queryTime: Bool default false // RSA9d, TO3j10, AO2g
  token: String? | TokenDetails? | TokenRequest? // RSA4a, RSA4, TO3j2, AO2h
  tokenDetails: TokenDetails? // RSA4a, RSA4, TO3j3, AO2i
  useTokenAuth: Bool? // RSA4, RSA14, TO3j4, AO2j

class TokenParams: // TK*
  capability: String api-default '{"*":["*"]}' // RSA9f, TK2b
  clientId: String? // TK2c
  nonce: String? // RSA9c, Tk2e
  timestamp: Time? // RSA9d, TK2d
  ttl: Duration api-default 60min // RSA9e, TK2a

class Auth: // RSA*
  clientId: String? // RSA7, RSC17, RSA12
  authorize(TokenParams?, AuthOptions?) => io TokenDetails // RSA10
  createTokenRequest(TokenParams?, AuthOptions?) => io TokenRequest // RSA9
  requestToken(TokenParams?, AuthOptions?) => io TokenDetails // RSA8
  tokenDetails: TokenDetails? // RSA16
  revokeTokens(TokenRevocationTargetSpecifier[], issuedBefore Time?, allowReauthMargin boolean?) => io BatchResult<TokenRevocationSuccessResult | TokenRevocationFailureResult> // RSA17

class TokenDetails: // TD*
  +fromJson(String | JsonObject) -> TokenDetails // TD7
  capability: String // TD5
  clientId: String? // TD6
  expires: Time // TD3
  issued: Time // TD4
  token: String // TD2

class TokenRequest: // TE*
  +fromJson(String | JsonObject) -> TokenRequest // TE6
  capability: String // TE3
  clientId: String? // TE2
  keyName: String // TE2
  mac: String // TE2
  nonce: String // TE2
  timestamp: Time? // TE5
  ttl: Duration? api-default 60min // TE4

class Channels<ChannelType>: // RSN*, RTS*
  exists(String) -> Bool // RSN2, RTS2
  get(String) -> ChannelType // RSN3, RTS3
  get(String, ChannelOptions) -> ChannelType // RSN3c, RTS3c
  iterate() -> Iterator<ChannelType> // RSN2, RTS2
  release(String) // RSN4, RTS4

class RestChannel: // RSL*
  name: String // RSL9
  presence: RestPresence // RSL3
  history(
    start: Time, // RSL2b1
    end: Time api-default now(), // RSL2b1
    direction: .Backwards | .Forwards api-default .Backwards, // RSL2b2
    limit: int api-default 100 // RSL2b3
  ) => io PaginatedResult<Message> // RSL2
  status() => ChannelDetails // RSL8
  publish(Message, params?: Dict<String, Stringifiable>) => io // RSL1
  publish([Message], params?: Dict<String, Stringifiable>) => io // RSL1
  publish(name: String?, data: Data?) => io // RSL1
  setOptions(options: ChannelOptions) => io // RSL7 - note asynchronous return value for
    // compatibility with RealtimeChannel#setOptions; not required for REST-only libraries

  // Only on platforms that support receiving push notifications:
  push: PushChannel // RSH7

class RealtimeChannel: // RTL*
  embeds EventEmitter<ChannelEvent, ChannelStateChange?> // RTL2, RTL2a, RTL2d
  name: String // RTL23
  errorReason: ErrorInfo? // RTL24
  state: ChannelState // RTL2b
  whenState(ChannelState, (ChannelStateChange?) ->) // RTL25
  presence: RealtimePresence // RTL9
  properties: ChannelProperties // RTL15
  // Only on platforms that support receiving push notifications:
  push: PushChannel // RSH7
  modes: readonly [ChannelMode] // RTL4m
  params: readonly Dict<String, String> // RTL4k1
  attach() => io ChannelStateChange // RTL4
  detach() => io // RTL5
  history(
    start: Time, // RTL10a
    end: Time api-default now(), // RTL10a
    direction: .Backwards | .Forwards api-default .Backwards, // RTL10a
    limit: int api-default 100, // RTL10a
    untilAttach: Bool default false // RTL10b
  ) => io PaginatedResult<Message> // RTL10
  publish(Message) => io // RTL6, RTL6i
  publish([Message]) => io // RTL6, RTL6i
  publish(name: String?, data: Data?) => io // RTL6, RTL6i
  subscribe((Message) ->) => io ChannelStateChange // RTL7, RTL7a
  subscribe(String, (Message) ->) => io ChannelStateChange // RTL7, RTL7b
  subscribe(MessageFilter, (Message) ->) io ChannelStateChange // RTL22
  unsubscribe() // RTL8, RTL8c
  unsubscribe((Message) ->) // RTL8, RTL8a
  unsubscribe(String, (Message) ->) // RTL8, RTL8b
  unsubscribe(MessageFilter, (Message) ->) // RTL22
  setOptions(options: ChannelOptions) => io // RTL16

class MessageFilter: // MFI*
  isRef: bool // MFI2a
  refTimeserial: string // MFI2b
  refType: string // MFI2c
  name: string // MFI2d

class ChannelProperties: // CP*
  attachSerial: String // CP2a
  channelSerial: String // CP2b

// Only on platforms that support receiving push notifications:
class PushChannel: // RSH7
  subscribeDevice() => io // RSH7a
  subscribeClient() => io // RSH7b
  unsubscribeDevice() => io // RSH7c
  unsubscribeClient() => io // RSH7d
  listSubscriptions(params?: Dict<String, String>) => io PaginatedResult<PushChannelSubscription> // RSH7e

enum ChannelState: // RTL2
  INITIALIZED
  ATTACHING
  ATTACHED
  DETACHING
  DETACHED
  SUSPENDED
  FAILED

enum ChannelEvent: // RTL2
  embeds ChannelState
  UPDATE // RTL2g

enum ChannelMode: // TB2d
  PRESENCE
  PUBLISH
  SUBSCRIBE
  PRESENCE_SUBSCRIBE

class ChannelStateChange: // TH*
  current: ChannelState // TH2, RTL2a, RTL2b
  event: ChannelEvent // TH5
  previous: ChannelState // TH2, RTL2a, RTL2b
  reason: ErrorInfo? // TH3
  resumed: Boolean // RTL2f, TH4
  hasBacklog: Boolean // RTL2i, TH6

class ChannelOptions: // TB*
  +withCipherKey(key: Binary | String)? -> ChannelOptions // TB3
  cipher: (CipherParams | CipherParamOptions)? // RSL5a, TB2b
  params?: Dict<String, String> // TB2c
  modes?: [ChannelMode] // TB2d

class DeriveOptions: // DO*
  filter: String // DO2a (The filter string is a valid JMESPath String Expression)

class ChannelDetails: // CHD*
  channelId: String // CHD2a
  status: ChannelStatus // CHD2b

class ChannelStatus: // CHS*
  isActive: Boolean // CHS2a
  occupancy: ChannelOccupancy // CHS2b

class ChannelOccupancy: // CHO*
  metrics: ChannelMetrics // CHO2a

class ChannelMetrics: // CHM*
  connections: Int // CHM2a
  presenceConnections: Int // CHM2b
  presenceMembers: Int // CHM2c
  presenceSubscribers: Int // CHM2d
  publishers: Int // CHM2e
  subscribers: Int // CHM2f

class CipherParams: // TZ*
  algorithm: String default "AES" // TZ2a
  key: Binary // TZ2d
  keyLength: Int // TZ2b
  mode: String default "CBC" // TZ2c

class CipherParamOptions: // CO* (may be implemented as a hashmap or a class depending on language)
  algorithm?: String // CO2a
  key: Binary | String // CO2b
  keyLength?: Int // CO2c
  mode?: String // CO2d

class Crypto: // RSE*
  +getDefaultParams(CipherParamOptions) -> CipherParams // RSE1
  +generateRandomKey(keyLength: Int?) => io Binary // RSE2

class RestPresence: // RSP*
  get(
    limit: int api-default 100, // RSP3a
    clientId: String?, // RSP3a2
    connectionId: String?, // RSP3a3
  ) => io PaginatedResult<PresenceMessage> // RSP3
  history(
    start: Time, // RSP4b1
    end: Time api-default now(), // RSP4b1
    direction: .Backwards | .Forwards api-default .Backwards, // RSP4b2
    limit: int api-default 100, // RSP4b3
  ) => io PaginatedResult<PresenceMessage> // RSP4

class RealtimePresence: // RTP*
  syncComplete: Bool // RTP13
  get(
    waitForSync: Bool default true, // RTP11c1
    clientId: String?, // RTP11c2
    connectionId: String?, // RTP11c3
  ) => io [PresenceMessage] // RTP11
  history(
    start: Time, // RTP12a
    end: Time, // RTP12a
    direction: .Backwards | .Forwards api-default .Backwards, // RTP12a
    limit: int api-default 100, // RTP12a
  ) => io PaginatedResult<PresenceMessage> // RTP12
  subscribe((PresenceMessage) ->) => io // RTP6a
  subscribe(PresenceAction | [PresenceAction], (PresenceMessage) ->) => io // RTP6b
  unsubscribe() // RTP7c
  unsubscribe((PresenceMessage) ->) // RTP7a
  unsubscribe(PresenceAction, (PresenceMessage) ->) // RTP7b
  // presence state modifiers
  enter(Data?, extras?: JsonObject) => io // RTP8
  update(Data?, extras?: JsonObject) => io // RTP9
  leave(Data?, extras?: JsonObject) => io // RTP10
  enterClient(clientId: String, Data?, extras?: JsonObject) => io // RTP4, RTP14, RTP15
  updateClient(clientId: String, Data?, extras?: JsonObject) => io // RTP15
  leaveClient(clientId: String, Data?, extras?: JsonObject) => io // RTP15

enum PresenceAction: // TP2
  ABSENT // TP2
  PRESENT // TP2
  ENTER // TP2
  LEAVE // TP2
  UPDATE // TP2

class ConnectionDetails: // CD*, internal
  clientId: String? // RSA12a, CD2a
  connectionKey: String // RTN15e, CD2b
  connectionStateTtl: Duration // CD2f, RTN14e, DF1a
  maxFrameSize: Int // CD2d
  maxInboundRate: Int // CD2e
  maxMessageSize: Int // CD2c
  serverId: String // CD2g
  maxIdleInterval: Duration // CD2h

class Message: // TM*
  constructor(name: String?, data: Data?) // TM4
  constructor(name: String?, data: Data?, clientId: String?) // TM4
  +fromEncoded(JsonObject, ChannelOptions?) -> Message // TM3
  +fromEncodedArray(JsonArray, ChannelOptions?) -> [Message] // TM3
  clientId: String? // TM2b
  connectionId: String? // TM2c
  connectionKey: String? // TM2h
  data: Data? // TM2d
  encoding: String? // TM2e
  extras: JsonObject? // TM2i
  id: String // TM2a
  name: String? // TM2g
  timestamp: Time // TM2f

class PresenceMessage // TP*
  +fromEncoded(JsonObject, ChannelOptions?) -> PresenceMessage // TP4
  +fromEncodedArray(JsonArray, ChannelOptions?) -> [PresenceMessage] // TP4
  action: PresenceAction // TP3b
  clientId: String // TP3c
  connectionId: String // TP3d
  data: Data? // TP3e
  encoding: String? // TP3f
  extras: JsonObject? // TP3i
  id: String // TP3a
  timestamp: Time // TP3g
  memberKey() -> String // TP3h

class ProtocolMessage: // internal
  action: ProtocolMessageAction // TR2, TR4a
  auth: AuthDetails? //
  channel: String? // TR4b
  channelSerial: String? // TR4c
  connectionDetails: ConnectionDetails? // RSA7b3, RTN19, TR4o
  connectionId: String? // RTN15c1, TR4d
  count: Int? // TR4g
  error: ErrorInfo? // RTN15c2, TR4h
  flags: Int? // TR4i; bitfield containing zero or more boolean flags specified in TR3
  id: String? // TR4b
  messages: [Message]? // TR4k
  msgSerial: Int? // RTN7b, TR4j
  presence: [PresenceMessage]? // TR4l
  timestamp: Time? // TR4m
  params: Dict<String, String>? // TR4q, RTL4k

enum ProtocolMessageAction: // internal
  HEARTBEAT // TR2
  ACK // TR2
  NACK // TR2
  CONNECT // TR2
  CONNECTED // TR2
  DISCONNECT // TR2
  DISCONNECTED // TR2
  CLOSE // TR2
  CLOSED // TR2
  ERROR // TR2
  ATTACH // TR2
  ATTACHED // TR2
  DETACH // TR2
  DETACHED // TR2
  PRESENCE // TR2
  MESSAGE // TR2
  SYNC // TR2
  AUTH // TR2

class AuthDetails: // AD*
  accessToken: String // AD2, RTC8a

class Connection: // RTN*
  embeds EventEmitter<ConnectionEvent, ConnectionStateChange> // RTN4a, RTN4e
  errorReason: ErrorInfo? // RTN25
  id: String? // RTN8
  key: String? // RTN9
  createRecoveryKey(): String? // RTN16g
  state: ConnectionState // RTN4d
  whenState(ConnectionState, (ConnectionStateChange?) ->) // RTN26
  close() // RTN12
  connect() // RTC1b, RTN3, RTN11
  ping() => io Duration // RTN13

enum ConnectionState: // RTN4
  INITIALIZED
  CONNECTING
  CONNECTED
  DISCONNECTED
  SUSPENDED
  CLOSING
  CLOSED
  FAILED

enum ConnectionEvent: // RTN4
  embeds ConnectionState
  UPDATE // RTN4h

class ConnectionStateChange: // TA*
  current: ConnectionState // TA2
  event: ConnectionEvent // TA5
  previous: ConnectionState // TA2
  reason: ErrorInfo? // RTN4f, TA3
  retryIn: Duration? // RTN14d, TA2

class Stats: // TS12
  intervalId: String // TS12a
  intervalTime: Time // TS12p (calculated client-side)
  unit: Stats.IntervalGranularity // TS12c
  inProgress: String? // TS12q
  entries: Dict<String, Int> // TS12r
  schema: String // TS12s
  appId: String // TS12t

enum StatsIntervalGranularity: // TS12c
  MINUTE
  HOUR
  DAY
  MONTH

class DeviceDetails: // PCD*
  id: String // PCD2
  clientId: String? // PCD3
  formFactor: DeviceFormFactor // PCD4
  metadata: JsonObject // PCD5
  platform: DevicePlatform // PCD6
  push: DevicePushDetails // PCD7

class DevicePushDetails: // PCP*
  errorReason: ErrorInfo? // PCP2
  recipient: JsonObject // PCP3
  state: .Active | .Failing | .Failed // PCP4

class LocalDevice extends DeviceDetails: // RSH8*
  deviceIdentityToken: String // RSH8k1
  deviceSecret: String // RSH8k2

class Push: RSH1, RSH2
  admin: PushAdmin // RSH1

  // Only on platforms that support receiving push notifications:

  activate(
    registerCallback: ((ErrorInfo?, DeviceDetails?) -> io String)?,
    // Only on platforms that, after first set, can update later its push
    // device details:
    updateFailedCallback: ((ErrorInfo) ->), // Deprecated, see RSH3e3a and RSH3e3d
    updatedCallback: ((ErrorInfo?) ->)?
  ) => io ErrorInfo? // RSH2a
  deactivate(
    deregisterCallback: ((ErrorInfo?, deviceId: String?) -> io)?
  ) => io ErrorInfo? // RSH2b

class PushAdmin: // RSH1
  publish(recipient: JsonObject, data: JsonObject) => io // RSH1a
  deviceRegistrations: PushDeviceRegistrations // RSH1b
  channelSubscriptions: PushChannelSubscriptions // RSH1c

class PushDeviceRegistrations: // RSH1b
  get(deviceId: String) => io DeviceDetails // RSH1b1
  list(params: Dict<String, String>) => io PaginatedResult<DeviceDetails> // RSH1b2
  save(DeviceDetails) => io DeviceDetails // RSH1b3
  remove(deviceId: String) => io // RSH1b4
  removeWhere(params: Dict<String, String>) => io // RSH1b5

class PushChannelSubscriptions: // RSH1c
  list(params: Dict<String, String>) => io PaginatedResult<PushChannelSubscription> // RSH1c1
  listChannels(params: Dict<String, String>?) => io PaginatedResult<String> // RSH1c2
  save(PushChannelSubscription) => io PushChannelSubscription // RSH1c3
  remove(PushChannelSubscription) => io // RSH1c4
  removeWhere(params: Dict<String, String>) => io // RSH1c5

enum DevicePlatform: // PCD6
  "android"
  "ios"
  "browser"

enum DeviceFormFactor: // PCD4
  "phone"
  "tablet"
  "desktop"
  "tv"
  "watch"
  "car"
  "embedded"
  "other"

class PushChannelSubscription: // PCS*
  +forDevice(channel: String, deviceId: String) => PushChannelSubscription // PSC5
  +forClientId(channel: String, clientId: String) => PushChannelSubscription // PSC5
  deviceId: String? // PCS2
  clientId: String? // PCS3
  channel: String // PCS4

class ErrorInfo: // TI*
  code: Int // TI1
  href: String? // TI1, TI4
  message: String // TI1
  cause: ErrorInfo? // TI1
  statusCode: Int // TI1
  requestId: String? // TI1, RSC7c

class EventEmitter<Event, Data>: // RTE*
  on((Data...) ->) // RTE3
  on(Event, (Data...) ->) // RTE3
  once((Data...) ->) // RTE4
  once(Event, (Data...) ->) // RTE4
  off() // RTE5
  off((Data...) ->) // RTE5
  off(Event, (Data...) ->) // RTE5
  emit(Event, Data...)  // internal, RTE6

class PaginatedResult<T>: // TG*
  items: [T] // TG3
  first() => io PaginatedResult<T> // TG5
  hasNext() -> Bool // TG6
  isLast() -> Bool // TG7
  next() => io PaginatedResult<T>? // TG4

class HttpPaginatedResponse // HP*
  embeds PaginatedResult<JsonObject>
  items: [JsonObject] // HP3
  statusCode: Int // HP4
  success: Bool // HP5
  errorCode: Int // HP6
  errorMessage: String // HP7
  headers: Dict<String, String> // HP8

class Plugin // PC2
  // Empty class/interface. Plugins are not expected to share any common interface.
  // An opaque base interface type for plugins is defined for type-safety in statically-typed languages.

enum PluginType // PT*
  "vcdiff" // PT2a

class VCDiffDecoder // VD*
  decode([byte] delta, [byte] base) -> [byte] // VD2a, PC3a

class DeltaExtras // DE*
  from: String // DE2a
  format: String // DE2b

class ReferenceExtras: // REX*
  timeserial: String // REX2a
  type: String //REX2b

class ClientInformation: // CR*
  +agents: Dict<String, String?> // CR2
  +agentIdentifier(additionalAgents: Dict<String, String?>?) => String // CR3; interface only offered by some libraries

class BatchResult<T>
  successCount: number // BAR2a
  failureCount: number // BAR2b
  results: [T] // BAR2c

class BatchPublishSpec:
  channels: [String] // BSP2a
  messages: [Message] //BSP2b

class BatchPublishSuccessResult:
  channel: string // BPR2a
  messageId: string // BPR2b

class BatchPublishFailureResult:
  channel: string // BPF2a
  error: ErrorInfo // BPF2c

class BatchPresenceSuccessResult:
  channel: string // BGR2a
  presence: [PresenceMessage] // BGR2b

class BatchPresenceFailureResult
  channel: string // BGF2a
  error: ErrorInfo // BGF2b

class TokenRevocationTargetSpecifier:
  type: string // TRT2a
  value: string // TRT2b

class TokenRevocationSuccessResult:
  target: string // TRS2a
  appliesAt: Time // TRS2b
  issuedBefore: Time // TRS2c

class TokenRevocationFailureResult:
  target: string // TRF2a
  error: ErrorInfo // TRF2b

Old specs

Use the version navigation to view older versions. References to diffs for each version are maintained below: