Message bridge
A block and its host talk over window.postMessage. Every message is a { type, payload } object discriminated by type. The tables below are generated from the published @civitai/app-sdk message unions (for payload shapes) joined with the civitai host parity inventory (for direction, request/reply pairing, and page-only flags).
Most builders never send these by hand — the React hooks wrap them. This page is the contract for advanced use and non-React SDK consumers.
Conventions
- block → host vs host → block — the direction of the message.
- request → reply — a request-style message that awaits a specific reply type (shown). An unhandled request-style message hangs the block until its SDK timeout, so the host always registers a handler.
- fire-and-forget — a block → host message with no reply; ignoring it is a silent no-op, never a hang.
- page-only — handled only by the full-page host (a page app at
/apps/run/<slug>), not by the model-slot host today. Slot apps are deferred during the closed beta, so build page apps and you get the full surface. - 🔴 A host → block push added after your block was built is inert until you rebuild. Your bundle compiles in the
@civitai/blocks-reactyou built against, so it only understands the messages that version knew about. A push the host began sending later —THEME_CHANGEis the current example — is silently discarded: the transport finds no validator for the type, matches no pending request and no push listener, and no-ops. No error, no warning, and nothing to see in the console. Nothing rebuilds a deployed block on your behalf, so it stays frozen at its build-time SDK until you resubmit; rebuilding against the current SDK picks up every push added since, automatically.
Lifecycle
BLOCK_ERRORblock → hostfire-and-forget{
message: string;
fatal: boolean;
}BLOCK_INIThost → blockBlockInitPayloadBLOCK_READYblock → hostfire-and-forget{
height: number;
}NAVIGATEblock → hostfire-and-forgetpage-only{
path: string;
target: 'current' | 'new_tab';
}RESIZE_IFRAMEblock → hostfire-and-forget{
height: number;
}RESUMEhost → blockSUSPENDhost → blockTRACK_EVENTblock → hostfire-and-forget{
eventName: string;
properties?: Record<string, unknown>;
}Auth & token
CONSENT_UNAVAILABLEhost → blockConsentUnavailablePayloadREQUEST_CONSENTblock → hostfire-and-forget{
scopes?: string[];
}REQUEST_SIGN_INblock → hostfire-and-forget{
returnUrl?: string;
}REQUEST_TOKENblock → hostrequest → reply{
requestId: string;
blockInstanceId: string;
}TOKEN_REFRESH_RESPONSE{
requestId: string;
token: WrappedToken;
}or a TOKEN_REFRESH push when no requestId was sent
TOKEN_REFRESHhost → block{
token: WrappedToken;
}Generation workflows
CANCEL_WORKFLOWblock → hostrequest → reply{
requestId: string;
workflowId: string;
}WORKFLOW_CANCELED{
requestId: string;
snapshot: BlockWorkflowSnapshot;
}ESTIMATE_WORKFLOWblock → hostrequest → reply{
requestId: string;
body: WorkflowBody;
}ESTIMATE_RESULT{
requestId: string;
snapshot: BlockWorkflowSnapshot;
}POLL_WORKFLOWblock → hostrequest → reply{
requestId: string;
workflowId: string;
waitSeconds?: number;
}WORKFLOW_STATUS{
requestId: string;
snapshot: BlockWorkflowSnapshot;
}SUBMIT_WORKFLOWblock → hostrequest → reply{
requestId: string;
body: WorkflowBody;
idempotencyKey?: string;
}WORKFLOW_SUBMITTED{
requestId: string;
snapshot: BlockWorkflowSnapshot;
}App subqueue
CANCEL_APP_WORKFLOWblock → hostrequest → replypage-only{
requestId: string;
workflowId: string;
}CANCEL_APP_WORKFLOW_RESULT{
requestId: string;
result?: {
workflow: AppWorkflow;
};
error?: string;
}QUERY_APP_WORKFLOWSblock → hostrequest → replypage-only{
requestId: string;
params?: AppWorkflowsParams;
}APP_WORKFLOWS_RESULT{
requestId: string;
result?: {
workflows: AppWorkflow[];
cursor: string | null;
};
error?: string;
}Buzz
GET_BUZZ_ACCOUNTSblock → hostrequest → replypage-only{
requestId: string;
}BUZZ_ACCOUNTS_RESULT{
requestId: string;
result?: {
accounts: BlockBuzzAccount[];
};
error?: string;
}GET_BUZZ_BALANCEblock → hostrequest → reply{
requestId: string;
}BUZZ_BALANCE_RESULT{
requestId: string;
balance?: {
blue: number;
green: number;
yellow: number;
};
error?: string;
}GET_BUZZ_TRANSACTIONSblock → hostrequest → replypage-only{
requestId: string;
params?: BlockBuzzTransactionsParams;
}BUZZ_TRANSACTIONS_RESULT{
requestId: string;
result?: {
cursor?: string;
transactions: BlockBuzzTransaction[];
};
error?: string;
}GET_DAILY_COMPENSATIONblock → hostrequest → replypage-only{
requestId: string;
params?: BlockDailyCompensationParams;
}DAILY_COMPENSATION_RESULT{
requestId: string;
result?: {
resources: BlockDailyCompensationResource[];
hasPublishedResources: boolean;
};
error?: string;
}OPEN_BUZZ_PURCHASEblock → hostrequest → reply{
requestId: string;
suggestedAmount?: number;
}BUZZ_PURCHASE_RESULT{
requestId: string;
purchased: boolean;
newBalance?: number;
}Viewer
GET_VIEWERblock → hostrequest → replypage-only{
requestId: string;
}VIEWER_RESULT{
requestId: string;
viewer?: BlockViewer;
error?: string;
}Pickers & upload
IMAGE_SCAN_RESOLVEDhost → block{
requestId: string;
imageId: number;
result: BlockImageScanResult;
}OPEN_CHECKPOINT_PICKERblock → hostrequest → reply{
requestId: string;
baseModelGroup: string;
/** Currently-selected versionId so the picker can pre-highlight it. */
currentVersionId?: number;
}CHECKPOINT_PICKER_RESULT{
requestId: string;
selected?: BlockCheckpointInfo;
}OPEN_IMAGE_UPLOADblock → hostrequest → replypage-only{
requestId: string;
purpose?: BlockUploadPurpose;
asyncScan?: boolean;
}IMAGE_UPLOAD_RESULT{
requestId: string;
selected?: BlockUploadedImageInfo | BlockGenerationSourceImageInfo | BlockPendingImageInfo;
}OPEN_RESOURCE_PICKERblock → hostrequest → replypage-only{
requestId: string;
resourceType: BlockResourcePickerType;
/** Optional base-model family hint (ecosystem key or baseModel name). */
baseModelGroup?: string;
}RESOURCE_PICKER_RESULT{
requestId: string;
selected?: BlockResourceInfo;
}SET_USER_CHECKPOINTblock → hostrequest → reply{
requestId: string;
versionId: number | null;
}USER_CHECKPOINT_SET{
requestId: string;
ok: boolean;
error?: string;
}Per-app storage
APP_STORAGE_DELETEblock → hostrequest → reply{
requestId: string;
key: string;
}APP_STORAGE_DELETE_RESULT{
requestId: string;
ok: boolean;
deleted: boolean;
error?: string;
}APP_STORAGE_GETblock → hostrequest → reply{
requestId: string;
key: string;
}APP_STORAGE_GET_RESULT{
requestId: string;
value: unknown;
error?: string;
}APP_STORAGE_LISTblock → hostrequest → reply{
requestId: string;
prefix?: string;
limit?: number;
cursor?: string;
}APP_STORAGE_LIST_RESULT{
requestId: string;
keys: Array<{
key: string;
updatedAt: string;
}>;
nextCursor?: string;
error?: string;
}APP_STORAGE_QUOTAblock → hostrequest → reply{
requestId: string;
}APP_STORAGE_QUOTA_RESULT{
requestId: string;
usedBytes: number;
rowCount: number;
limitBytes: number;
limitRows: number;
error?: string;
}APP_STORAGE_SETblock → hostrequest → reply{
requestId: string;
key: string;
value: unknown;
}APP_STORAGE_SET_RESULT{
requestId: string;
ok: boolean;
error?: string;
sizeBytes?: number;
}Shared storage
SHARED_APPENDblock → hostrequest → reply{
requestId: string;
value: SharedStorageValue;
}SHARED_APPEND_RESULT{
requestId: string;
key: string;
error?: string;
}SHARED_GETblock → hostrequest → reply{
requestId: string;
key: string;
}SHARED_GET_RESULT{
requestId: string;
item: SharedStorageItemWire | null;
error?: string;
}SHARED_GET_COUNTblock → hostrequest → reply{
requestId: string;
key: string;
}SHARED_GET_COUNT_RESULT{
requestId: string;
count: number;
error?: string;
}SHARED_GET_COUNTSblock → hostrequest → reply{
requestId: string;
keys: string[];
}SHARED_GET_COUNTS_RESULT{
requestId: string;
counts: Record<string, number>;
error?: string;
}SHARED_LISTblock → hostrequest → reply{
requestId: string;
prefix?: string;
limit?: number;
cursor?: string;
}SHARED_LIST_RESULT{
requestId: string;
items: SharedStorageItemWire[];
nextCursor?: string;
error?: string;
}SHARED_REPORTblock → hostrequest → reply{
requestId: string;
key: string;
reason?: string;
}SHARED_REPORT_RESULT{
requestId: string;
ok: boolean;
error?: string;
}SHARED_UNVOTEblock → hostrequest → reply{
requestId: string;
key: string;
}SHARED_UNVOTE_RESULT{
requestId: string;
count: number;
error?: string;
}SHARED_UPDATEblock → hostrequest → reply{
requestId: string;
key: string;
value: SharedStorageValue;
}SHARED_UPDATE_RESULT{
requestId: string;
ok: boolean;
error?: string;
}SHARED_VOTEblock → hostrequest → reply{
requestId: string;
key: string;
}SHARED_VOTE_RESULT{
requestId: string;
count: number;
error?: string;
}SHARED_WITHDRAWblock → hostrequest → reply{
requestId: string;
key: string;
}SHARED_WITHDRAW_RESULT{
requestId: string;
ok: boolean;
deleted: boolean;
error?: string;
}Wildcard packs
GET_WILDCARD_PACKblock → hostrequest → replypage-only{
requestId: string;
modelVersionId: number;
}WILDCARD_PACK_RESULT{
requestId: string;
pack?: BlockWildcardPack;
error?: BlockWildcardPackErrorCode;
}Other
BLOCK_HELLOblock → hostfire-and-forgetGET_IMAGES_BY_IDSblock → hostrequest → replypage-only{
requestId: string;
imageIds: number[];
}IMAGES_RESULT{
requestId: string;
result?: {
images: BlockGatedImage[];
};
error?: string;
}PUBLISH_GENERATION_OUTPUTSblock → hostrequest → replypage-only{
requestId: string;
workflowId: string;
imageIndexes?: number[];
title?: string;
}PUBLISH_RESULT{
requestId: string;
result?: {
imageIds: number[];
};
error?: string;
}SAVE_IMAGEblock → hostrequest → replypage-only{
requestId: string;
/** Own-output URL — origin-allowlisted host-side. Mutually exclusive with `imageId`. */
url?: string;
/** Cross-user image id — routed through the gated per-viewer read. Mutually exclusive with `url`. */
imageId?: number;
/** Optional download filename (host-sanitized). */
filename?: string;
}SAVE_IMAGE_RESULT{
requestId: string;
ok: boolean;
error?: string;
}THEME_CHANGEhost → block{
theme: Theme;
}Payloads reference SDK types
Some payload fields are typed as named SDK interfaces (for example WorkflowBody, BlockViewer, AppWorkflow). Those come from @civitai/app-sdk/blocks — import the package to get the full type definitions in your editor.