# papergarden > Make real printed hardcover picture books (8.5in square) by talking to an > AI agent. Agents build the book with a few operations over MCP or REST. ## Connect - **MCP** (Streamable HTTP, no sign-in): `https://dev.papergarden.ai/mcp` - Codex: `codex mcp add papergarden --url https://dev.papergarden.ai/mcp` - Claude Code: `claude mcp add --transport http papergarden https://dev.papergarden.ai/mcp` - **REST**: `https://dev.papergarden.ai/api/v1`, OpenAPI at `https://dev.papergarden.ai/api/v1/openapi.json`. JSON in, JSON out. Errors: HTTP status + `{"error": {"code", "message", "fix", "path", "retryable"}}`. `version_conflict` is 409, `invalid_input` is 422. Operations (identical over MCP and REST): - `get_guide`: GET /api/v1/guide. How to make a papergarden book: workflow, page geometry (630x630pt, safe area 45..585), images, text and taste rules. Read topic "all" once before making a book. - `create_book`: POST /api/v1/books. Start a new picture book. Returns its bookId (also the edit key: keep it, don't show it in public links) and viewUrl (a read-only page to share). New books are anonymous. - `show_book`: GET /api/v1/books/{bookId}/show. Open the book in the chat as an interactive widget (pages, print status, Upload and Order buttons). Call it once after create_book so the user can watch; it refreshes itself when you edit. Other clients get the view link. - `get_book`: GET /api/v1/books/{bookId}. The whole book (or some pages) as JSON: version, surfaces with layers, assets, print issues, printReady and the view link. Call it before editing if you don't know the current version. - `view_book`: GET /api/v1/shared/{shareToken}. Read-only: the book behind a view link (/b/), with every surface's layers, print issues and printReady. Needs only the shareToken, never the bookId. No notes, no editing. - `edit_book`: POST /api/v1/books/{bookId}/edits. Apply a batch of ops to one book: pages, layers, text, colours, notes, restore (undo). All ops apply or none; one new version per call. Send baseVersion = the version you last saw. Returns print issues for the surfaces you touched. Lay out 2-4 spreads per call. - `add_image`: POST /api/v1/books/{bookId}/assets. Turn a picture into a book asset (does not place it; place it with edit_book add_layer). Give exactly one source: url, uploadId (from create_upload_url), base64, or image (ChatGPT file). Prefer transparent PNG cut-outs. Returns size, transparency, max sharp print width and a preview. - `create_upload_url`: POST /api/v1/books/{bookId}/uploads. For agents with a shell or HTTP client (Codex, Claude Code): get one-time URLs to PUT local image files to. PUT with ?label=... and each file becomes a book asset right away (no add_image call needed). Ask for all the links you need at once with count. - `render`: POST /api/v1/books/{bookId}/renders. A picture (JPEG) of pages, a spread or the whole book as a contact sheet, exactly as it will print. Use it to judge your layout like an art director. guides: true draws the trim (red) and safe area (green). - `create_order`: POST /api/v1/orders. Start an order for the book as it is at baseVersion (frozen: later edits don't change it). Runs the print check first. Give email, shippingAddress and shippingLevel now or later with update_order. When nothing is missing, the order has a checkoutUrl: give it to the user to pay (only a person pays). €39 per copy + shipping. - `update_order`: PATCH /api/v1/orders/{orderId}. Set or change quantity, email, shippingAddress (returns live shippingOptions with prices) or shippingLevel on an unpaid order. When nothing is missing, the order gets a checkoutUrl for the user to pay. Same input twice gives the same order. - `get_orders`: GET /api/v1/orders. One order (orderId) or all orders of a book (bookId): status, price, payment link, tracking, problems. Read-only. - `list_books`: GET /api/v1/books. The signed-in user's books across chats (needs the papergarden account connected). Anonymous books appear here once the user claims them. REST example: ```sh curl -s -X POST https://dev.papergarden.ai/api/v1/books -H 'content-type: application/json' -d '{"title":"Mila and the Rain"}' curl -s -X POST https://dev.papergarden.ai/api/v1/books//uploads -H 'content-type: application/json' -d '{"count":2}' # → uploads[].uploadUrl curl -s -X PUT --data-binary @mila.png -H 'content-type: image/png' "?label=Mila" # → the new asset curl -s -X POST https://dev.papergarden.ai/api/v1/books//edits -H 'content-type: application/json' -d '{"baseVersion":1,"ops":[...]}' ``` ## Docs - [Skill / full guide](https://dev.papergarden.ai/skill.md) - [OpenAPI](https://dev.papergarden.ai/api/v1/openapi.json) # papergarden: making a picture book papergarden prints real hardcover picture books (8.5in square, 24-84 pages). You build the book out of layers, like a designer in Figma: a paper colour, a few illustrated elements (characters, props, scenery pieces) and text. The server checks print rules on every write and tells you what to fix. ## Workflow Operations (same names over MCP and REST): `get_guide`, `create_book`, `show_book`, `get_book`, `view_book`, `edit_book`, `add_image`, `create_upload_url`, `render`, `create_order`, `update_order`, `get_orders`, `list_books`. 1. **Plan with the user**: who it is for, what happens, the ending, names, the look. Or write the whole story yourself if they want that. 2. **Write the manuscript and a storyboard** first: about 12 spreads, short read-aloud text (often 1 to 3 sentences per page). For every spread, note one distinct scene: who, where, what happens, and the composition (close-up, small figure in big space, full-bleed picture, text-only page...). Vary it. No two spreads should look the same. 3. `create_book` with the title, page count (even, 24-84, default 24) and `notes` = the plan: manuscript, storyboard, palette (3-5 hex colours), one style sentence, the characters (look, clothes, colours), font choice. Keep the `bookId`: it is the edit key. Call `show_book` once so the user can watch the book in the chat (it refreshes itself), or give them the `viewUrl`. 4. **Make the pictures** with your image tool (see Images). Make a character reference first and reuse it in every later picture so characters look the same on every page. **Plan size and resolution together**: decide how big each picture will be on the page, then make it big enough. Width in pt × 4.2 = the pixels you need for sharp print (e.g. 510pt wide needs ~2100px; a 1254px picture is only sharp up to ~375pt). `add_image` tells you `maxWidthPt` for each picture: never place it wider. 5. **Lay out** with `edit_book`: 2-4 spreads per call. Every call sends `baseVersion` (the version you last saw) and a list of `ops`. All ops apply or none. Use `ref` on `add_layer` to style a new layer later in the same call. 6. Read `issues` in every `edit_book` result and fix every error. **Look at your work** with `render`: each spread after laying it out (`{ "spread": 4 }`). Judge it like an art director and fix what's off. 7. **Finish the whole book.** You are done only when ALL of this is true: - `readiness.printReady` (no blocking errors) **and** `readiness.complete` (no blank pages, a title on the cover); - `readiness.warnings` is 0, or each warning is a deliberate choice; - you rendered `{ "contactSheet": true }` and looked at every spread: distinct scenes, consistent characters, nothing cut off or crowded. `printReady: true` alone does NOT mean finished: a blank book is "printable". If you were interrupted (an approval prompt, an error), resume the original plan: all pages, all pictures. Never report success after a few successful tool calls. Then give the user the view link, and offer to order the printed book (see Ordering). 8. **Accounts are optional.** Books work without one; the bookId is the key. If the user connected their papergarden account, `list_books` finds their books from other chats. `get_book` gives a `links.claim` link for unclaimed books: the user opens it to keep the book in their account. 9. Only have a view link (`/b/`), no bookId? `view_book` reads that book (layers, issues, printReady). It cannot edit. ### edit_book ops | op | fields | |---|---| | `set_book` | `title?`, `notes?`, `background?` (book paper colour) | | `add_pages` | `count`, `after?` (0 = start, default end) | | `delete_pages` | `pages` | | `move_page` | `from`, `to` | | `duplicate_page` | `page` | | `set_surface` | `surface`, `background?` (hex = override, null = follow book), `note?` | | `add_layer` | `surface`, `layer`, `ref?`, `index?` | | `update_layer` | `layerId` (or ref), `set` | | `delete_layer` | `layerId` | | `move_layer` | `layerId`, `to` ("front", "back" or index), `surface?` | | `copy_layer` | `layerId`, `surface`, `x?`, `y?`, `ref?` | | `restore` | `version` (undo: the book goes back to that version as a new version) | - `surface` is `"cover"` or a 1-based page number. - Page numbers refer to the book as it is after the previous op. - Every version is kept. Nothing is lost; `restore` brings anything back. - `version_conflict` means the book changed since your `baseVersion` (another chat, the user). Call `get_book`, then redo the edit. Example call: ```json { "bookId": "bk_…", "baseVersion": 3, "ops": [ { "op": "add_layer", "surface": 2, "ref": "mila", "layer": { "type": "image", "assetId": "as_…", "x": 150, "y": 160, "width": 330 } }, { "op": "add_layer", "surface": 2, "layer": { "type": "text", "text": "Mila had a red umbrella.", "x": 60, "y": 470, "width": 510, "size": 24, "align": "center" } }, { "op": "update_layer", "layerId": "mila", "set": { "flipX": true } } ] } ``` ## Geometry (points, 72pt = 1in, origin top-left) - Interior page: **630 x 630**. The outer 9pt is bleed and gets trimmed. - **Safe area: x 45..585, y 45..585.** Everything important and ALL text must sit inside it. The validator rejects anything else. - A layer may run off the page only with `bleed: true`, and then each edge must either go past the sheet edge (<= 0 or >= 630) or stay inside the safe area. Nothing may END in the margin. Use it for ground strips, a full-page wash or picture, a character peeking in from the edge. - **Cover**: one 1368 x 738 sheet = back | spine | front. Front safe area x 738..1278, y 90..648. Back safe area x 90..630, y 90..648. The spine (x 675..693) and the outer 45pt fold-over are keep-out zones. Put the title on the front. A full-cover background picture is a bleed layer at x 0, y 0, 1368 x 738. - Page 1 is a right-hand page. Then the reader sees spreads 2+3, 4+5, ... Design spreads as pairs, but keep faces and text away from the centre fold. - Text always renders above images and shapes. - **Paper colour**: one book colour (`set_book.background`) that every page and the cover follow. Override a page only on purpose (a night page) with `set_surface`. Default is warm off-white #FAF7F0. ## Images All art comes from an image model; papergarden does not draw or generate. - **Cut-outs are the default style**: characters and props on the book's plain paper colour, no painted background. Ask your image tool for a transparent PNG: "Mila, a small girl in a yellow raincoat, jumping, full body, side view, isolated on a transparent background, no ground, no shadow, no text." If `add_image` returns a `note` about an opaque background, ask again for a transparent PNG, or use it as a full-bleed picture. - **Never put text inside images.** Text is always a text layer. - Use the same style sentence and the same reference images for every picture so the characters stay consistent. - Never send the user's own photos of children to `add_image`. Use them only as references for the image model; send the finished art. How to hand an image to `add_image` (exactly one source): - `url`: any public http(s) image URL. - **Local files (Codex, Claude Code, any shell)**: call `create_upload_url` once with `count` = the number of pictures, then PUT each file with a label: `curl -sS -X PUT --data-binary @mila.png -H "Content-Type: image/png" "?label=Mila%20jumping"`. The response is the new asset (same as `add_image`), so no extra call is needed. Upload all files in one shell command. Fastest way. - `uploadId`: if you PUT without `?label`, call `add_image` with the uploadId. - `base64`: the file bytes as base64 (small files only; it costs context). - `image`: a ChatGPT file reference (`download_url`, `file_id`). `add_image` trims transparent margins (`trim: false` keeps them) and returns `width`, `height` (pixels), `hasAlpha`, `maxWidthPt` (widest sharp placement) and a small preview. Placing: an image layer takes `x`, `y`, `width`; the height follows the picture. Print resolution must stay above 150 ppi (240+ is sharp): a 1536px picture prints sharp up to about 460pt wide. Use `crop` (fractions 0..1) to show part of an image, `flipX` to face a character the other way, and `mask` "ellipse" or "rounded" for framed pictures. ## Text Text layer fields: `text`, `x`, `y`, `width`, `height?`, `font?`, `size?`, `weight?`, `color?`, `align?` (left/center/right), `valign?` (top/middle/bottom), `lineHeight?`, `letterSpacing?`. - Leave out `height` and the frame fits the text exactly (`fit: true`). The result's `adjustments` tell you the height it got. - Markup: `**bold**` and `[word]{#C0392B}` for a coloured word. `\n` breaks a line. - Fonts: `andika` (default, very legible early-reader sans), `nunito` (soft rounded), `quicksand` (light rounded), `literata` (warm serif), `fraunces` (characterful serif, great for titles, weights 300-800), `fredoka` (chunky rounded display, 300-700), `patrick-hand` (handwriting). - Story text 18-26pt. Titles 48-90pt. Keep lines under ~45 characters. - Errors: `text_overflow` (frame too short: drop `height` or enlarge it), `text_too_wide` (one word wider than the frame: widen or shrink). ## Taste: what makes it not look like AI slop - **Restraint.** One focal subject per page. Lots of calm empty space. Most pages have 1 to 3 illustrated elements, not a full scene. - **Flat calm backgrounds.** The paper colour does most of the work. A coloured shape (ellipse, rounded rect) behind a character, or a bleed ground strip, is often all the scenery a page needs. - **One consistent style**, prompted the same way every time. Prefer simple, graphic, limited-palette styles (gouache, flat colour, crayon, risograph, cut paper) over glossy 3D or hyper-detail. - **Consistent scale.** A character stays about the same size from page to page unless the story calls for a close-up or a far view. - **Vary the rhythm** across spreads: a big close-up, then a small figure on a big empty page, then a full-colour page with just text. - **Typography.** One story font, optionally one display font for the title. Left or centred. Dark text on light paper. Put text where the picture leaves room, never over a busy area. - **Look critically** at the viewer as an art director would. If it looks busy, cluttered or generic, remove things. ## Ordering A printed hardcover costs **€39 per copy + shipping** (VAT included). Only a person pays; you do everything else. 1. `create_order` with `bookId` and `baseVersion` (the version the user approved). It runs the print check: `ready: false` + `issues` means fix those first. Later edits don't change an order. 2. Ask the user, one thing at a time, and pass it with `update_order`: their **email**, the **shipping address** (name, line1, city, postcode, country ISO-2, phone; the recipient can be someone else, e.g. a gift), then the **shipping speed** from `shippingOptions` (each with price and delivery estimate). `quantity` for more copies. 3. When `missing` is empty the order has `checkoutUrl`. Show the total (`price.display`) and give the link. Stop there: the user pays on Stripe. 4. `get_orders` shows status (paid → printing → shipped → delivered), tracking and any `problem`. To order another copy, `create_order` again. Address errors come back per field (`path: "shippingAddress.postcode"`) with a `fix`: ask the user for exactly that.