{"openapi":"3.1.0","info":{"title":"Stickit API","version":"1.0.0","description":"Turn a person's photos into a sticker set: plan a set, cut, animate and finish stickers, lay them out in a notebook.\n\nEvery paid, minutes-long step is a **generation** (`POST /v1/packs/{packId}/generations`): the call answers at once with the queued resource; the outcome lands on the pack (poll `GET /v1/packs/{packId}` with `If-None-Match` while any slot is `busy`).\nErrors are RFC 9457 problem documents (`application/problem+json`); branch on `status`, `code` and `retryable`, never on `title` or `detail`.\nAn app signs in with `POST /v1/auth/apple`, `/auth/google` or the email code (`/auth/email/start` then `/auth/email/verify`) and gets a **Session**: a bearer access token for every other call (about an hour) and a refresh token to trade at `POST /v1/auth/refresh`; `DELETE /v1/auth/session` signs the device out.\nA person keeps every pack they have made (`GET /v1/packs`); a new set is made in a new pack (`POST /v1/packs`, optionally `from` an earlier one). A pack is private until its owner sets `visibility: tenant`; then anyone signed in to the tenant reads it (`scope=shared` lists them) and only the owner writes.\nIds carry a type prefix (`pack_…`, `gen_…`). Times are RFC 3339 in UTC. File URLs are same-origin and need the session.\nEvery model call is on record (`GET /v1/packs/{packId}/provenance`): the prompt as sent, the pictures shown, the model, the answer; the templates the prompts are built from are readable at `GET /v1/prompts`.\nOperations marked `x-agent.sideEffect: billable` spend model credits; ask the person before calling them on their behalf."},"servers":[{"url":"/","description":"Same origin as the document"}],"security":[{"cookieAuth":[]},{"bearerAuth":[]}],"tags":[{"name":"auth","description":"Signing in from the app: Apple, Google, a code by mail; refreshing and signing out."},{"name":"me","description":"The signed-in person."},{"name":"packs","description":"A person's sticker notebooks: one per set, kept, and shared with the tenant when the owner says so."},{"name":"stickers","description":"The slots of a pack: cuts, animations, finishes."},{"name":"generations","description":"Queued work: plans, cuts, animations, finishes."},{"name":"provenance","description":"What every model call was told and shown, and the prompt templates it was built from."}],"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"stickit_session","description":"The session the web app's login leaves in the browser; the web app proxies /v1 here so the cookie is same-origin."},"bearerAuth":{"type":"http","scheme":"bearer","description":"The access token of a Session (`POST /v1/auth/*`), for the app and other clients without a cookie jar. Short-lived; refresh at `POST /v1/auth/refresh`."}},"schemas":{"Problem":{"type":"object","properties":{"type":{"type":"string","maxLength":200,"description":"URI reference naming the problem class: `/v1/problems/{code}`."},"title":{"type":"string","maxLength":200,"description":"For people; may be reworded. Branch on `code`."},"status":{"type":"integer","minimum":400,"maximum":599},"code":{"type":"string","maxLength":80,"description":"Stable machine-readable code, e.g. `validation_failed`, `plan_in_progress`."},"detail":{"type":"string","maxLength":2000,"description":"For people; may be reworded or localised."},"instance":{"type":"string","maxLength":500},"requestId":{"type":"string","maxLength":80,"description":"Also sent as the `X-Request-Id` header. Quote it when reporting a problem."},"retryable":{"type":"boolean","description":"Whether the same request may succeed later without any change."},"errors":{"type":"array","items":{"$ref":"#/components/schemas/ProblemError"},"maxItems":50}},"required":["type","title","status","code","requestId","retryable"],"additionalProperties":false,"description":"RFC 9457 Problem Details, served as `application/problem+json`."},"ProblemError":{"type":"object","properties":{"path":{"type":"string","maxLength":200,"description":"JSON pointer-like path of the offending field, e.g. `body.kind`."},"code":{"type":"string","maxLength":80},"message":{"type":"string","maxLength":500}},"required":["path","code","message"],"additionalProperties":false},"EmailCodeRequest":{"type":"object","properties":{"email":{"type":"string","maxLength":254,"format":"email","description":"The address, any case; compared lower-cased."}},"required":["email"],"additionalProperties":false},"Session":{"type":"object","properties":{"tokenType":{"type":"string","enum":["Bearer"]},"accessToken":{"type":"string","maxLength":4096,"description":"Send as `Authorization: Bearer …` on every call. Lives about an hour; refresh before `accessTokenExpiresAt`."},"accessTokenExpiresAt":{"type":"string","format":"date-time","description":"RFC 3339, UTC."},"refreshToken":{"type":"string","maxLength":200,"description":"Keep in the keychain. One use: `POST /v1/auth/refresh` retires it and hands back a new one. Presenting a retired token ends the session."},"refreshTokenExpiresAt":{"type":"string","format":"date-time","description":"When this refresh token stops working if unused; every refresh extends it, up to a cap from the first sign-in."},"user":{"$ref":"#/components/schemas/Me"}},"required":["tokenType","accessToken","accessTokenExpiresAt","refreshToken","refreshTokenExpiresAt","user"],"additionalProperties":false,"description":"A device's sign-in: the tokens and who they are for."},"Me":{"type":"object","properties":{"id":{"type":"string","maxLength":200,"description":"The signed-in person's id: an opaque string, stable across the ways they sign in. Owner of every pack the session can see."},"openId":{"type":"string","maxLength":200,"deprecated":true,"description":"The same value as `id`, under the name it had when every person signed in with Lark. Read `id`."},"name":{"type":"string","maxLength":200}},"required":["id","openId","name"],"additionalProperties":false},"EmailCodeVerifyRequest":{"type":"object","properties":{"email":{"type":"string","maxLength":254,"format":"email","description":"The address, any case; compared lower-cased."},"code":{"type":"string","pattern":"^\\d{4,10}$","description":"The digits from the mail."},"device":{"$ref":"#/components/schemas/Device"}},"required":["email","code"],"additionalProperties":false},"Device":{"type":"object","properties":{"platform":{"type":"string","enum":["ios","android","web","other"],"description":"Which kind of device signed in."},"name":{"type":"string","maxLength":80,"default":"","description":"A name for a list of signed-in devices, e.g. the phone's model.","example":"iPhone 17 Pro"}},"required":["platform"],"additionalProperties":false,"description":"The device a session is issued to. Optional on every sign-in; unnamed devices are `other`."},"AppleSignInRequest":{"type":"object","properties":{"identityToken":{"type":"string","minLength":1,"maxLength":8192,"description":"`ASAuthorizationAppleIDCredential.identityToken`, as a string."},"nonce":{"type":"string","minLength":16,"maxLength":256,"description":"The raw nonce; the app gave Apple its SHA-256 (hex), which the token echoes."},"name":{"type":"string","maxLength":80,"description":"`fullName` from the credential, joined; Apple gives it on the first sign-in only. Kept if the person has no name yet."},"device":{"$ref":"#/components/schemas/Device"}},"required":["identityToken","nonce"],"additionalProperties":false},"GoogleSignInRequest":{"type":"object","properties":{"idToken":{"type":"string","minLength":1,"maxLength":8192,"description":"`GIDGoogleUser.idToken.tokenString`; on Android, the Credential Manager's Google ID token."},"nonce":{"type":"string","minLength":16,"maxLength":256,"description":"The raw nonce the app passed to the SDK; the token echoes it as is."},"device":{"$ref":"#/components/schemas/Device"}},"required":["idToken","nonce"],"additionalProperties":false},"RefreshRequest":{"type":"object","properties":{"refreshToken":{"type":"string","minLength":1,"maxLength":200}},"required":["refreshToken"],"additionalProperties":false},"PackList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Pack"},"maxItems":50},"nextPageToken":{"type":["string","null"],"maxLength":500,"description":"Pass back as `pageToken` for the next page; null on the last page."}},"required":["items","nextPageToken"],"additionalProperties":false},"Pack":{"type":"object","properties":{"id":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"},"photos":{"type":"array","items":{"$ref":"#/components/schemas/Photo"},"maxItems":4,"description":"The character's photos, first on top."},"references":{"type":"array","items":{"type":"string","minLength":1,"maxLength":500},"maxItems":3,"description":"The maker's own style reference stickers."},"preset":{"type":"string","maxLength":80,"description":"Style preset id; empty until the planner picks one."},"emoji":{"type":"string","maxLength":16,"description":"The two theme emoji, joined; empty for none."},"memes":{"type":"array","items":{"type":"string","maxLength":80},"maxItems":24},"mode":{"type":"string","enum":["style","meme","remix"],"description":"How the set is made: `style` (chat moments in a style), `meme` (memes re-enacted), `remix` (both chosen)."},"anchor":{"type":"string","minLength":1,"maxLength":500,"description":"The anchor sticker every cut copies its style from."},"count":{"type":"integer","minimum":6,"maximum":24},"brief":{"type":"string","maxLength":1000},"plan":{"$ref":"#/components/schemas/Plan"},"planError":{"type":"string","maxLength":2000,"description":"Why the last plan generation failed, for people; put away with `PATCH {planError: null}`."},"busy":{"type":"string","enum":["plan"],"description":"A plan generation is queued or running for the pack."},"stamp":{"type":["string","null"],"maxLength":40},"seed":{"type":"integer"},"labels":{"type":"object","additionalProperties":{"type":"string","maxLength":200}},"page":{"type":"integer","minimum":0,"maximum":1000},"placed":{"type":"array","items":{"$ref":"#/components/schemas/Placed"},"maxItems":200},"stickers":{"type":"array","items":{"$ref":"#/components/schemas/Sticker"},"maxItems":24},"visibility":{"type":"string","enum":["private","tenant"],"description":"`private`: only the owner. `tenant`: anyone signed in to the same tenant reads and downloads it; only the owner writes."},"sharedBy":{"type":"string","maxLength":200,"description":"The owner's name as it was when they shared the pack; only on a shared pack."},"editable":{"type":"boolean","description":"Whether this session may write to the pack: true for the owner, false for a reader of a shared pack."},"createdAt":{"type":"string","format":"date-time","description":"RFC 3339, UTC."},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","photos","references","preset","emoji","memes","mode","count","brief","plan","stamp","seed","labels","page","placed","stickers","visibility","editable","createdAt","updatedAt"],"additionalProperties":false,"description":"A person's sticker notebook: photos, plan, stickers and their layout. A person keeps every notebook they have made; each one is one set."},"Photo":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":500,"description":"Same-origin file URL; send the session with it."},"name":{"type":"string","maxLength":200}},"required":["url","name"],"additionalProperties":false},"Plan":{"type":["object","null"],"properties":{"subject":{"type":"string","maxLength":2000,"description":"The character notes; editable through `subject` on the pack."},"supportingCast":{"type":"string","maxLength":2000,"description":"Recurring supporting-character identities and their stable relationship to the main subject; no per-sticker action or emotion."},"style":{"type":"string","maxLength":2000,"description":"The style card in force; editable through `style` on the pack."},"title":{"type":"string","maxLength":200},"stickers":{"type":"array","items":{"$ref":"#/components/schemas/StickerSpec"},"maxItems":24}},"required":["subject","style","stickers"],"additionalProperties":false},"StickerSpec":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":80,"description":"The sticker's slot id within its pack, from the plan.","example":"s1"},"caption":{"type":"string","maxLength":200},"scene":{"type":"string","maxLength":1000},"expression":{"type":"string","maxLength":500},"pose":{"type":"string","maxLength":500},"camera":{"type":"string","maxLength":500},"prop":{"type":"string","maxLength":500},"costume":{"type":"string","maxLength":500,"description":"A temporary outfit or worn item in this sticker, named with its wearer; not a permanent identity trait."},"setting":{"type":"string","maxLength":500},"companion":{"type":"string","maxLength":500},"effects":{"type":"string","maxLength":500},"showCaption":{"type":"boolean"},"motion":{"type":"string","maxLength":1000},"meme":{"type":"string","maxLength":80,"description":"The meme this sticker re-enacts, if any."},"spirit":{"type":"string","maxLength":1000},"meaning":{"type":"string","maxLength":200},"emoji":{"type":"array","items":{"type":"string","maxLength":16},"maxItems":10},"keywords":{"type":"array","items":{"type":"string","maxLength":60},"maxItems":20}},"required":["id","caption","scene","motion","meaning","emoji","keywords"],"additionalProperties":false},"Placed":{"type":"object","properties":{"id":{"type":"string","maxLength":80},"x":{"type":"number"},"y":{"type":"number"},"rot":{"type":"number"},"scale":{"type":"number"},"z":{"type":"number"},"page":{"type":"integer","minimum":0,"maximum":1000}},"required":["id","x","y","rot","scale","z","page"],"additionalProperties":false,"description":"A sticker laid on a page: position as fractions of the page, rotation in degrees, stacking order."},"Sticker":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":80,"description":"The sticker's slot id within its pack, from the plan.","example":"s1"},"image":{"type":"string","minLength":1,"maxLength":500,"description":"The current cut, a transparent PNG. Absent until the cut lands."},"gif":{"type":"string","minLength":1,"maxLength":500},"aged":{"type":"string","maxLength":40},"spares":{"type":"array","items":{"$ref":"#/components/schemas/Variant"},"maxItems":50,"description":"Earlier cuts of the slot, newest first; `:swap` rotates them."},"revealed":{"type":"boolean"},"videoJob":{"type":"string","maxLength":200,"description":"The relay's video task still rendering, if any."},"imageError":{"type":"string","maxLength":2000,"description":"Why the slot is empty: the last cut's failure, for people."},"gifError":{"type":"string","maxLength":2000,"description":"The last animation or finish failure, for people."},"busy":{"type":"string","enum":["plan","cut","animate","holo","age"],"description":"The generation queued or running for this slot; poll the pack while any slot has one."}},"required":["id","spares","revealed"],"additionalProperties":false},"Variant":{"type":"object","properties":{"image":{"type":"string","minLength":1,"maxLength":500},"gif":{"type":"string","minLength":1,"maxLength":500},"aged":{"type":"string","maxLength":40,"description":"The stamp this variant went through; absent for a clean cut."}},"required":["image"],"additionalProperties":false},"CreatePackRequest":{"type":"object","properties":{"from":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","description":"One of the person's own packs to start from: its photos and style references are copied into the new pack, with its notes and choices. The plan, stickers and pages are not; the pack named here is left as it is."}},"additionalProperties":false},"UpdatePackRequest":{"type":"object","properties":{"count":{"type":"integer","minimum":6,"maximum":24},"page":{"type":"integer","minimum":0,"maximum":1000},"brief":{"type":"string","maxLength":1000},"emoji":{"type":"string","maxLength":16,"description":"Two theme emoji, joined."},"memes":{"type":"array","items":{"type":"string","maxLength":80},"maxItems":24,"description":"Meme ids to re-enact, in slot order; `[]` hands the pack back to the chat-moment deal."},"mode":{"type":"string","enum":["style","meme","remix"]},"subject":{"type":"string","maxLength":600,"description":"Edits the plan's character notes."},"style":{"type":"string","maxLength":600,"description":"Edits the plan's style card; retires the anchor."},"preset":{"type":"string","maxLength":80,"description":"A style preset id, or `\"\"` to leave the choice to the planner; retires the anchor."},"stamp":{"type":["string","null"],"enum":["forwarded","fried","gif2008","crt",null]},"planError":{"type":"null","description":"Only `null`: puts the last plan failure note away."},"labels":{"type":"object","additionalProperties":{"type":"string","maxLength":200}},"placed":{"type":"array","items":{"$ref":"#/components/schemas/Placed"},"maxItems":200},"visibility":{"type":"string","enum":["private","tenant"],"description":"`tenant` shares the pack with everyone signed in to the tenant (read-only for them); `private` takes it back."}},"additionalProperties":false,"description":"JSON Merge Patch semantics on the listed fields only."},"StickerList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Sticker"},"maxItems":24},"nextPageToken":{"type":["string","null"],"maxLength":500}},"required":["items","nextPageToken"],"additionalProperties":false},"UpdateStickerRequest":{"type":"object","properties":{"revealed":{"type":"boolean","description":"Turn the sticker face up (or down)."},"gifError":{"type":"null","description":"Only `null`: puts the last animation or finish note away."}},"additionalProperties":false},"Generation":{"type":"object","properties":{"id":{"type":"string","pattern":"^gen_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"},"packId":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"},"stickerId":{"type":["string","null"],"minLength":1,"maxLength":80,"description":"Null for pack-level work (a plan).","example":"s1"},"kind":{"type":"string","enum":["plan","cut","animate","holo","age"]},"status":{"type":"string","enum":["queued","running","succeeded","failed"]},"attempts":{"type":"integer","minimum":0},"maxAttempts":{"type":"integer","minimum":1},"failure":{"$ref":"#/components/schemas/GenerationFailure"},"createdAt":{"type":"string","format":"date-time","description":"RFC 3339, UTC."},"updatedAt":{"type":"string","format":"date-time"},"heartbeatAt":{"type":["string","null"],"format":"date-time","description":"Last sign of life from the worker while running."}},"required":["id","packId","stickerId","kind","status","attempts","maxAttempts","createdAt","updatedAt","heartbeatAt"],"additionalProperties":false,"description":"Paid, minutes-long work the worker does for a pack: a plan, or a cut, animation or finish of one sticker. Its outcome lands on the pack (the sticker's image, GIF or error note); this resource is the audit trail."},"GenerationFailure":{"type":"object","properties":{"code":{"type":"string","maxLength":80},"message":{"type":"string","maxLength":2000,"description":"For people; the model's reason when it gave one."},"retryable":{"type":"boolean"}},"required":["code","message","retryable"],"additionalProperties":false,"description":"Present when `status` is `failed`."},"CreateGenerationRequest":{"type":"object","properties":{"kind":{"type":"string","enum":["plan","cut","animate","holo","age"],"description":"`plan`: a new set (replaces every sticker and queues their cuts). `cut`: draw one sticker. `animate`: a GIF of one cut. `holo`: holographic foil. `age`: a forwarding-history stamp."},"stickerId":{"type":"string","minLength":1,"maxLength":80,"description":"Required for every kind but `plan`.","example":"s1"},"stamp":{"type":"string","enum":["forwarded","fried","gif2008","crt"],"description":"Required for `age`: which stamp to press."}},"required":["kind"],"additionalProperties":false},"GenerationList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Generation"},"maxItems":100},"nextPageToken":{"type":["string","null"],"maxLength":500}},"required":["items","nextPageToken"],"additionalProperties":false},"ProvenanceList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Provenance"},"maxItems":500},"nextPageToken":{"type":["string","null"],"maxLength":500}},"required":["items","nextPageToken"],"additionalProperties":false},"Provenance":{"type":"object","properties":{"id":{"type":"string","maxLength":80},"kind":{"type":"string","enum":["plan","style","anchor","cut","video"],"description":"`plan`: the meta prompt that wrote the plan. `style`: a style card read off reference stickers. `anchor`, `cut`: one image call. `video`: one animation."},"stickerId":{"type":["string","null"],"minLength":1,"maxLength":80,"description":"Null for pack-level work (the plan, a style card, the anchor).","example":"s1"},"output":{"type":"string","minLength":1,"maxLength":500,"description":"The file the call produced (the anchor, the cut, the GIF); absent for text answers."},"model":{"type":"string","maxLength":120,"description":"The model that answered; for an image call, the backup that answered when the first did not."},"prompt":{"type":"string","maxLength":40000,"description":"The prompt exactly as sent."},"response":{"type":"string","maxLength":40000,"description":"The model's text answer as it came, for the plan and a style card."},"inputs":{"type":"array","items":{"$ref":"#/components/schemas/ProvenanceInput"},"maxItems":12,"description":"The pictures shown to the model, in upload order."},"params":{"$ref":"#/components/schemas/ProvenanceParams"},"template":{"$ref":"#/components/schemas/TemplateStamp"},"createdAt":{"type":"string","format":"date-time"},"reconstructed":{"type":"boolean","description":"True for a record worked out after the fact from the pack as it stands (a sticker cut before records were kept): the prompt the current notes would give and the pictures on the pack today, not the call's own."}},"required":["id","kind","stickerId","model","prompt","inputs","params","template","createdAt"],"additionalProperties":false,"description":"The record of one model call made for a pack: what it was told, what it was shown, what came out."},"ProvenanceInput":{"type":"object","properties":{"role":{"type":"string","enum":["photo","reference","sample","sheet","anchor","sticker"],"description":"What the prompt told the model this picture was: a photo of the character, a style reference of the maker's, a preset's sample sticker, the character sheet of a pack with no photo (drawn from the notes alone, before the anchor), the pack's anchor, or the cut an animation starts from."},"url":{"type":"string","minLength":1,"maxLength":500,"description":"Same-origin file URL (a pack file needs the session; a preset sample is a public file of the web app)."}},"required":["role","url"],"additionalProperties":false},"ProvenanceParams":{"type":"object","properties":{"size":{"type":"string","maxLength":20},"background":{"type":"string","maxLength":20},"duration":{"type":"number"},"ratio":{"type":"string","maxLength":10},"resolution":{"type":"string","maxLength":10},"screen":{"type":"string","maxLength":10,"description":"The chroma-key colour the sticker was flattened onto, as hex."},"task":{"type":"string","maxLength":200,"description":"The relay's video task id."},"count":{"type":"integer"},"seed":{"type":"integer","description":"The seed the plan's deal was dealt from."},"mode":{"type":"string","maxLength":20},"memes":{"type":"array","items":{"type":"string","maxLength":80},"maxItems":24},"preset":{"type":"string","maxLength":80},"theme":{"type":"array","items":{"type":"string","maxLength":16},"maxItems":2}},"additionalProperties":false},"TemplateStamp":{"type":"object","properties":{"id":{"type":"string","enum":["plan","style","anchor","cut","video"]},"version":{"type":"string","maxLength":40,"description":"The label a person gave this revision of the template."},"hash":{"type":"string","maxLength":40,"description":"Hash of the template rendered over a fixed sample pack: changes whenever any wording changes."}},"required":["id","version","hash"],"additionalProperties":false},"PromptTemplateList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PromptTemplate"},"maxItems":10},"nextPageToken":{"type":["string","null"],"maxLength":500}},"required":["items","nextPageToken"],"additionalProperties":false},"PromptTemplate":{"type":"object","properties":{"id":{"type":"string","enum":["plan","style","anchor","cut","video"]},"version":{"type":"string","maxLength":40},"hash":{"type":"string","maxLength":40},"title":{"type":"string","maxLength":80},"description":{"type":"string","maxLength":1000},"source":{"type":"string","maxLength":200,"description":"Where the wording is built in the API's code."},"sample":{"type":"string","maxLength":40000,"description":"The template rendered over one fixed sample pack: the current wording, readable end to end."}},"required":["id","version","hash","title","description","source","sample"],"additionalProperties":false}},"parameters":{}},"paths":{"/v1/auth/email/start":{"post":{"operationId":"startEmailCode","tags":["auth"],"summary":"Mail a one-time code","description":"Sends a one-time code to the address; follow with `POST /v1/auth/email/verify`. The answer is the same whether or not the address has signed in before, and an address unknown so far is signed up by verifying the code: the code proves the address, the address is the account.","security":[],"x-agent":{"sideEffect":"write","idempotency":"none","note":"Sends a mail every time; do not call on a person's behalf without their address and consent."},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCodeRequest"}}}},"responses":{"204":{"description":"The code is on its way."},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many tries from this caller or for this address (`rate_limited`); wait and try again.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"This way in is not configured on the server (`sign_in_not_configured`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/auth/email/verify":{"post":{"operationId":"verifyEmailCode","tags":["auth"],"summary":"Sign in with the mailed code","description":"Trades the code from the mail for a Session. A wrong or expired code is 401 `invalid_code`; after too many wrong ones the address is locked for a while (429).","security":[],"x-agent":{"sideEffect":"write","idempotency":"none"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailCodeVerifyRequest"}}}},"responses":{"200":{"description":"Signed in: the tokens for this device.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"401":{"description":"The code is wrong or has expired (`invalid_code`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many tries from this caller or for this address (`rate_limited`); wait and try again.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"This way in is not configured on the server (`sign_in_not_configured`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/auth/apple":{"post":{"operationId":"signInWithApple","tags":["auth"],"summary":"Sign in with Apple","description":"Takes the identity token Sign in with Apple gave the app and answers with a Session. The token must be for this app (its audience is the bundle id), signed by Apple, fresh, and carry the SHA-256 of `nonce`; anything else is 401 `invalid_identity_token`.\n\nPass `name` from the credential's `fullName` when Apple gives it (the first sign-in only): it becomes the person's name if they have none yet. The address in the token joins this sign-in to an existing person who proved the same address another way; a private relay address is the person's own.","security":[],"x-agent":{"sideEffect":"write","idempotency":"none"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppleSignInRequest"}}}},"responses":{"200":{"description":"Signed in: the tokens for this device.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"401":{"description":"Apple did not vouch for this token, or it was made for another sign-in (`invalid_identity_token`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"This way in is not configured on the server (`sign_in_not_configured`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/auth/google":{"post":{"operationId":"signInWithGoogle","tags":["auth"],"summary":"Sign in with Google","description":"Takes the ID token Google Sign-In gave the app and answers with a Session. The token must be for one of this app's OAuth clients, signed by Google, fresh, and echo `nonce`; anything else is 401 `invalid_identity_token`. The name and picture Google gives are kept; the address joins this sign-in to an existing person who proved the same address another way.","security":[],"x-agent":{"sideEffect":"write","idempotency":"none"},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoogleSignInRequest"}}}},"responses":{"200":{"description":"Signed in: the tokens for this device.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"401":{"description":"Google did not vouch for this token, or it was made for another sign-in (`invalid_identity_token`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"This way in is not configured on the server (`sign_in_not_configured`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/auth/refresh":{"post":{"operationId":"refreshSession","tags":["auth"],"summary":"Trade a refresh token for a new pair","description":"Retires the refresh token and answers with a new Session (new access token, new refresh token) for the same device. Store the new refresh token before using anything else from the answer: the old one is gone the moment this returns.\n\nA token that was already traded in is a replay (401 `refresh_token_reused`): the whole device session is revoked and the person signs in again on that device. An unknown token is 401 `refresh_token_invalid`; one past its life, 401 `refresh_token_expired`.","security":[],"x-agent":{"sideEffect":"write","idempotency":"none","note":"Each call retires the token it was given."},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"}}}},"responses":{"200":{"description":"Signed in: the tokens for this device.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"401":{"description":"The refresh token is unknown, expired, revoked or already used (`refresh_token_invalid`, `refresh_token_expired`, `refresh_token_reused`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/auth/session":{"delete":{"operationId":"signOut","tags":["auth"],"summary":"Sign this device out","description":"Revokes the device session the bearer access token was issued under: its refresh token stops working at once; the access token itself runs out within the hour, so the app drops both. Idempotent; a session already revoked answers the same. A cookie session (the web) is not a device session and is signed out at `/api/auth/logout` instead.","x-agent":{"sideEffect":"write","idempotency":"safe"},"responses":{"204":{"description":"Signed out."},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/me":{"get":{"operationId":"getMe","tags":["me"],"summary":"Who the session belongs to","description":"The signed-in person. The one call a client makes to learn whether it is signed in and whose packs it will see. No side effects.","x-agent":{"sideEffect":"none","idempotency":"safe"},"responses":{"200":{"description":"The signed-in person.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/packs":{"get":{"operationId":"listPacks","tags":["packs"],"summary":"The person's packs, or everyone's shared ones","description":"Newest first, a page at a time. `scope=mine` (the default) lists only the person's own packs, however many they have made; nobody else's ever appear in it. `scope=shared` lists every pack anyone in the tenant has shared (`visibility: tenant`), each `editable: false` unless it is the person's own. A person with no pack yet gets an empty list: `POST /v1/packs` opens one.","x-agent":{"sideEffect":"none","idempotency":"safe"},"parameters":[{"schema":{"type":"string","enum":["mine","shared"],"description":"`mine` (default): the person's own packs. `shared`: every pack anyone has shared with the tenant, read-only."},"required":false,"description":"`mine` (default): the person's own packs. `shared`: every pack anyone has shared with the tenant, read-only.","name":"scope","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"description":"Packs per page; default 20, at most 50."},"required":false,"description":"Packs per page; default 20, at most 50.","name":"pageSize","in":"query"},{"schema":{"type":"string","maxLength":500,"description":"The `nextPageToken` of the previous page."},"required":false,"description":"The `nextPageToken` of the previous page.","name":"pageToken","in":"query"}],"responses":{"200":{"description":"One page of packs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackList"}}}},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createPack","tags":["packs"],"summary":"Open a new pack","description":"A blank pack, or one started `from` another of the person's own: that pack's photos and style references are copied over (as files of the new pack's own), with its notes, choices and label tapes; its plan, stickers and pages stay where they are. This is how a new set is made without losing the last one: open a pack from the old one, then queue a `plan` generation on it. The new pack is private.","x-agent":{"sideEffect":"write","idempotency":"none","note":"Every call opens another pack; list first if one may already exist."},"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePackRequest"}}}},"responses":{"201":{"description":"The new pack.","headers":{"Location":{"schema":{"type":"string"},"description":"Where to read the pack."},"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pack"}}}},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"`from` names no pack of this person's.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/packs/{packId}":{"get":{"operationId":"getPack","tags":["packs"],"summary":"One pack, as it stands","description":"The pack with its stickers and what the worker has in hand for it (`busy` on the pack and on each sticker). Poll this while anything is busy, sending `If-None-Match` with the last `ETag`; an unchanged pack answers 304. Start around every 2 seconds and back off towards 10.","x-agent":{"sideEffect":"none","idempotency":"safe"},"parameters":[{"schema":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","example":"pack_0f1e2d3c-4b5a-4c6d-8e9f-0a1b2c3d4e5f"},"required":true,"name":"packId","in":"path"}],"responses":{"200":{"description":"The pack.","headers":{"ETag":{"schema":{"type":"string"},"description":"Weak validator of this representation."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pack"}}}},"304":{"description":"Unchanged since the version named by `If-None-Match`."},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such pack (or sticker) for this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"operationId":"deletePack","tags":["packs"],"summary":"Throw the pack away","description":"The pack, its stickers, pages and every file of theirs are gone for good. Refused (409, retryable) while a generation of the pack is queued or running.","x-agent":{"sideEffect":"write","idempotency":"safe","confirmationRecommended":true,"note":"Irreversible; a second call answers 404."},"parameters":[{"schema":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","example":"pack_0f1e2d3c-4b5a-4c6d-8e9f-0a1b2c3d4e5f"},"required":true,"name":"packId","in":"path"}],"responses":{"204":{"description":"Gone."},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The pack is someone else's, shared with this person (`read_only`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such pack (or sticker) for this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"A generation of the pack is live (`pack_in_progress`); wait for it, then try again.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updatePack","tags":["packs"],"summary":"Edit the pack's notes, choices and layout","description":"Merge-patch semantics on the listed fields: a missing field is left alone, `planError: null` puts the plan failure note away, arrays replace whole. Changing `preset` or `style` retires the anchor; the next cut draws a new one. `visibility: tenant` shares the pack with everyone signed in to the tenant, under the person's name; `private` takes it back at once. Send `If-Match` with the `ETag` you read to refuse a lost update (412).","x-agent":{"sideEffect":"write","idempotency":"safe","note":"Idempotent by content: the same patch twice leaves the same pack."},"parameters":[{"schema":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","example":"pack_0f1e2d3c-4b5a-4c6d-8e9f-0a1b2c3d4e5f"},"required":true,"name":"packId","in":"path"},{"schema":{"type":"string","maxLength":200,"description":"The `ETag` of the version you read; the edit is refused (412) if the pack moved on."},"required":false,"description":"The `ETag` of the version you read; the edit is refused (412) if the pack moved on.","name":"If-Match","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePackRequest"}}}},"responses":{"200":{"description":"The pack after the edit.","headers":{"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pack"}}}},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The pack is someone else's, shared with this person (`read_only`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such pack (or sticker) for this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"`If-Match` names a version that is no longer current.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/packs/{packId}/photos":{"post":{"operationId":"addPackPhoto","tags":["packs"],"summary":"Put a photo of the character on the pack","description":"The JPEG goes in the request body; there is no separate upload step and no storage credential to hold. The newest photo is the one on the notes page, and every photo goes to the models as the same character, so send photos of one person, pet or thing. Adding one retires the anchor: the next cut draws a new one. At most 4 photos; resize the long edge to about 2048 px and keep it under 4 MB.","x-agent":{"sideEffect":"write","idempotency":"none","note":"Sending the same photo twice puts two photos on the pack; read the pack first to see what is already there."},"parameters":[{"schema":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","example":"pack_0f1e2d3c-4b5a-4c6d-8e9f-0a1b2c3d4e5f"},"required":true,"name":"packId","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":120,"description":"What to call this photo on the desk. Defaults to 参考照."},"required":false,"description":"What to call this photo on the desk. Defaults to 参考照.","name":"name","in":"query"}],"requestBody":{"required":true,"description":"The photo itself, a JPEG of at most 4 MB.","content":{"image/jpeg":{"schema":{"type":"string","format":"binary"}}}},"responses":{"201":{"description":"The pack with the photo on it.","headers":{"ETag":{"schema":{"type":"string"},"description":"Weak validator of this representation."},"Location":{"schema":{"type":"string"},"description":"Where to read the photo back, with the session."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pack"}}}},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The pack is someone else's, shared with this person (`read_only`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such pack (or sticker) for this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The pack already holds as many photos as it takes (`photo_limit_reached`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"The body is larger than 4 MB.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"415":{"description":"The body is not `image/jpeg`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"The photo did not reach the store; send it again.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"operationId":"removePackPhoto","tags":["packs"],"summary":"Take a photo off the pack","description":"Names the photo by the `url` the pack lists for it. The file goes with it, and the anchor retires the way it does when a photo is added.","x-agent":{"sideEffect":"write","idempotency":"safe","note":"Removing a photo that is already gone answers 404; the pack is left alone."},"parameters":[{"schema":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","example":"pack_0f1e2d3c-4b5a-4c6d-8e9f-0a1b2c3d4e5f"},"required":true,"name":"packId","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":500,"description":"The photo's `url` as the pack lists it."},"required":true,"description":"The photo's `url` as the pack lists it.","name":"url","in":"query"}],"responses":{"200":{"description":"The pack without that photo.","headers":{"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pack"}}}},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The pack is someone else's, shared with this person (`read_only`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such pack, or no photo at that url (`photo_not_found`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/packs/{packId}/stickers":{"get":{"operationId":"listStickers","tags":["stickers"],"summary":"The pack's stickers","description":"Every slot of the plan with its current cut, GIF, finishes, spares and what the worker has in hand for it. The same list as `stickers` on the pack.","x-agent":{"sideEffect":"none","idempotency":"safe"},"parameters":[{"schema":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","example":"pack_0f1e2d3c-4b5a-4c6d-8e9f-0a1b2c3d4e5f"},"required":true,"name":"packId","in":"path"}],"responses":{"200":{"description":"The stickers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StickerList"}}}},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such pack (or sticker) for this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/packs/{packId}/stickers/{stickerId}":{"get":{"operationId":"getSticker","tags":["stickers"],"summary":"One sticker","description":"One slot of the pack, with what the worker has in hand for it.","x-agent":{"sideEffect":"none","idempotency":"safe"},"parameters":[{"schema":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","example":"pack_0f1e2d3c-4b5a-4c6d-8e9f-0a1b2c3d4e5f"},"required":true,"name":"packId","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":80,"description":"The sticker's slot id within its pack, from the plan.","example":"s1"},"required":true,"description":"The sticker's slot id within its pack, from the plan.","name":"stickerId","in":"path"}],"responses":{"200":{"description":"The sticker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sticker"}}}},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such pack (or sticker) for this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateSticker","tags":["stickers"],"summary":"Turn a sticker over, or put its failure note away","description":"The two facts about a sticker the maker decides: `revealed` turns it face up or down; `gifError: null` puts away an animation or finish failure note once read.","x-agent":{"sideEffect":"write","idempotency":"safe"},"parameters":[{"schema":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","example":"pack_0f1e2d3c-4b5a-4c6d-8e9f-0a1b2c3d4e5f"},"required":true,"name":"packId","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":80,"description":"The sticker's slot id within its pack, from the plan.","example":"s1"},"required":true,"description":"The sticker's slot id within its pack, from the plan.","name":"stickerId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStickerRequest"}}}},"responses":{"200":{"description":"The sticker after the edit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sticker"}}}},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The pack is someone else's, shared with this person (`read_only`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such pack (or sticker) for this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/packs/{packId}/stickers/{stickerId}:swap":{"post":{"operationId":"swapSticker","tags":["stickers"],"summary":"Bring the next earlier cut to the top","description":"Rotates the slot's spares: the newest spare becomes the current cut (with its own GIF and finish), the current one goes to the bottom of the pile. Refused (409) while the worker holds the slot or a plan is on its way; a slot with no spares answers 404.","x-agent":{"sideEffect":"write","idempotency":"none","note":"Each call rotates once; calling twice rotates twice."},"parameters":[{"schema":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","example":"pack_0f1e2d3c-4b5a-4c6d-8e9f-0a1b2c3d4e5f"},"required":true,"name":"packId","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":80,"description":"The sticker's slot id within its pack, from the plan.","example":"s1"},"required":true,"description":"The sticker's slot id within its pack, from the plan.","name":"stickerId","in":"path"}],"responses":{"200":{"description":"The sticker after the swap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sticker"}}}},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The pack is someone else's, shared with this person (`read_only`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such pack (or sticker) for this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"The slot is busy or a plan is on its way; try again when it settles.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/packs/{packId}/generations":{"post":{"operationId":"createGeneration","tags":["generations"],"summary":"Queue a plan, cut, animation or finish","description":"Queues paid work for the worker and answers at once with the generation resource; the outcome lands on the pack, not here. A `plan` replaces every sticker of the pack and queues a cut for each new slot (the plan job also draws the anchor). `cut` draws one sticker from the photos and the anchor; `animate` renders a GIF of a cut; `holo` and `age` are finishes on a cut (`age` names a `stamp`).\n\nIdempotent by natural key: a second request for the same pack, sticker, kind and parameters while the first is still queued or running returns that same generation. One generation holds a slot at a time and a plan holds every slot, so a request in the way of live work is refused with 409 and a `retryable` problem; poll the pack until `busy` clears, then send it again.","x-agent":{"sideEffect":"billable","idempotency":"natural-key","confirmationRecommended":true,"note":"Spends model credits: a plan is one vision call plus one image per anchor, a cut one image edit, an animation minutes of video rendering."},"parameters":[{"schema":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","example":"pack_0f1e2d3c-4b5a-4c6d-8e9f-0a1b2c3d4e5f"},"required":true,"name":"packId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGenerationRequest"}}}},"responses":{"201":{"description":"The generation, queued (or the live one already queued for the same work).","headers":{"Location":{"schema":{"type":"string"},"description":"Where to poll this generation."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Generation"}}}},"400":{"description":"The pack is not ready for this kind (no photo or theme, no memes for a meme pack).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The pack is someone else's, shared with this person (`read_only`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such pack (or generation) for this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"A live generation stands in the way (`plan_in_progress`, `sticker_in_progress`, `stickers_in_progress`, `sticker_not_cut`); retryable once it settles.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"operationId":"listGenerations","tags":["generations"],"summary":"The pack's generations","description":"Live generations (queued and running) by default; `status=all` adds the most recently settled ones, live first. Settled generations are kept a week.","x-agent":{"sideEffect":"none","idempotency":"safe"},"parameters":[{"schema":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","example":"pack_0f1e2d3c-4b5a-4c6d-8e9f-0a1b2c3d4e5f"},"required":true,"name":"packId","in":"path"},{"schema":{"type":"string","enum":["live","all"],"description":"`live` (default): queued and running only. `all`: the most recent settled ones too."},"required":false,"description":"`live` (default): queued and running only. `all`: the most recent settled ones too.","name":"status","in":"query"}],"responses":{"200":{"description":"The generations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationList"}}}},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such pack (or generation) for this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/packs/{packId}/generations/{generationId}":{"get":{"operationId":"getGeneration","tags":["generations"],"summary":"One generation","description":"The generation's status and, when it failed, why. Send `If-None-Match` with the last `ETag` while polling; unchanged answers 304. The result itself (the picture, the GIF) is on the pack.","x-agent":{"sideEffect":"none","idempotency":"safe"},"parameters":[{"schema":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","example":"pack_0f1e2d3c-4b5a-4c6d-8e9f-0a1b2c3d4e5f"},"required":true,"name":"packId","in":"path"},{"schema":{"type":"string","pattern":"^gen_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","example":"gen_9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d"},"required":true,"name":"generationId","in":"path"}],"responses":{"200":{"description":"The generation.","headers":{"ETag":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Generation"}}}},"304":{"description":"Unchanged since the version named by `If-None-Match`."},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such pack (or generation) for this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/packs/{packId}/provenance":{"get":{"operationId":"listProvenance","tags":["provenance"],"summary":"What every model call for the pack was told and shown","description":"One record per model call, newest first: the plan's meta prompt, a style card read off references, the anchor, every cut, every animation. Each carries the prompt exactly as sent, the pictures shown (in upload order, with their role), the model that answered, the answer or the file that came out, and the template revision. A sticker (or anchor) made before records were kept gets a record worked out from the pack as it stands, marked `reconstructed: true`. `stickerId` narrows it to one slot. Readable by whoever may read the pack: its owner, or anyone it is shared with. Kept while the pack is.","x-agent":{"sideEffect":"none","idempotency":"safe"},"parameters":[{"schema":{"type":"string","pattern":"^pack_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","example":"pack_0f1e2d3c-4b5a-4c6d-8e9f-0a1b2c3d4e5f"},"required":true,"name":"packId","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":80,"description":"Only this slot's cuts and animations.","example":"s1"},"required":false,"description":"Only this slot's cuts and animations.","name":"stickerId","in":"query"}],"responses":{"200":{"description":"The records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvenanceList"}}}},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such pack for this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request does not fit its schema; see `errors`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/prompts":{"get":{"operationId":"listPromptTemplates","tags":["provenance"],"summary":"The prompt templates as this build has them","description":"Every template the API builds prompts from, with its id, version label, the hash of its current wording and the wording itself rendered over one fixed sample pack. A provenance record names its template by the same id, version and hash, so a prompt in a record can be matched to the template revision that built it.","x-agent":{"sideEffect":"none","idempotency":"safe"},"responses":{"200":{"description":"The templates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptTemplateList"}}}},"401":{"description":"No valid session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"webhooks":{}}