Save to Playbook
Get your renders out of ComfyUI/output and onto a board
- images
- asset_tokens
Most renders die in ComfyUI/output/. SaveImage writes a PNG with the whole graph welded into its text chunks, which is great until you want someone else to look at it - then you're dragging files into Slack, or uploading to a host that recompresses the metadata away. Save to Playbook is the other ending: hand it an IMAGE batch and the frames land on a Playbook board, tagged, statused, marked AI-generated, with a record of how they were made attached to each one.
The reason to reach for it isn't storage, it's the review loop. A board is something a client or teammate can scroll and approve, and the companion Load from Playbook node can pull back only the assets marked Approved. Generate, get sign-off, pull the approved ones back in for an upscale or inpaint pass - that round trip is the whole pitch. No GPU involved: it's an HTTP client in the credential-holding family (external-api-nodes.md), calling a file board rather than a model.
How it works
Three legs per image: the node asks Playbook to prepare an upload, gets a signed URL on Google Cloud or Backblaze, PUTs the bytes straight there, then completes the asset record. Your key never goes to the storage provider - the signed URL is the credential on that leg. Each frame is its own prepare/transfer/complete, so one bad image doesn't sink the batch; failures come back named, per file.
Every asset is flagged ai_generated and gets a provenance payload: the node count, up to five model filenames (scraped from ckpt_name / unet_name / lora_name / model_name / vae_name / control_net_name anywhere in the graph), and the entire graph when it fits Playbook's 4 KB limit. It measures that payload the way the server does, so a graph full of <lora:...> tags can't pass a local size check and then get rejected after the bytes are already in storage. Anything whose input name looks like a key, token, secret, password or auth is redacted before upload, along with any value matching a known secret.
What you actually set
- images - the batch.
- title - doubles as the filename. One image is
Title.png; a batch isTitle 01.png,Title 02.png. Blank it and you'd get.png, which the server reads as an untyped binary with no preview, so it falls back toComfyUI render. - board - a token filled by the Pick board button, not typed by hand.
- new_board_name - create a board by name, or reuse the one that already has it, nested under
boardwhen that's set.
Then tags (comma-separated) and description.
status is the trap. It matches by name, and a name that doesn't exist gets created in your workspace. Aproved doesn't fail - it quietly adds a second status to the workspace dropdown, and your load-side filter for Approved then matches nothing. Type it carefully once.
api_key and workspace are input-only, for headless or wired-in setups, and workspace is required alongside a wired key. The output is one asset_tokens string, comma-separated in batch order.
Install
ComfyUI Manager, search Playbook (registry package playbook-creative), or:
cd ComfyUI/custom_nodes
git clone https://github.com/playbook-labs/playbook-comfyui
The only dependency is requests. No models, nothing to drop in models/. Restart ComfyUI, press Connect Playbook on the node, paste a token from Developer → SDK in the Playbook app. Headless setups use the environment instead:
PLAYBOOK_API_KEY=pb_...
PLAYBOOK_WORKSPACE=your-workspace-slug
The key is deliberately not a widget. Widget values get serialised into workflows and into the PNG text chunks of everything ComfyUI saves, so a key typed onto a node would travel out with every render you share (image-io-metadata.md). It lives in user/playbook/credentials.json, mode 0600, outside the pack folder so updates don't wipe it.
Where people get burned
The 5 MB wall. Playbook's multipart upload isn't implemented, and the node says so plainly: anything over roughly 5 MB returns "Playbook returned a multipart upload, which this node does not implement." A 4K upscale clears that easily - downscale, or upload that one through the app.
It runs on every queue. As an output node it always executes - that's what makes the cache work backward from it (comfyui-node-plumbing.md) - and there's no dedupe, so forty queue presses is forty sets of uploads. Mute it while iterating.
Expiring tokens and quotas. Without full API access on your plan, tokens die after 30 days and you get a blunt 401 telling you to make a new one; a 429 is the workspace's API quota, not a broken node.
Multi-user servers. A saved key would be shared by every profile, so the node refuses to store one under --multi-user - set PLAYBOOK_API_KEY instead. Same for the board picker: it talks to the machine running ComfyUI, so connecting over a tunnel is refused by design.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| title | STRING | ComfyUI render | — |
| boardopt | STRING | Board token. Use 'Pick board' to fill it. | |
| new_board_nameopt | STRING | Create a board with this name, or reuse the one that already has it. Nested under 'board' when that is set. | |
| tagsopt | STRING | Comma separated. | |
| statusopt | STRING | Asset status name, e.g. Approved. Matched by name, and a name that does not exist is CREATED in the workspace -- so a typo here adds a status, it does not fail. | |
| descriptionopt | STRING | — | |
| api_keyopt | STRING | — | |
| workspaceopt | STRING | Workspace slug. Required alongside api_key. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| asset_tokens | STRING | — |