{"openapi":"3.1.0","info":{"title":"FamilyLetter postcard API for AI agents","version":"1.0.0","description":"Build a real printed 4x6 photo postcard for your user, get it paid for through a pay link, then address and mail it. US addresses only, postage included. Also available as a remote MCP server at /api/mcp. Read /llms.txt first."},"servers":[{"url":"https://www.familyletter.io/api/agent/v1"}],"components":{"securitySchemes":{"orderKey":{"type":"http","scheme":"bearer","description":"The order_key from POST /postcards."},"editKey":{"type":"http","scheme":"bearer","description":"The edit_key from POST /postcards (or the latest PATCH)."}},"schemas":{"Design":{"type":"object","properties":{"photos":{"type":"array","minItems":0,"maxItems":4,"items":{"description":"A photo for the front of the card: a public https URL to the image file, or base64 image data (a data: URI or raw base64). JPEG, PNG, WebP or HEIC, at least 600x400 pixels, up to 15 MB.","anyOf":[{"type":"string"},{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]},{"type":"object","properties":{"base64":{"type":"string"}},"required":["base64"]}]},"description":"1-4 photos, laid out as a collage on the front. Leave out (one design only) when you can't pass the user's photos to a tool: you get back a prefill_url they add photos on."},"message":{"type":"string","maxLength":420,"description":"The message printed on the back in the chosen font, up to 420 characters. Optional."},"font":{"type":"string","enum":["mansalva","playwrite","gloria","indie","dancing","rocksalt","giveyouglory","craftygirls","sueellen","justme","gochi","mynerve","playful","girlnextdoor","waitingsunrise","zeyada","quick","neat","patrick","homemade","sacramento","clean","classic","typewriter"],"default":"indie","description":"The font for the message. Preset fonts, not the user's handwriting."},"quantity":{"type":"integer","minimum":1,"maximum":10,"default":1,"description":"How many copies of this design, one per recipient."},"stickers":{"type":"array","maxItems":8,"items":{"type":"string"},"description":"Sticker ids for the message side, from list_decor. Placed around the edges."},"front_stickers":{"type":"array","maxItems":8,"items":{"type":"string"},"description":"Sticker ids for the photo side, from list_decor."},"background":{"type":["string","null"],"description":"A background id for the message side, from list_decor. null for the plain cream back."}}}}},"paths":{"/pricing":{"get":{"operationId":"get_pricing","summary":"FamilyLetter postcard prices","description":"Prices for FamilyLetter's printed 4x6 photo postcards, mailed to US addresses with postage included, plus the fonts and order limits.","responses":{"200":{"description":"Prices, fonts and limits","content":{"application/json":{"schema":{"type":"object"}}}},"default":{"description":"Refused. `error` is a stable code; `hint` says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"hint":{"type":"string"}}}}}}}}},"/decor":{"get":{"operationId":"list_decor","summary":"Postcard fonts, stickers and backgrounds","description":"The fonts, sticker packs (upcoming holidays first) and back backgrounds a postcard can use, with the ids that create_postcard and update_postcard accept.","responses":{"200":{"description":"Fonts, stickers and backgrounds","content":{"application/json":{"schema":{"type":"object"}}}},"default":{"description":"Refused. `error` is a stable code; `hint` says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"hint":{"type":"string"}}}}}}}}},"/postcards/checkout":{"post":{"operationId":"get_payment_link","summary":"Get the payment link","description":"Returns a Stripe-hosted checkout page (payment_url) for the postcard, valid about 2 hours. The user pays there themselves; this tool charges nothing and moves no money.","security":[{"editKey":[]}],"responses":{"200":{"description":"payment_url: Stripe's secure checkout, valid about 2 hours","content":{"application/json":{"schema":{"type":"object"}}}},"default":{"description":"Refused. `error` is a stable code; `hint` says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"hint":{"type":"string"}}}}}}}}},"/postcards":{"post":{"operationId":"create_postcard","summary":"Build a postcard","description":"Builds a printed 4x6 photo postcard from a message, a font, optional stickers and background, a quantity, and 1-4 photos per design (photos the user attached arrive as photo_files; https image links and base64 are also accepted). Returns front and back preview images, the price (postage included) and an edit_key for changes. Without photos it returns status needs_photos: the card is shown with an Add photos button, and prefill_url opens the same card on familyletter.io. Up to 10 designs and 10 cards per order. Nothing is charged.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"designs":{"type":"array","minItems":1,"maxItems":10,"items":{"type":"object","properties":{"photos":{"type":"array","minItems":0,"maxItems":4,"items":{"description":"A photo for the front of the card: a public https URL to the image file, or base64 image data (a data: URI or raw base64). JPEG, PNG, WebP or HEIC, at least 600x400 pixels, up to 15 MB.","anyOf":[{"type":"string"},{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]},{"type":"object","properties":{"base64":{"type":"string"}},"required":["base64"]}]},"description":"1-4 photos, laid out as a collage on the front. Leave out (one design only) when you can't pass the user's photos to a tool: you get back a prefill_url they add photos on."},"message":{"type":"string","maxLength":420,"description":"The message printed on the back in the chosen font, up to 420 characters. Optional."},"font":{"type":"string","enum":["mansalva","playwrite","gloria","indie","dancing","rocksalt","giveyouglory","craftygirls","sueellen","justme","gochi","mynerve","playful","girlnextdoor","waitingsunrise","zeyada","quick","neat","patrick","homemade","sacramento","clean","classic","typewriter"],"default":"indie","description":"The font for the message. Preset fonts, not the user's handwriting."},"quantity":{"type":"integer","minimum":1,"maximum":10,"default":1,"description":"How many copies of this design, one per recipient."},"stickers":{"type":"array","maxItems":8,"items":{"type":"string"},"description":"Sticker ids for the message side, from list_decor. Placed around the edges."},"front_stickers":{"type":"array","maxItems":8,"items":{"type":"string"},"description":"Sticker ids for the photo side, from list_decor."},"background":{"type":["string","null"],"description":"A background id for the message side, from list_decor. null for the plain cream back."}}}},"photo_files":{"type":"array","maxItems":4,"items":{"type":"object","properties":{"download_url":{"type":"string"},"file_id":{"type":"string"},"mime_type":{"type":"string"},"file_name":{"type":"string"}},"required":["download_url","file_id"]},"description":"Photos the user attached in this conversation, used for the first design."}},"required":["designs"]}}}},"responses":{"201":{"description":"order_key, edit_key, view_url, edit_url, price and previews (front_image_url and back_image_url per design)","content":{"application/json":{"schema":{"type":"object"}}}},"default":{"description":"Refused. `error` is a stable code; `hint` says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"hint":{"type":"string"}}}}}}}},"patch":{"operationId":"update_postcard","summary":"Change the postcard","description":"Changes a postcard before it is paid for: the message, font, quantity, photos, stickers or background of any design, or adds or removes designs. Fields left out stay as they are; stickers: [] removes the stickers. Returns fresh front and back previews and the price.","security":[{"editKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"changes":{"type":"array","items":{"type":"object","properties":{"design":{"type":"integer","minimum":1,"default":1,"description":"Which design to change, 1 for the first."},"message":{"type":"string","maxLength":420},"font":{"type":"string","enum":["mansalva","playwrite","gloria","indie","dancing","rocksalt","giveyouglory","craftygirls","sueellen","justme","gochi","mynerve","playful","girlnextdoor","waitingsunrise","zeyada","quick","neat","patrick","homemade","sacramento","clean","classic","typewriter"]},"quantity":{"type":"integer","minimum":1,"maximum":10},"photos":{"type":"array","minItems":1,"maxItems":4,"items":{"description":"A photo for the front of the card: a public https URL to the image file, or base64 image data (a data: URI or raw base64). JPEG, PNG, WebP or HEIC, at least 600x400 pixels, up to 15 MB.","anyOf":[{"type":"string"},{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]},{"type":"object","properties":{"base64":{"type":"string"}},"required":["base64"]}]},"description":"Replaces this design's photos."},"stickers":{"type":"array","maxItems":8,"items":{"type":"string"},"description":"Sticker ids for the message side, from list_decor. Placed around the edges."},"front_stickers":{"type":"array","maxItems":8,"items":{"type":"string"},"description":"Sticker ids for the photo side, from list_decor."},"background":{"type":["string","null"],"description":"A background id for the message side, from list_decor. null for the plain cream back."}}}},"add_designs":{"type":"array","items":{"type":"object","properties":{"photos":{"type":"array","minItems":0,"maxItems":4,"items":{"description":"A photo for the front of the card: a public https URL to the image file, or base64 image data (a data: URI or raw base64). JPEG, PNG, WebP or HEIC, at least 600x400 pixels, up to 15 MB.","anyOf":[{"type":"string"},{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]},{"type":"object","properties":{"base64":{"type":"string"}},"required":["base64"]}]},"description":"1-4 photos, laid out as a collage on the front. Leave out (one design only) when you can't pass the user's photos to a tool: you get back a prefill_url they add photos on."},"message":{"type":"string","maxLength":420,"description":"The message printed on the back in the chosen font, up to 420 characters. Optional."},"font":{"type":"string","enum":["mansalva","playwrite","gloria","indie","dancing","rocksalt","giveyouglory","craftygirls","sueellen","justme","gochi","mynerve","playful","girlnextdoor","waitingsunrise","zeyada","quick","neat","patrick","homemade","sacramento","clean","classic","typewriter"],"default":"indie","description":"The font for the message. Preset fonts, not the user's handwriting."},"quantity":{"type":"integer","minimum":1,"maximum":10,"default":1,"description":"How many copies of this design, one per recipient."},"stickers":{"type":"array","maxItems":8,"items":{"type":"string"},"description":"Sticker ids for the message side, from list_decor. Placed around the edges."},"front_stickers":{"type":"array","maxItems":8,"items":{"type":"string"},"description":"Sticker ids for the photo side, from list_decor."},"background":{"type":["string","null"],"description":"A background id for the message side, from list_decor. null for the plain cream back."}}},"description":"New designs to add to the order."},"remove_designs":{"type":"array","items":{"type":"integer","minimum":1},"description":"Design numbers to remove."}},"required":[]}}}},"responses":{"200":{"description":"A fresh preview, price and links","content":{"application/json":{"schema":{"type":"object"}}}},"default":{"description":"Refused. `error` is a stable code; `hint` says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"hint":{"type":"string"}}}}}}}}},"/orders":{"get":{"operationId":"get_order","summary":"Check a postcard order","description":"Shows whether a postcard order is paid and each card's status (needs_address, sending, sent_to_printer, mailed, delivered), with each card_id.","security":[{"orderKey":[]}],"responses":{"200":{"description":"Order and card status","content":{"application/json":{"schema":{"type":"object"}}}},"default":{"description":"Refused. `error` is a stable code; `hint` says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"hint":{"type":"string"}}}}}}}}},"/orders/cards/{cardId}/verify-address":{"post":{"operationId":"verify_address","summary":"Check a recipient's address","description":"Checks a US mailing address for one paid card with USPS and returns the standardized address it would be mailed to. Mails nothing.","security":[{"orderKey":[]}],"parameters":[{"name":"cardId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"recipient_name":{"type":"string","maxLength":40,"description":"Who the card is to, as it should print, e.g. \"Grandma Rose\"."},"address":{"type":"object","properties":{"line1":{"type":"string"},"line2":{"type":"string","description":"Apartment, suite or unit."},"city":{"type":"string"},"state":{"type":"string","description":"Two-letter US state."},"postal_code":{"type":"string"}},"required":["line1","city","state","postal_code"]}},"required":["recipient_name","address"]}}}},"responses":{"200":{"description":"verification_id and the standardized mail_to address","content":{"application/json":{"schema":{"type":"object"}}}},"default":{"description":"Refused. `error` is a stable code; `hint` says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"hint":{"type":"string"}}}}}}}}},"/orders/cards/{cardId}/mail":{"post":{"operationId":"mail_card","summary":"Print and mail a card","description":"Prints and mails one paid card to an address returned by verify_address. Irreversible: the card goes to the printer.","security":[{"orderKey":[]}],"parameters":[{"name":"cardId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"verification_id":{"type":"string","description":"From verify_address."},"recipient_name":{"type":"string","maxLength":40}},"required":["verification_id","recipient_name"]}}}},"responses":{"200":{"description":"The card is on its way","content":{"application/json":{"schema":{"type":"object"}}}},"default":{"description":"Refused. `error` is a stable code; `hint` says what to do.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"hint":{"type":"string"}}}}}}}}}}}