ConversionResult

Both functions return plain JSON-serializable data. Receipts can contain private file IDs, so they are not safe for unrestricted logs.

ConversionResult

Returned by convert():

interface ConversionResult {
  readonly blocks: ReadonlyArray<Block>;
  readonly text: string;
  readonly degradations: ReadonlyArray<Degradation>;
  readonly receipts: ReadonlyArray<UploadReceipt>;
}
FieldMeaning
blocksBlock Kit JSON for chat.postMessage's blocks field. At most 50 blocks; default conversion always passes validateBlocks.
textPlain-text rendering for the text field (push notifications, accessibility). Never empty; clamped to Slack's 40,000-character threshold.
degradationsFallbacks and truncations, in document order. Empty means no fallback was recorded (known gaps for raw block HTML).
receiptsImmutable external upload effects, including attempts hidden by later renderer success. Sensitive operational data.

ConversionMessagesResult

Returned by convertToMessages():

interface ConversionMessagesResult {
  readonly messages: ReadonlyArray<ConversionMessage>;
  readonly degradations: ReadonlyArray<Degradation>;
  readonly receipts: ReadonlyArray<UploadReceipt>;
}

interface ConversionMessage {
  readonly blocks: ReadonlyArray<Block>;
  readonly text: string;
}

Each message is independently postable with its own notification text; degradations covers the whole conversion. Without overflow: "split" the array always has exactly one entry.

Degradation

interface Degradation {
  readonly nodeType: string;
  readonly from: string;
  readonly to: string;
  readonly reason: string;
  readonly path: string;
  readonly details?: {
    readonly code?: string;
    readonly stage?: string;
    readonly retryable?: boolean;
    readonly rendererId?: string;
  };
}
FieldContents
nodeTypeMarkdown node or subsystem that degraded: "heading", "table", "code.mermaid", "message", …
fromWhat was attempted: a block form ("header.h5"), a size ("63 body blocks"), or a source value
toWhat was emitted instead: "rich_text.bold", "data_table", "truncated", "omitted", …
reasonHuman-readable explanation, including the limit that applied
pathPosition in the source document. "root[2]" is the third top-level node; "assemble" marks message-level packing
detailsOptional safe renderer metadata; absent for ordinary structural fallbacks

A level-5 heading, for example (Slack headers stop at 4):

{
  "nodeType": "heading",
  "from": "header.h5",
  "to": "rich_text.bold",
  "reason": "Heading level 5 exceeds max 4",
  "path": "root[0]"
}

Treat reason as display text, not a parseable format.

UploadReceipt

interface UploadReceipt {
  readonly kind: "upload";
  readonly provider: string;
  readonly rendererId?: string;
  readonly filename?: string;
  readonly phase: "requested" | "allocated" | "uploaded" | "completed" | "shared" | "unknown";
  readonly fileId?: string;
  readonly shared: boolean;
  readonly processing: "not-requested" | "pending" | "verified" | "unverified";
  readonly status?: number;
  readonly retryAfterMs?: number;
  readonly providerCode?: string;
  readonly retrySafety: "never" | "safe" | "may-duplicate";
}

Receipts are independent from content fallback and survive later renderer success, fallback, and typed errors when known.

Prompt operation/shutdown errors carry a frozen receipts snapshot and a non-rejecting settledReceipts promise tied to that operation. It resolves after underlying collaborators settle and may remain pending forever for a never-settling collaborator.

Block

blocks entries are typed by the exported Block union (rich_text, header, section, image, context, divider, markdown, table, data_table, data_visualization, and container), matching Slack's wire format field-for-field. All block and rich text element types are exported for use in your own code (RichTextBlock, TableBlock, RichTextInline, …).