Skip to main content
definePlatform is the entry point for building your own provider. It takes a platform ID and a definition object, and returns a callable that:
  • exposes a .config() method for registering the provider on Spectrum()
  • accepts the app, a , or a for narrowing
  • carries any static properties you declare (like iMessage’s effect constants)
The core definition shape is . You pass the platform ID as the first argument to definePlatform. The optional static object is an additional definePlatform input whose properties are copied onto the returned provider.

Platform IDs

Platform IDs are stable, developer-facing identifiers. Spectrum exposes the ID on message.platform and __platform, and uses it for provider registration, webhook routing, telemetry, and environment-variable prefixes. An ID must start with a lowercase letter and contain only lowercase letters, numbers, and single underscores between segments. In other words, it must match /^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/. Examples include discord, whatsapp_business, and my_platform. definePlatform rejects invalid IDs instead of silently normalizing them, so names containing uppercase letters, spaces, or hyphens must be corrected by the platform author.

Shape

Field reference

Return a from send when the content produces a message. Return undefined for fire-and-forget signals.

Message direction

Records yielded from messages are wrapped as inbound values. Records returned from send are wrapped as outbound messages. When a provider knows a record’s actual direction, it can set direction on the raw record and Spectrum uses it instead of the wrapping context:
This matters for nested records like reaction targets: the outer reaction is inbound (someone reacted) but the target may be outbound (they reacted to a message you sent). Without direction, nested targets default to the outer record’s direction.

Event producers

Every event generator receives { client, config, projectConfig, store } and returns an AsyncIterable. The signature is . The core messages stream lives at the top level of the definition. Optional custom event streams (presence, read receipts, etc.) live inside events:
Custom events are auto-wired as flat properties on both the Spectrum instance (app.presence) and the narrowed platform instance (myPlatform(app).presence).

Message extras

Declare a message.schema to add extra typed fields to every incoming message. The extractor surfaces them through a narrowed message:
Use when you need the message shape in your own types.

Instance actions

Methods declared in actions are projected onto the platform instance returned by myPlatform(app). The framework injects ctx = { client, config, store } as the first argument, so callers only pass the trailing args. Two tiers share the actions slot:
  • Platform-wise actions (getMessage, getMembers, getAvatar, getDisplayName) — framework-recognized names. Always present on every platform instance. When omitted, the framework wires a default that throws an .
  • Platform-specific actions — free-form keys for platform ergonomics (e.g. iMessage’s getAttachment). Only present when declared.

Fusor-backed providers

When your platform receives inbound messages through webhooks (rather than a persistent connection), use fusor(...) as the client in lifecycle.createClient. A Fusor client handles webhook signature verification and delivers parsed payloads to a handler:
The Fusor overload of definePlatform replaces the top-level messages async generator with a per-webhook-delivery handler. It receives a . Call respond() to set the HTTP response sent back to the webhook caller.

Registering your platform

Exported platforms work like the built-ins — register with .config() and use narrowing for the typed surface: