{"openapi":"3.1.0","info":{"title":"MakeSweet","version":"1","description":"Put photos and words onto a design and render it. A render is built up rather than posted in one go: open one, layer its surfaces, adjust the placement, render it, look, render it again. Keep what you make and it becomes a composition, which is still there tomorrow.\n\nMake an API key at https://deluxe.makesweet.com/account/connections and send it as `Authorization: Bearer <key>`.\n\nThe same operations are available to agents over MCP at https://deluxe.makesweet.com/mcp."},"servers":[{"url":"https://deluxe.makesweet.com/api"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"An API key"}},"responses":{"NoKey":{"description":"No API key, or not one of ours","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"What went wrong, in a sentence"},"reason":{"type":"string","description":"Short code for the refusal, for branching on","enum":["no-such-design","members-only","members-only-size","no-large-version","no-animation","quota","storage","kept","refused","no-key","bad-request","server"]}}}}}},"Membership":{"description":"Needs a Deluxe membership","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"What went wrong, in a sentence"},"reason":{"type":"string","description":"Short code for the refusal, for branching on","enum":["no-such-design","members-only","members-only-size","no-large-version","no-animation","quota","storage","kept","refused","no-key","bad-request","server"]}}}}}},"Storage":{"description":"Your stored renders are at their limit","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"What went wrong, in a sentence"},"reason":{"type":"string","description":"Short code for the refusal, for branching on","enum":["no-such-design","members-only","members-only-size","no-large-version","no-animation","quota","storage","kept","refused","no-key","bad-request","server"]}}}}}},"Quota":{"description":"Today's render allowance is used","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"What went wrong, in a sentence"},"reason":{"type":"string","description":"Short code for the refusal, for branching on","enum":["no-such-design","members-only","members-only-size","no-large-version","no-animation","quota","storage","kept","refused","no-key","bad-request","server"]}}}}}}}},"paths":{"/designs":{"get":{"operationId":"list_designs","tags":["Designs"],"summary":"List designs","description":"List the MakeSweet designs this account can render. A free account gets ten, chosen so that no two are alike; a Deluxe membership opens the whole catalog. Most designs are stills: check `animates` before promising somebody a moving one. start_render reports how many surfaces the design you pick has.","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"designs":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string","description":"What to pass as `design`"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"What the site calls it"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"animates":{"type":"boolean","description":"Whether this design can be rendered as an animation"}},"required":["slug","name","description","animates"]}},"more_with_membership":{"description":"How many further designs a Deluxe membership opens","type":"integer"}},"required":["designs"]}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/renders":{"post":{"operationId":"start_render","tags":["Renders"],"summary":"Start a render","description":"Begin a composition on a MakeSweet design. Costs nothing and renders nothing: it tells you how many surfaces the design has and gives you a URL to send photos to. Layer the surfaces with set_surface and photo uploads, then call render.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"design":{"type":"string","description":"Design slug, e.g. 'heart-locket'"},"size":{"description":"400x300 (default), or 800x600 with a membership","type":"string","enum":["400","800"]}},"required":["design"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","rendering","done","error"]},"render":{"description":"Handle for this render","type":"string"},"design":{"type":"string"},"surfaces":{"type":"array","items":{"type":"object","properties":{"surface":{"type":"integer"},"holds":{"type":"string"},"photo":{"description":"Handle of the photo on it, for use_photo","type":"string"},"text":{"type":"string"},"font":{"type":"string"},"color":{"type":"string"},"outline":{"type":"string"},"scale":{"type":"number"},"angle":{"type":"number"},"layers":{"description":"How many layers are on this surface, when more than one","type":"integer"}},"required":["surface","holds"]}},"upload_url":{"description":"POST a photo here; a person can also open it and pick a file","type":"string"},"url":{"description":"The finished render, when done","type":"string"},"mime_type":{"type":"string"},"renders_left_today":{"type":"number"},"stills_left_today":{"type":"integer"},"poll_after_seconds":{"type":"integer"},"error":{"type":"string"},"reason":{"description":"Short code for a refusal","type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}},"delete":{"operationId":"forget_renders","tags":["Renders"],"summary":"Delete stored renders","description":"Delete finished renders you are holding, freeing storage. Their URLs stop working immediately, so do not do this to anything you have passed on. Called with no arguments it deletes all of them; with older_than_days it deletes only renders finished before then.","parameters":[{"name":"older_than_days","in":"query","required":false,"schema":{"description":"Only delete renders finished more than this many days ago","type":"number","exclusiveMinimum":0},"description":"Only delete renders finished more than this many days ago"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"number"},"freed_bytes":{"type":"number"},"stored_bytes":{"type":"number"},"storage_limit_bytes":{"type":"number"}},"required":["deleted","freed_bytes","stored_bytes","storage_limit_bytes"]}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/renders/{render}/surfaces/{surface}":{"patch":{"operationId":"set_surface","tags":["Renders"],"summary":"Set a surface","description":"Put a caption on one of a design's surfaces, or adjust what is already there. Everything is optional: send only what changes. Photos arrive by upload_link or send_photo_bytes.","parameters":[{"name":"render","in":"path","required":true,"schema":{"type":"string"}},{"name":"surface","in":"path","required":true,"schema":{"type":"integer","minimum":1,"description":"Which surface, 1-based"},"description":"Which surface, 1-based"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"description":"Words to put here","type":"string","maxLength":200},"font":{"description":"Font name, e.g. 'Arial', 'Bangers'","type":"string"},"color":{"description":"Text color, e.g. '#000000'","type":"string"},"outline":{"description":"Outline color, e.g. '#ffffff'","type":"string"},"scale":{"description":"Size: 1 as placed, 2 twice as big, 0.5 half","type":"number","exclusiveMinimum":0},"angle":{"description":"Degrees, positive clockwise","type":"number"},"offset":{"description":"Move it, in fractions of the surface: {x: 0.25} is a quarter of the way right of center. Follows the surface, so on a tilted one 'right' tilts with it.","type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"}}}},"required":[]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","rendering","done","error"]},"render":{"description":"Handle for this render","type":"string"},"design":{"type":"string"},"surfaces":{"type":"array","items":{"type":"object","properties":{"surface":{"type":"integer"},"holds":{"type":"string"},"photo":{"description":"Handle of the photo on it, for use_photo","type":"string"},"text":{"type":"string"},"font":{"type":"string"},"color":{"type":"string"},"outline":{"type":"string"},"scale":{"type":"number"},"angle":{"type":"number"},"layers":{"description":"How many layers are on this surface, when more than one","type":"integer"}},"required":["surface","holds"]}},"upload_url":{"description":"POST a photo here; a person can also open it and pick a file","type":"string"},"url":{"description":"The finished render, when done","type":"string"},"mime_type":{"type":"string"},"renders_left_today":{"type":"number"},"stills_left_today":{"type":"integer"},"poll_after_seconds":{"type":"integer"},"error":{"type":"string"},"reason":{"description":"Short code for a refusal","type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/renders/{render}/surfaces/{surface}/upload":{"get":{"operationId":"upload_link","tags":["Renders"],"summary":"Get the upload link","description":"The address photos go to for this render. POST an image to it, or hand it to the person you are working with: it opens in a browser as a page to pick a file, from a phone or wherever else the photo already is.","parameters":[{"name":"render","in":"path","required":true,"schema":{"type":"string"}},{"name":"surface","in":"path","required":true,"schema":{"description":"Which surface the photo lands on; 1 if left out","type":"integer","minimum":1},"description":"Which surface the photo lands on; 1 if left out"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","rendering","done","error"]},"render":{"description":"Handle for this render","type":"string"},"design":{"type":"string"},"surfaces":{"type":"array","items":{"type":"object","properties":{"surface":{"type":"integer"},"holds":{"type":"string"},"photo":{"description":"Handle of the photo on it, for use_photo","type":"string"},"text":{"type":"string"},"font":{"type":"string"},"color":{"type":"string"},"outline":{"type":"string"},"scale":{"type":"number"},"angle":{"type":"number"},"layers":{"description":"How many layers are on this surface, when more than one","type":"integer"}},"required":["surface","holds"]}},"upload_url":{"description":"POST a photo here; a person can also open it and pick a file","type":"string"},"url":{"description":"The finished render, when done","type":"string"},"mime_type":{"type":"string"},"renders_left_today":{"type":"number"},"stills_left_today":{"type":"integer"},"poll_after_seconds":{"type":"integer"},"error":{"type":"string"},"reason":{"description":"Short code for a refusal","type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/renders/{render}/surfaces/{surface}/photo-from-url":{"post":{"operationId":"copy_photo","tags":["Renders"],"summary":"Copy a photo from a URL","description":"Put a photo on a surface by naming somewhere we can fetch it from. The address has to be one the photo already lives at, reachable over http or https.","parameters":[{"name":"render","in":"path","required":true,"schema":{"type":"string"}},{"name":"surface","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"Where the photo is, http or https"}},"required":["url"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","rendering","done","error"]},"render":{"description":"Handle for this render","type":"string"},"design":{"type":"string"},"surfaces":{"type":"array","items":{"type":"object","properties":{"surface":{"type":"integer"},"holds":{"type":"string"},"photo":{"description":"Handle of the photo on it, for use_photo","type":"string"},"text":{"type":"string"},"font":{"type":"string"},"color":{"type":"string"},"outline":{"type":"string"},"scale":{"type":"number"},"angle":{"type":"number"},"layers":{"description":"How many layers are on this surface, when more than one","type":"integer"}},"required":["surface","holds"]}},"upload_url":{"description":"POST a photo here; a person can also open it and pick a file","type":"string"},"url":{"description":"The finished render, when done","type":"string"},"mime_type":{"type":"string"},"renders_left_today":{"type":"number"},"stills_left_today":{"type":"integer"},"poll_after_seconds":{"type":"integer"},"error":{"type":"string"},"reason":{"description":"Short code for a refusal","type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/renders/{render}/surfaces/{surface}/photo":{"put":{"operationId":"send_photo_bytes","tags":["Renders"],"summary":"Send photo bytes","description":"Put a photo on a surface by sending the image itself. Of the three ways to layer a surface with a photo this is the one that needs nothing but this call: copy_photo fetches from a URL you already have, and upload_link hands back an address to upload to - or to give to a person, who can pick a file there.","parameters":[{"name":"render","in":"path","required":true,"schema":{"type":"string"}},{"name":"surface","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"application/octet-stream":{"schema":{"type":"string","format":"binary"}},"application/json":{"schema":{"type":"object","properties":{"image":{"type":"string","description":"The image as base64, or a data: URI. JPEG or PNG. Over HTTP you can post the file itself instead, with its own content-type."}},"required":["image"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","rendering","done","error"]},"render":{"description":"Handle for this render","type":"string"},"design":{"type":"string"},"surfaces":{"type":"array","items":{"type":"object","properties":{"surface":{"type":"integer"},"holds":{"type":"string"},"photo":{"description":"Handle of the photo on it, for use_photo","type":"string"},"text":{"type":"string"},"font":{"type":"string"},"color":{"type":"string"},"outline":{"type":"string"},"scale":{"type":"number"},"angle":{"type":"number"},"layers":{"description":"How many layers are on this surface, when more than one","type":"integer"}},"required":["surface","holds"]}},"upload_url":{"description":"POST a photo here; a person can also open it and pick a file","type":"string"},"url":{"description":"The finished render, when done","type":"string"},"mime_type":{"type":"string"},"renders_left_today":{"type":"number"},"stills_left_today":{"type":"integer"},"poll_after_seconds":{"type":"integer"},"error":{"type":"string"},"reason":{"description":"Short code for a refusal","type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/renders/{render}/surfaces/{surface}/photo-from-library":{"post":{"operationId":"use_photo","tags":["Renders"],"summary":"Use a photo again","description":"Put a photo you have already sent on a surface, by its handle. Every photo you send comes back with one, check_render says which photo a surface is holding, and list_photos has the rest. Nothing is uploaded twice.","parameters":[{"name":"render","in":"path","required":true,"schema":{"type":"string"}},{"name":"surface","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"photo":{"type":"string","description":"A photo handle, as given when it was sent"}},"required":["photo"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","rendering","done","error"]},"render":{"description":"Handle for this render","type":"string"},"design":{"type":"string"},"surfaces":{"type":"array","items":{"type":"object","properties":{"surface":{"type":"integer"},"holds":{"type":"string"},"photo":{"description":"Handle of the photo on it, for use_photo","type":"string"},"text":{"type":"string"},"font":{"type":"string"},"color":{"type":"string"},"outline":{"type":"string"},"scale":{"type":"number"},"angle":{"type":"number"},"layers":{"description":"How many layers are on this surface, when more than one","type":"integer"}},"required":["surface","holds"]}},"upload_url":{"description":"POST a photo here; a person can also open it and pick a file","type":"string"},"url":{"description":"The finished render, when done","type":"string"},"mime_type":{"type":"string"},"renders_left_today":{"type":"number"},"stills_left_today":{"type":"integer"},"poll_after_seconds":{"type":"integer"},"error":{"type":"string"},"reason":{"description":"Short code for a refusal","type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/renders/{render}/render":{"post":{"operationId":"render","tags":["Renders"],"summary":"Render","description":"Render the composition as it stands. A still is one frame, returned as a PNG image, and costs a tenth of the quota. An animation is every frame: as a GIF, or as an mp4 with 'video', which is smaller and is what most places want for posting. The composition survives a render, so it can be rendered again.","parameters":[{"name":"render","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"format":{"description":"'still' for one frame, 'animation' for all of them as a GIF, 'video' for all of them as an mp4","type":"string","enum":["still","animation","video"]}},"required":[]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","rendering","done","error"]},"render":{"description":"Handle for this render","type":"string"},"design":{"type":"string"},"surfaces":{"type":"array","items":{"type":"object","properties":{"surface":{"type":"integer"},"holds":{"type":"string"},"photo":{"description":"Handle of the photo on it, for use_photo","type":"string"},"text":{"type":"string"},"font":{"type":"string"},"color":{"type":"string"},"outline":{"type":"string"},"scale":{"type":"number"},"angle":{"type":"number"},"layers":{"description":"How many layers are on this surface, when more than one","type":"integer"}},"required":["surface","holds"]}},"upload_url":{"description":"POST a photo here; a person can also open it and pick a file","type":"string"},"url":{"description":"The finished render, when done","type":"string"},"mime_type":{"type":"string"},"renders_left_today":{"type":"number"},"stills_left_today":{"type":"integer"},"poll_after_seconds":{"type":"integer"},"error":{"type":"string"},"reason":{"description":"Short code for a refusal","type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/compositions/{composition}/render":{"post":{"operationId":"render_composition","tags":["Compositions"],"summary":"Render a composition","description":"Render a composition off your shelf, as it stands. The composition is not changed and not spent: render it again at another size, or again after the editor has moved something. A still is one frame as a PNG and costs a tenth of the quota, an animation is every frame as a GIF, and 'video' is an mp4.","parameters":[{"name":"composition","in":"path","required":true,"schema":{"type":"string","description":"Handle from list_compositions"},"description":"Handle from list_compositions"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"size":{"description":"400x300 (default), or 800x600 with a membership","type":"string","enum":["400","800"]},"format":{"description":"'still' for one frame, 'animation' for all of them as a GIF, 'video' for all of them as an mp4","type":"string","enum":["still","animation","video"]}},"required":[]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","rendering","done","error"]},"render":{"description":"Handle for this render","type":"string"},"design":{"type":"string"},"surfaces":{"type":"array","items":{"type":"object","properties":{"surface":{"type":"integer"},"holds":{"type":"string"},"photo":{"description":"Handle of the photo on it, for use_photo","type":"string"},"text":{"type":"string"},"font":{"type":"string"},"color":{"type":"string"},"outline":{"type":"string"},"scale":{"type":"number"},"angle":{"type":"number"},"layers":{"description":"How many layers are on this surface, when more than one","type":"integer"}},"required":["surface","holds"]}},"upload_url":{"description":"POST a photo here; a person can also open it and pick a file","type":"string"},"url":{"description":"The finished render, when done","type":"string"},"mime_type":{"type":"string"},"renders_left_today":{"type":"number"},"stills_left_today":{"type":"integer"},"poll_after_seconds":{"type":"integer"},"error":{"type":"string"},"reason":{"description":"Short code for a refusal","type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/renders/{render}/surfaces/{surface}/fit":{"post":{"operationId":"fit_surface","tags":["Renders"],"summary":"Fit a surface","description":"Size and center what is on a surface so it sits inside the shape ('contain') or fills it and is cropped ('cover'). It works out a position once; later adjustments start from there.","parameters":[{"name":"render","in":"path","required":true,"schema":{"type":"string"}},{"name":"surface","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["contain","cover"]}},"required":[]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","rendering","done","error"]},"render":{"description":"Handle for this render","type":"string"},"design":{"type":"string"},"surfaces":{"type":"array","items":{"type":"object","properties":{"surface":{"type":"integer"},"holds":{"type":"string"},"photo":{"description":"Handle of the photo on it, for use_photo","type":"string"},"text":{"type":"string"},"font":{"type":"string"},"color":{"type":"string"},"outline":{"type":"string"},"scale":{"type":"number"},"angle":{"type":"number"},"layers":{"description":"How many layers are on this surface, when more than one","type":"integer"}},"required":["surface","holds"]}},"upload_url":{"description":"POST a photo here; a person can also open it and pick a file","type":"string"},"url":{"description":"The finished render, when done","type":"string"},"mime_type":{"type":"string"},"renders_left_today":{"type":"number"},"stills_left_today":{"type":"integer"},"poll_after_seconds":{"type":"integer"},"error":{"type":"string"},"reason":{"description":"Short code for a refusal","type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/renders/{render}":{"get":{"operationId":"check_render","tags":["Renders"],"summary":"Check a render","description":"What a composition holds, whether a render is still going, and the URL of the last finished one.","parameters":[{"name":"render","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","rendering","done","error"]},"render":{"description":"Handle for this render","type":"string"},"design":{"type":"string"},"surfaces":{"type":"array","items":{"type":"object","properties":{"surface":{"type":"integer"},"holds":{"type":"string"},"photo":{"description":"Handle of the photo on it, for use_photo","type":"string"},"text":{"type":"string"},"font":{"type":"string"},"color":{"type":"string"},"outline":{"type":"string"},"scale":{"type":"number"},"angle":{"type":"number"},"layers":{"description":"How many layers are on this surface, when more than one","type":"integer"}},"required":["surface","holds"]}},"upload_url":{"description":"POST a photo here; a person can also open it and pick a file","type":"string"},"url":{"description":"The finished render, when done","type":"string"},"mime_type":{"type":"string"},"renders_left_today":{"type":"number"},"stills_left_today":{"type":"integer"},"poll_after_seconds":{"type":"integer"},"error":{"type":"string"},"reason":{"description":"Short code for a refusal","type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/photos":{"post":{"operationId":"add_photo","tags":["Photos"],"summary":"Add a photo to your library","description":"Send a photo to your library without putting it on a surface yet: back comes the handle that names it to keep_composition and use_photo. The same picture on twenty compositions is one call here and twenty uses of the handle.","requestBody":{"required":true,"content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"application/octet-stream":{"schema":{"type":"string","format":"binary"}},"application/json":{"schema":{"type":"object","properties":{"image":{"type":"string","description":"The image as base64, or a data: URI. JPEG or PNG. Over HTTP you can post the file itself instead, with its own content-type."},"label":{"description":"What to call it in list_photos","type":"string"}},"required":["image"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"photo":{"description":"The handle, for keep_composition and use_photo","type":"string"},"bytes":{"type":"integer"},"error":{"type":"string"},"reason":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}},"get":{"operationId":"list_photos","tags":["Photos"],"summary":"List your photos","description":"The photos you have sent and can still put on a surface, newest first. Each one has a handle for use_photo. A photo is kept for a fortnight after the last time it was used, and counts against your storage while it is here.","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"photos":{"type":"array","items":{"type":"object","properties":{"photo":{"type":"string","description":"The handle, for use_photo"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"What it was called when it arrived"},"kind":{"type":"string"},"bytes":{"type":"integer"},"added":{"type":"string"},"last_used":{"type":"string"},"kept_until":{"type":"string"}},"required":["photo","label","kind","bytes","added","last_used","kept_until"]}},"stored_bytes":{"type":"number"},"storage_limit_bytes":{"type":"number"}},"required":["photos","stored_bytes","storage_limit_bytes"]}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}},"delete":{"operationId":"forget_photos","tags":["Photos"],"summary":"Delete photos","description":"Delete photos you have sent, freeing storage. A composition still holding one cannot be rendered afterwards, so it says so rather than rendering without it. Called with no arguments it deletes all of them.","parameters":[{"name":"photo","in":"query","required":false,"schema":{"description":"Just this one, by its handle","type":"string"},"description":"Just this one, by its handle"},{"name":"older_than_days","in":"query","required":false,"schema":{"description":"Only those unused for more than this many days","type":"number","exclusiveMinimum":0},"description":"Only those unused for more than this many days"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"number"},"freed_bytes":{"type":"number"},"stored_bytes":{"type":"number"},"storage_limit_bytes":{"type":"number"}},"required":["deleted","freed_bytes","stored_bytes","storage_limit_bytes"]}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/photos/{photo}":{"get":{"operationId":"get_photo","tags":["Photos"],"summary":"Fetch a photo from your library","description":"One of your photos back as a data: URI, named by its handle. The inverse of add_photo, for putting a composition back together somewhere new.","parameters":[{"name":"photo","in":"path","required":true,"schema":{"type":"string","description":"The photo's handle (msp_...)"},"description":"The photo's handle (msp_...)"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"photo":{"type":"string"},"data_uri":{"type":"string"},"bytes":{"type":"integer"},"kind":{"type":"string"},"error":{"type":"string"},"reason":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/compositions":{"post":{"operationId":"keep_composition","tags":["Compositions"],"summary":"Keep a composition","description":"Keep a composition so it is still here tomorrow. One composition per design, updated in place; unpinned ones roll silently (newest in, oldest out) and pin_composition makes one hold its slot. Layers go in stacking order, several may sit on one surface, and photos are named by library handles (msp_..., see list_photos).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"design":{"type":"string","description":"Design slug, e.g. 'heart-locket'"},"layers":{"type":"array","items":{"type":"object","properties":{"surface":{"type":"integer","minimum":1,"description":"Which surface, in the design's own numbering"},"photo":{"description":"A library photo handle (msp_...)","type":"string"},"text":{"type":"string"},"font":{"type":"string"},"color":{"type":"string"},"outline":{"type":"string"},"ghost":{"type":"integer","minimum":0,"maximum":2},"pose":{"description":"The editor's placement numbers, applied verbatim","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"}},"scale":{"type":"number"},"angle":{"type":"number"}},"required":["surface"]},"description":"Layers in stacking order; several may sit on one surface"},"plate":{"description":"Optional: a small JPEG or PNG of the composition as it looks, base64 or a data: URI, shown on the pages that list your compositions. The site's editor sends the canvas it has drawn. Leave it out and the last one stands.","type":"string"},"from":{"description":"The duet code this was opened from. Work begun on somebody else's half is kept in its own row for that duet, never over your own composition for the design.","type":"string","pattern":"^mss_[A-Za-z0-9._~-]{1,200}$"}},"required":["design","layers"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"kept":{"type":"boolean"},"composition":{"description":"Handle for the composition","type":"string"},"design":{"type":"string"},"rolled":{"type":"integer"},"shelf":{"type":"object","properties":{"kept":{"type":"integer","description":"Compositions on the shelf"},"max":{"type":"integer","description":"The shelf's size on this plan"}},"required":["kept","max"]},"error":{"type":"string"},"reason":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}},"get":{"operationId":"list_compositions","tags":["Compositions"],"summary":"List your compositions","description":"Everything on your shelf, newest first: your compositions across all designs, pinned or rolling, with their handles.","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"compositions":{"type":"array","items":{"type":"object","properties":{"composition":{"type":"string"},"design":{"type":"string"},"pinned":{"type":"boolean"},"updated":{"type":"string"},"layers":{"type":"integer"}},"required":["composition","design","pinned","updated","layers"]}},"shelf":{"type":"object","properties":{"kept":{"type":"integer","description":"Compositions on the shelf"},"max":{"type":"integer","description":"The shelf's size on this plan"}},"required":["kept","max"]}},"required":["compositions","shelf"]}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/renders/{render}/keep":{"post":{"operationId":"keep_render","tags":["Renders"],"summary":"Keep a render","description":"Put what you have built on your shelf, so it is still here tomorrow. The render goes on living as a render: this keeps the composition behind it, which is what the site's editor keeps and what render_composition renders. One per design, updated in place, the same slot the editor uses.","parameters":[{"name":"render","in":"path","required":true,"schema":{"type":"string","description":"Handle from start_render"},"description":"Handle from start_render"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"kept":{"type":"boolean"},"composition":{"description":"Handle for the composition","type":"string"},"design":{"type":"string"},"rolled":{"type":"integer"},"shelf":{"type":"object","properties":{"kept":{"type":"integer","description":"Compositions on the shelf"},"max":{"type":"integer","description":"The shelf's size on this plan"}},"required":["kept","max"]},"error":{"type":"string"},"reason":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/compositions/latest/{design}":{"get":{"operationId":"latest_composition","tags":["Compositions"],"summary":"Your latest composition on a design","description":"The newest composition you have kept on a design, with its layers and when it last changed. The freshness is there so the caller can decide between restoring silently and offering.","parameters":[{"name":"design","in":"path","required":true,"schema":{"type":"string","description":"Design slug"},"description":"Design slug"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"design":{"type":"string"},"composition":{"type":"string"},"pinned":{"type":"boolean"},"updated":{"type":"string"},"updated_seconds_ago":{"type":"integer"},"layers":{"type":"array","items":{"type":"object","properties":{"id":{"description":"This layer's own name","type":"integer"},"surface":{"type":"integer"},"holds":{"type":"string"},"photo":{"description":"Handle of the photo, for reuse","type":"string"},"text":{"type":"string"},"font":{"type":"string"},"color":{"type":"string"},"outline":{"type":"string"},"ghost":{"type":"integer"},"pose":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"}},"scale":{"type":"number"},"angle":{"type":"number"}},"required":["surface","holds"]}},"error":{"type":"string"},"reason":{"type":"string"}},"required":["design"]}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/compositions/{composition}":{"delete":{"operationId":"discard_composition","tags":["Compositions"],"summary":"Discard a composition","description":"Delete a composition, pinned or not. The photos it referenced stay in your library and age from their last use.","parameters":[{"name":"composition","in":"path","required":true,"schema":{"type":"string","description":"The composition's handle"},"description":"The composition's handle"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"discarded":{"type":"boolean"},"shelf":{"type":"object","properties":{"kept":{"type":"integer","description":"Compositions on the shelf"},"max":{"type":"integer","description":"The shelf's size on this plan"}},"required":["kept","max"]},"error":{"type":"string"},"reason":{"type":"string"}},"required":["discarded"]}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/compositions/{composition}/pin":{"post":{"operationId":"pin_composition","tags":["Compositions"],"summary":"Pin a composition","description":"Make a composition hold its slot: it will never roll away, and later saves on its design still land in it. Pinning refuses when pins layer the shelf.","parameters":[{"name":"composition","in":"path","required":true,"schema":{"type":"string","description":"The composition's handle"},"description":"The composition's handle"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"pinned":{"type":"boolean"},"composition":{"type":"string"},"shelf":{"type":"object","properties":{"kept":{"type":"integer","description":"Compositions on the shelf"},"max":{"type":"integer","description":"The shelf's size on this plan"}},"required":["kept","max"]},"error":{"type":"string"},"reason":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}},"delete":{"operationId":"unpin_composition","tags":["Compositions"],"summary":"Unpin a composition","description":"The composition is no longer pinned. It is not deleted now, but a later save may delete it.","parameters":[{"name":"composition","in":"path","required":true,"schema":{"type":"string","description":"The composition's handle"},"description":"The composition's handle"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"pinned":{"type":"boolean"},"composition":{"type":"string"},"shelf":{"type":"object","properties":{"kept":{"type":"integer","description":"Compositions on the shelf"},"max":{"type":"integer","description":"The shelf's size on this plan"}},"required":["kept","max"]},"error":{"type":"string"},"reason":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/duets":{"post":{"operationId":"publish_duet","tags":["Duets"],"summary":"Publish a duet","description":"Publish a composition as a duet: a link anybody can open to find your half in place and the rest waiting. What they make is theirs, in their own browser; you never see it. Publishing keeps the composition for good, so the link cannot go stale.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"composition":{"type":"string","description":"The composition's handle"}},"required":["composition"]}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"duet":{"description":"The code that opens it","type":"string"},"url":{"description":"The link to post","type":"string"},"design":{"type":"string"},"live":{"type":"integer"},"max_live":{"type":"integer"},"error":{"type":"string"},"reason":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}},"get":{"operationId":"list_duets","tags":["Duets"],"summary":"List your duets","description":"The duets you have published, with how they are doing: how many people opened one, and how many finished something of their own from it. The second number is the one worth watching.","responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"duets":{"type":"array","items":{"type":"object","properties":{"duet":{"type":"string"},"url":{"type":"string"},"design":{"type":"string"},"live":{"type":"boolean"},"opened":{"type":"integer"},"finished":{"type":"integer"},"published":{"type":"string"}},"required":["duet","url","design","live","opened","finished","published"]}},"live":{"type":"integer"},"max_live":{"type":"integer"}},"required":["duets","live","max_live"]}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}},"/duets/{duet}":{"delete":{"operationId":"unpublish_duet","tags":["Duets"],"summary":"Take a duet down","description":"Stop a duet: the link stops working at once for anybody who has not already opened it. The composition stays kept, and its numbers stay with it.","parameters":[{"name":"duet","in":"path","required":true,"schema":{"type":"string","description":"The duet's code"},"description":"The duet's code"}],"responses":{"200":{"description":"Done","content":{"application/json":{"schema":{"type":"object","properties":{"stopped":{"type":"boolean"},"live":{"type":"integer"},"error":{"type":"string"},"reason":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/NoKey"},"402":{"$ref":"#/components/responses/Membership"},"413":{"$ref":"#/components/responses/Storage"},"429":{"$ref":"#/components/responses/Quota"}}}}}}