Guides
PostableObject Interface
FluxyChat's PostableObject interface defines a contract for objects that can be passed to thread.post() and rendered by adapters, enabling third-party extensions.
PostableObject Interface
FluxyChat's PostableObject interface defines a contract for objects that can be passed to thread.post() and rendered by adapters. This enables third-party extensions to create custom postable types.
Overview
A PostableObject is any object that implements the $$typeof, kind, isSupported(), getPostData(), getFallbackText(), and onPosted() interface. When posted, the adapter checks if it supports the object natively; if not, it falls back to the text representation.
Source: apps/worker/src/lib/postable-object.js
Interface
interface PostableObject {
$$typeof: symbol; // Must equal POSTABLE_OBJECT
kind: string; // Object kind (e.g. "card", "plan", "poll")
isSupported(adapter): boolean; // Can this adapter render it natively?
getPostData(): object; // Data to send to adapter.postObject()
getFallbackText(): string; // Text for unsupported adapters
onPosted(context): void; // Callback after posting (e.g. store messageId)
}Built-in Postable Objects
FluxyChat's Card builder produces objects that implement the PostableObject interface:
- Card — Rich interactive messages with buttons, tables, sections
- Plan — Streaming plan objects with task/step updates
Creating a Custom PostableObject
Using a class
import { POSTABLE_OBJECT, isPostableObject } from "@fluxy-chat/sdk";
class Poll {
constructor(question, options) {
this.question = question;
this.options = options;
}
get $$typeof() { return POSTABLE_OBJECT; }
get kind() { return "poll"; }
isSupported(adapter) {
// Only supported on web and Slack
return ["web", "slack"].includes(adapter.slug);
}
getFallbackText() {
return `${this.question}\n${this.options.map((o, i) => `${i + 1}. ${o}`).join("\n")}`;
}
getPostData() {
return {
question: this.question,
options: this.options,
};
}
onPosted(context) {
this.messageId = context.messageId;
this.threadId = context.threadId;
}
}Using the mixin helper
import { withPostable } from "@fluxy-chat/sdk";
class MyObject extends withPostable(BaseClass) {
kind = "my-object";
getFallbackText() {
return "Custom object (not supported here)";
}
getPostData() {
return { /* ... */ };
}
// isSupported() defaults to true, onPosted() defaults to no-op
}Posting
The postPostableObject helper handles the adapter negotiation:
import { postPostableObject } from "@fluxy-chat/sdk";
const poll = new Poll("Lunch?", ["Pizza", "Sushi", "Tacos"]);
await postPostableObject(
poll, // The PostableObject
adapter, // Target adapter (or null for web)
threadId, // Thread ID
postFn, // Fallback: (threadId, message) => Promise<{id}>
logger // Optional logger
);Flow:
- Check
poll.isSupported(adapter)→ if true and adapter haspostObject(), use native rendering. - Otherwise, call
postFn(threadId, poll.getFallbackText())for text fallback. - Call
poll.onPosted(context)with the result.
Type Guard
import { isPostableObject } from "@fluxy-chat/sdk";
function handleContent(content) {
if (isPostableObject(content)) {
console.log(`PostableObject of kind: ${content.kind}`);
// Use postPostableObject()
} else if (typeof content === "string") {
// Regular text message
}
}Context
The onPosted callback receives a context object:
interface PostableObjectContext {
adapter: Adapter; // Adapter instance that posted
messageId: string; // ID of the sent message
threadId: string; // Thread ID where it was posted
logger?: Logger; // Optional logger
}See Also
- Card Builder — Built-in PostableObject for rich messages
- Unified Chat API — Thread posting methods
- Adapter Pattern — How adapters handle PostableObjects