Wyldcards API — Link Relations

The API is HAL (application/hal+json) with HAL-FORMS negotiable alongside it. Every relation Wyldcards defines is namespaced under the wyldcards curie, whose template points at this page: /docs/api-rels.html#{rel}. IANA relations such as self stay bare.

Start at GET /api. It answers with the relations the caller may follow: everyone signed in gets account; an admin also gets users. A signed-out caller gets a 401 whose body advertises forgotPassword, passkeyLogin and, when sign-up is on, signUp.

Navigation

RelationMeaning
usersThe collection of accounts. Admin only.
accountThe signed-in user's own account. Not an admin's view of someone else's.
forgotPasswordAsk for a password-reset email. POST {"email"}. Always answers 202 with the same message, whether or not the address is registered. Advertised on the 401 challenge, since nothing behind the sign-in can advertise it.
resetPasswordSet a new password using the token from a recovery email. POST {"token","password"}. GET the same URL with ?token= to ask whether a token is still good and whose account it belongs to.
passkeysThe passkeys registered to the signed-in user. Present only where passkeys are configured.
passkeyA single registered passkey: credentialId, label, created, lastUsed, backedUp (true when the passkey is synced by its provider rather than held on one device) and transports (internal, hybrid, usb, nfc…, which is how a client tells a security key from a platform passkey).
renamePasskeyPUT {"label"} (200 characters or fewer) to rename one of the caller's own passkeys; only the name changes. Answers the refreshed passkeys collection, 400 for a blank or over-long label, 404 for a passkey that is not the caller's.
registerPasskeyBegin registering a passkey: POST (no body) answers the standard WebAuthn creation options under publicKey plus a challengeId, with a verify link to POST {"challengeId","label","credential"} back to. The ceremony state lives behind the challengeId — no cookies involved — and expires after a few minutes, single-use.
passkeyLoginBegin a passkey sign-in. Advertised on the 401 challenge, like forgotPassword: the caller is signed out by definition. POST (no body) answers WebAuthn request options under publicKey plus a challengeId, with a verify link; a verified assertion answers {"username","token"} — a bearer token the client sends as Authorization: Bearer in place of Basic, since a passkey leaves it with no password.
signUpMake an account for a device that has none. Advertised on the 401 challenge beside passkeyLogin, only while the sign-up feature flag is on. POST (no body) answers 201 with the new account's username and password, shown once: the client keeps them and authenticates with Basic from then on, as with a typed password. Rate limited per address; 404 when the feature is off.
verifyThe second half of a WebAuthn ceremony: where the authenticator's response is POSTed.
revokeTokenDELETE, with the bearer token in the Authorization header, to revoke it — the API's sign-out. Links from /api/token: a password sign-in POSTs there with HTTP Basic (optional body {"label"} naming the device) and gets 201 with {"username","token"}, the same kind of session token a passkey sign-in mints, so a client need not keep the password. When the account has two-step sign-in, the password alone answers 401 {"error":"two_factor_required"}: send it again with {"code"} (see twoFactor).
stacksThe caller's own stacks, not archived and not trashed. Owner-only, exactly like the web: another user's uid answers 404. ?archived=true lists the archive instead. A stack its owner protected from themselves (privateAccess) is listed locked until its password is entered: see unlock.
homeThe signed-in user's stack arrangement (All stacks): its title, layout, sort order, sign-in stack (null opens the Home page), and active stacks in their displayed order. Each stack includes its uid, whether it is docked, and its immediate group parent. recent is the Home card's Jump back in: up to three stacks the user was in most recently, newest first, each with cardNumber and cardLabel for the card they were on (absent when that card is gone or hidden from them — open the stack instead) and when in words. Visits are recorded from the web card page only, so API reads never crowd it. This is the entry point for an AI client that needs to organize the stack screen.
stackOne stack: name, description, icon (an emoji or a glyph), card size, script, cardCount, and the template it was cloned from. Links to its cards, its export, and the mutations below.
cardsA stack's cards in order, each with its 1-based number. ?include=fields adds each card's field text by name as fields; ?field= (a field name or id) with ?value=, and ?name=, keep only exact trimmed, case-insensitive matches. Its create accepts an optional name and fields (keyed by field name or id): every value is validated first, and a bad one answers 400 without creating the card.
cardOne card. PATCH changes its name, script, marked, cantDelete, dontSearch, inBook (false leaves it out of the stack's EPUB book; the card answers inBook: false only then), pictureDescription (plain text up to 1000 characters describing the card's picture, its alt text in the EPUB book; blank clears it, longer answers 400, and the card has it only when set), bookRole (its role in the EPUB book: dedication, epigraph, foreword, preface, prologue, epilogue, afterword, acknowledgments, copyright-page or about-the-author; "" or chapter makes it a chapter again, and the card answers bookRole only when it is not one; an unknown role answers 400), publiclyVisible (a card hidden with the retired Hide card answers false until made public again, which also clears it) and place. Links to its parts (background parts first, then its own), to fields for a batch field write, and to next / prev, which wrap, and to its audioRecordings.
audioRecordingsThe audio kept with a card, which its chapter of the stack's EPUB plays, in order. Each is described (title, file name, content type, byte size, duration when known) and none is transferred: the bytes are behind each one's audioFile. Carries uploadAudio.
audioRecordingOne audio file kept with a card; readAlong ({"starts","endMs"}) when the card's words are synced to it. Links to its audioFile, its readAlong, and to update (rename, PATCH {"title"}) and delete. Cards from the recording days may hold other formats and trimStartMs/trimEndMs; the book leaves those formats out and plays whole files.
readAlongA clip's read-along, which the reflowing EPUB book makes a media overlay that highlights each line as it plays. GET answers {"lines","starts","endMs"}: the card's read-along lines (each line of its multiline, rich text and paragraphs fields, in reading order, as the book numbers them) and, when synced, when each starts in ms from the top of the file and where the reading ends. PUT {"starts":[…],"endMs"} syncs it (each start after the one before, endMs after the last; otherwise 400) and stops any other clip of the card reading along; DELETE stops this one. Starts pair with lines in order, so words edited after syncing still read along as far as both go. Owners and editors write.
audioFileThe audio itself, served with the content type it was uploaded as and Accept-Ranges: bytes, so a player can seek.
partsEvery part a card shows, with owner = background or card, and for a field this card's text.
partOne part: rect, z (PATCH "layer":"front" or "back" to bring it in front of the other parts of its layer or send it behind them), style, script, and the kind-specific fields (field style / shared / locked, and for a list field multipleLines, for a list or a checklist dontWrap, for a checklist hideCount; for a dropdown field options, the choices in order (PATCH options as one per line to set them; the field's text is the option picked); for a styled paragraphs field paragraphStyles — each style's key, label, element type, whether it is the default, caps, bold, italic, underline, align, indent and right in character widths, space before, the style Return starts (enter) and Tab moves to, the starts prefixes that turn a line into it, and parens; button label / icon; widget type / config).
fieldPUT {"text"} to write a field's text on this card — the API's autosave. Only on parts of kind FIELD. A list field's text is one item per line; a checklist field's carries the marks too, [x] done and [ ] not. Rewriting one item's wording is the same PUT with that line changed. Add "base", the part's textHash from when editing began, and a write made after someone else saved the field is merged with theirs word by word when the two changed different words of a plain, multiline, paragraphs, list or checklist field: the answer is 200 with the merged text and textHash, and the header X-Field-Merged: true. When both changed the same words (or the field is another kind), it answers 409 {"conflict","message","text","textHash"} with their text instead of overwriting it; send again without base, or with their textHash, to keep yours or a version you edited from both.
fieldsPUT {"fields":{"field name or part ID":"text"}} to write several fields on a card in one request. Every supplied value is validated before any field changes. Prefer stable part IDs; names are accepted when they are unique on the card.
exportThe whole stack as a .wyldcards.json bundle (see docs/STACK-FORMAT.md). GET answers the JSON with a download filename. For a file a person keeps, prefer exportEpub, which carries the same stack.
thumbnailThe stack's picture: its cover card (or first card) drawn as a PNG, as Home and All stacks show it. Add w (160, 320, 640 or 960 pixels wide) and theme=dark for a Classic card in the dark theme. The address changes whenever the stack does, so a copy can be kept. Drawn from saved content only: no scripts run, and a role's hidden fields stay hidden. Also on each card as thumbnailUrl. Set the cover with coverCardUid on update.
exportFolderThe stack as a folder of files, zipped: stack.json, a Markdown file per card in cards/ (YAML front matter, then each field's text under a <!-- field: Name --> line), scripts as ._hs and paint as .png beside them. It carries everything export does, and is the same folder exportEpub carries under wyldcards/; kept for older app builds. POST the zip to importPreview with ?filename=….zip to import it. See docs/STACK-FORMAT.md, "Stack folders". Owners and editors only.
exportEpubThe stack as an EPUB 3 book (application/epub+zip) to read without Wyldcards: a title page, then a chapter per card holding the card's picture and its words in reading order, the same reading as text. Links and buttons that open a card of the stack link to its chapter. A card whose inBook is false has no chapter, and a link to it is only its words. A card's bookRole is its section's epub:type, and the landmarks start the book's body at its first chapter, prologue or epilogue, past the front matter. A card's pictureDescription is its picture's alt text in both layouts; without one the picture book's page says "Card N: name". It also carries the stack folder (as exportFolder writes it) under wyldcards/, outside the book's manifest, so POSTing it to importPreview with ?filename=….epub imports the whole stack and POSTing it to updateFromFolder brings edited card files back. ?layout=pictures makes the picture book instead: a fixed-layout page per card at the card's size, the words kept in the page. The book is in the stack's language, styled by its bookTheme (classic, modern, children or screenplay) and bookCss, with the card bookCoverCardUid names as its cover (all three set on update) unless a cover picture uploaded in Settings (bookCoverImage is true) takes its place. The book's details, also set on update and each "" (or []) for none, go in its package for reading and library apps to shelve it by: bookSubjects (up to 10 keywords, each a dc:subject), bookSeries and bookSeriesPosition (a number such as 2 or 2.5; the series as belongs-to-collection and Calibre's calibre:series, and "Book 2 of …" on the title page), bookRights (up to 500 characters, dc:rights, printed small on the title page), bookPublished (YYYY-MM-DD, dc:date), bookIsbn (an ISBN-10 or ISBN-13, refused with 400 when its check digit fails, kept and answered as the 13 digits, a second dc:identifier as urn:isbn:), bookSubtitle (up to 300 characters, a second dc:title refined as the subtitle, and under the title on the title page) and bookContributors (a list, in credit order and replaced whole, of up to 20 {"name", "role"}, the role one of author for a co-author, editor, illustrator, translator, foreword, narrator, photographer, cover-designer or contributor, the default; a MARC relator code such as edt is taken too, and an unknown role answers 400. Co-authors are further dc:creators and join the byline; the rest are dc:contributors credited on the title page, each refined by its MARC relator role; the stack answers bookContributors as a list, empty for none. To prefill from the people the stack is shared with, read collaborators); the published book carries them too. The same stack gives the same bytes, so the response carries an ETag and answers If-None-Match with 304. A published stack's public book, for anyone, is /published/{slug}/book.epub: its public cards only, with no stack folder. Owners and editors only.
bookChecks"Before you publish": what would make the stack's book better, as the Preview book page lists them. GET answers layout (reflowable, or pictures with ?layout=pictures), ready (true when there is nothing to look at) and checks, each with an id, a severity (tip or warning), a message to show as it is, an href on the web where it is fixed and its action words (both null when the caller cannot open that page, such as Settings for an editor), and the cardUid and cardNumber it is about, when it is about a card. The ids: emptyBook (every card is left out, inBook false), pictureDescription (a card in the book with no pictureDescription; a warning for the picture book, a tip otherwise, and one entry for the first card when more than three have none), cover (no uploaded cover and the cover card is blank, or left out so the cover is plain), footnoteUndefined (a [^x] reference with no [^x]: line on its card), footnoteUnused (a note nothing on its card refers to), isbnWithoutDate (bookIsbn with no bookPublished) and seriesWithoutName (bookSeriesPosition with no bookSeries). More may come; show an unknown one by its message. Advertised to owners and editors; nothing changes.
exportCanvasThe stack as a JSON Canvas (jsoncanvas.org), the .canvas file Obsidian's Canvas opens. A text node per card, in card order on a grid five across (400 by 300, 80 apart), its id the card's uid and its text the card's name as a ## heading, then the words of the fields the Markdown editor carries: a field's text alone when the card has one, each under a ### heading of its name when it has more. A marked card's node has color "4" (green). An edge, with an arrow at its end and the field's or button's name as its label, runs from a card's node to each card of the stack it points at: through a linked cards field, a song put into a styled paragraphs field ([+] uid), a card link field, a button that goes to a card, or a card shortcut. Links to other stacks are left out. POST the file to importPreview with ?filename=….canvas to make the cards again. The same stack gives the same bytes, so the response carries an ETag and answers If-None-Match with 304. Owners and editors only.
updateFromFolderPOST the EPUB exportEpub wrote, or a zip of the folder inside it (or of one exportFolder wrote), edited anywhere, to bring its changes back into this stack. Each card file carries its card's uid and the card's version when the folder was written, so every card is compared three ways: changed in the folder only, the card takes the file's words and flags (as a file PUT would, so not cardDate or hidden); changed in both, the card is kept and the file's version goes in after it as a copy named "… (from folder)"; changed here only, the card is kept. A file with no uid, or one the stack no longer has, becomes a new card after the card before it in the folder. Cards with no file are left alone, as are layouts, scripts, pictures and the order of existing cards. Without ?apply=true nothing changes and the answer says what would; with it the whole folder applies or, when a field refuses its text, none of it (400). The answer: applied, counts updated, added, conflicts, unchanged, keptNewer, notInFolder, and cards — each file's file, name, status and card number. A folder from another stack answers 400. Owners and editors only.
fileOne card as the Markdown file a stack folder holds for it, with an ETag. PUT the edited file back (any content type) to change the card's name, marked, cantDelete, dontSearch, inBook, pictureDescription, bookRole, author notes (<!-- notes -->) and field text; a field or the notes left out is left alone, and layout, background and scripts are not read back. A cardDate or hidden kept from before those features went is still written in the file but not read back: changing or removing it changes nothing, and is not an error. Every field is checked before anything changes; a bad file answers 400 with a sentence. Send the ETag as If-Match and a PUT made after the card changed answers 412 with the card's current file. The answer is the file as it now stands; X-Wyldcards-Changes counts what changed. Owners and editors only.
importPreviewThe Import page's preview. POST a file's raw bytes with ?filename= — a stack's .epub or .zip folder (up to 25 MB), a .wyldcards.json backup, a .md Markdown copy, or a .docx Word document, or an Obsidian .canvas (JSON Canvas), up to 2 MB — and nothing is created: the answer carries the stack's name, backgroundCount, cardCount and partCount, and the bundle to POST to import once the person confirms. Any other .epub becomes a card per chapter, named as its contents name it, read as Markdown with the same cardBreak. A .canvas becomes a card per text, file or link node on one background, Note: a text node's first line (a heading's words) names its card and the rest is its Text, a file node's card is named after the file with the path as its text, a link node's card has the address in Link, a node with a color is a marked card, and a group's label goes in a Group field of the cards inside it. Cards come group by group, then top to bottom and left to right, and each edge is a line @key in the linked cards field Links of the card it starts from. In Markdown, # names the stack, ## starts a card and ### a field; in Word, headings start cards and paragraphs and tables become card text. A .zip of stacks — the .epub books or .wyldcards.json backups the Library's Export of several stacks makes — previews every stack: the answer adds stacks, one entry per file in the zip's order with its filename, name, format (epub or backup), the same counts and cardNames, and that stack's own bundle; the top-level format is stacks, the counts are the stacks' together, cardNames names the stacks, and bundle is {"format":"wyldcards-stacks","stacks":[…]}, which import makes all of. A file that can't be read answers 400 naming file.
importPOST a .wyldcards.json bundle as the request body to make a new stack of it. A zip of stacks' preview bundle, {"format":"wyldcards-stacks","stacks":[bundle, …]}, makes every stack in it: the answer is the first stack, as for one, with stacks listing every stack made in order. Advertised on the stack collection.
findThe same search narrowed to one stack — HyperCard's Cmd+F. It searches cards, fields and buttons, but deliberately leaves out private author notes. Each hit carries the card's n, the number in /stack/{uid}/card/{n}: a position rather than an identity, so cardId rides along for a client that wants to survive a deletion.
nextThe card after this one, wrapping to the first past the end.
prevThe card before this one, wrapping to the last before the start.
apiTokensThe caller's personal access tokens, newest first: id, label, created, lastUsed. Never the secret. create (POST {"label":"wyld"}) answers 201 with the raw token, shown once; it is refused with 403 to a request that is itself authenticated by a bearer token. Send the token as Authorization: Bearer wyld_….
apiTokenOne personal access token. Its delete revokes it.
fieldDefinitionsThe fields a new card in the stack starts with — the last card's background fields, then its own — each with id, name (null when unnamed), style, background, required and unique. What an integration builds its input form from.
fieldDefinitionOne field definition in fieldDefinitions.
sharedStacksOther people's stacks shared with the caller that they can open, each with the caller's role (editor or viewer) and a leave link. A stack whose password the caller has yet to enter is listed locked, with an unlock link in place of the rest. A viewer can read cards; an editor can also change them, though on a shared stack a script runs only once its owner has approved it, and only for a collaborator who chooses to run it in the web app.
collaboratorsWho the owner shared a stack with, each with a role and a delete link. Only the owner may read it. POST {"username","role"} or {"email","role"} (which emails an invitation) to share the stack; sharing with someone again changes their role.
collaboratorOne person a stack is shared with, listed in its collaborators.
publicSharingThe owner's view of a stack's password-protected publicLink and publishedUrl. PUT {"password"} to …/public/link to create a new link and DELETE it to revoke; PUT {"slug"} to …/public/published to publish and DELETE to unpublish. Only cards with publiclyVisible appear in either.
teamsThe caller's teams: groups of people who share stacks together. GET lists each team's uid, name, whether the caller is a manager, and its memberCount; POST {"name"} makes one, which the maker manages. /api/teams/{uid} answers the members (each with its id, username, name, role member or manager, and whether it is you), the stacks shared with the team and at what access, and — for managers — pending invitations. A manager PATCHes {"name"} to rename, DELETEs the team (members keep only access given to them directly), POSTs {"person","role"} to …/members — a username, or an email, which adds the person with that account now and sends anyone else an invitation that works for 14 days — PATCHes {"role"} on …/members/{id}, and DELETEs …/invitations/{id} to withdraw one. DELETE on …/members/{id} removes someone, or leaves the team when the id is the caller's own membership. Advertised on the API root.
stackTeamsThe teams a stack is shared with, for its owner: the shares (each with its id, teamUid, teamName, access value and accessLabel), the owner's teams it could be shared with, and the access options — viewer, writer, editor, and the stack's named roles. POST {"team","role"} shares it with a team the owner is in: everyone in the team gets it, and so does anyone who joins later. DELETE …/teams/{id} stops sharing; people keep access given to them directly. A collaborator whose access comes through a team carries that team, where the access is changed.
editionsA stack's editions: parallel copies of it, a script's blue draft beside its white one. GET answers the family, the original first — each edition's stack uid, label, whether it is the original, the isDefault one people with edit access open, the published one people with view-only access see, and the current one this request named — plus whether the caller canManage them and whether the stack is shared. Open another edition by its uid. The owner POSTs {"name"} here to make a new edition from this one, which copies every card and field; the answer's uid is the new edition. On …/edition, the owner PATCHes {"name"} to rename it, POSTs …/edition/default to make it the default, PUTs or DELETEs …/edition/published to publish it to viewers or publish nothing, and DELETEs …/edition to move it to the trash (never the original; the answer is the original's family). Listed for anyone who can edit the stack. A stack that is an edition carries its editionName.
readThe read view: a stack's styled paragraphs field from every card, in order, as one script — the same content the web read view shows. The answer carries the title and, when there is one, the titlePage; the stack's styled paragraphs fields and the field being read (choose another with ?field=); the styles, each with its key, label, element type (scene, character, dialogue and so on), caps, bold, italic, underline, align, indent and right in character widths, space before, and whether it is drawn in parens; the sections, one per card with its lines (each a style key and text, and an anchor on a scene heading); and the contents of scene headings to jump to. ?marked=true reads only the marked cards. Anyone who can open the stack may read it.
paintA card's paint layer, the picture behind its parts. PUT {"image","background"} with the whole picture as a PNG data URL (data:image/png;base64,…), up to 4,096 pixels a side — the same thing the web's paint mode posts; background true paints the card's background instead, which every card on it shows. DELETE wipes it (?background=true for the background's). Either answers the card, whose paintUrl and backgroundPaintUrl are then the fresh, cache-busted paths to the pictures. Authoring level, like moving a part.
drawingA card's draw layer: vector shapes over its picture and under its parts. PUT {"width","height","shapes","background"} with every shape of the layer, bottom first — the same thing the web's Draw mode posts; background true draws on the card's background instead, which every card on it shows, and an empty shapes clears the layer. A shape is rect or oval (x, y, w, h, a rect's rx, rotate), line (x1, y1, x2, y2, arrow: start, end or both), path (points as [x, y] pairs, closed, smooth, arrow) or text (x, y, text, size, font, bold, italic, rotate), each with stroke and fill as #rrggbb or none, strokeWidth, dash (dashed or dotted) and opacity. The server keeps only what it recognises, clamped and rounded, and never takes SVG. DELETE removes the layer (?background=true for the background's). Either answers the card, whose drawing and backgroundDrawing are the shapes as kept. Up to 1,000 shapes a layer. Authoring level, like painting.
sortHyperCard's Sort over a whole stack, which changes the order of the cards themselves. POST {"field","order","from"}: field is a field's name (absent sorts by card name), order is descending or absent for ascending, and from is the card number the caller is on. The answer gives that card's new number and the stack's cardCount, so a client can sort without losing the reader's place. Editors only.
changesA stack's change history, the web's Changes: every edit to a field or a card's name, newest first (the newest 500 are kept). GET (optionally ?card= a card number) answers changes, each with id, when, author, action, subject, cardNumber, cardName, whether it was undone, and a word-by-word diff of segments whose kind is same, removed or added. POST /{id}/put-back returns the text to how it was before that change; if it has changed again since, the answer is 409 with changedSince, and POST {"anyway":true} replaces what is there now. POST /undo takes back the caller's own most recent change and /redo brings it back; neither writes over someone else's later edit (409 with a message). Answers carry a message and the affected cardNumber. For people who can type in the stack; others get 404.
helpTopicsThe Help Center's topics, for a client that searches and shows help itself. GET answers topics, each with a unique key, the id the web page anchors it by (/help#id; a part of a topic, such as Presenter Notes, opens its topic), its title, a one-line description where the feature finder has one, the feature finder's keywords for it, the longer pageKeywords the Help Center page searches (worth less in a ranking), and its paragraphs as plain text. They are read from the same help page the web serves, so they change with it.
componentsComponents, the web inspector's saved copies of a field, button or widget, kept per person. GET /api/components answers components, each with id, name, kind and createdAt. POST {"partId","cardId","name"} saves a part as it shows on that card (editors of its stack; a script the owner hasn't approved isn't saved with it). POST /api/cards/{id}/components/{componentId} puts a copy on the card, or on its background with ?placement=background, and answers the new part; changing it doesn't change the component. DELETE /api/components/{id} removes one. A copied card link starts unassigned.
scriptFileThe page view's files. GET with ?format= pdf (the default), fountain, fdx, txt, docx or epub, optionally ?field= (the styled paragraphs field to use) and ?marked=true, and the page settings the web passes along — paper (letter or a4), margins, numbers, numberFirst, cardBreaks, titlePage — and the answer is the file with its type and a download filename. A field that can't be written that way answers 400 naming format. POST a Fountain or Final Draft file's raw bytes with ?filename= to read it in: ?placement=scenes (the default, editors) adds a card for each scene; placement=card&card= (people who can type) replaces that card's text. The answer carries paragraphs, cards and a message.
titlePageThe script's title page, which the read view and the script files put first. PUT {"title","credit","authors","source","draftDate","contact","notes"} to save it; the answer is the saved page. The read rel's answer carries it for reading. Editors only.
linkedFromLinked cards, the field type that links as many cards as you like from any stack you can open. GET on a card's linkedFrom answers the cards whose Linked cards fields name it, grouped by stack and field (stackName, stackUid, fieldName, cards); stacks the reader can't open are left out. For one field, GET /api/cards/{id}/parts/{partId}/links answers its links in the order they were made, and /link-options?q= the options a person who can type could link, found by name (the field's own stack first, or only the stack in its config). Each link carries uid, visible (false for a card in a stack the reader can't open, whose name is then withheld), name, number, stackName, stackUid, href and sameStack. Link and unlink by saving the field's text: the linked cards' uids, one per line. A part that isn't a Linked cards field on that card answers 404.
linksHereThe cards pointing at this one, the web card page's Links here: GET answers links, each with the linking card's cardId, cardNumber and cardName and the partLabel of the button or card-link field that points here, in stack order. A background button counts once for every card on that background; parts the reader's role hides are left out.
relatedCardsA card's Related cards, the undirected connection the web edits under Related cards. GET answers related — each with cardId, cardNumber and cardName, in stack order, archived cards left out. PUT {"cardIds"} replaces them with cards from the same stack (the card itself is ignored), and the relationship appears on both cards; a card from another stack answers 400. PUT is for editors.
bulkOne action across every card in a stack. POST {"action"} — mark, unmark, or delete (which deletes the marked cards, never a stack's last card) — and the answer carries how many cards changed and a message. Editors only.
cardTemplatesA stack's saved card layouts, by name, each with its backgroundName, a create link, and a delete link. POST {"cardId","name"} to save a card's parts and background as a template; POST {"after"} to a template's create link to add a card from it after that card number (or at the end). Editors only.
cardTemplateOne saved card layout, listed in cardTemplates.
snapshotsThe owner's saved snapshots of a whole stack, newest first, each with a name and a restore link. POST {"name"} to save one. Restoring makes a new stack from the snapshot and leaves the original unchanged.
snapshotOne saved copy of a stack, listed in snapshots.
trashStacks the caller moved to the trash, newest first. Each carries deletedAt and links to restore and purge; the collection links to emptyTrash.
notesA card's private author notes (once called presenter notes, which is why the field keeps that name) as {"presenterNotes"}. Only people who can edit the stack may read them; PUT the same shape to replace them, and a blank value clears them.
historyA card's saved versions, newest first (up to the most recent 50), each with a changeSummary such as "Before card date changed" and a restore link. Only people who can edit the stack may read it.
revisionOne saved version of a card, listed in its history.
commentsA card's comments, oldest first, each with its author, text, and time. Anyone who can open the card may read them and POST {"text"} to add one; a comment's delete link appears for its author and the stack's owner.
commentOne comment on a card, listed in its comments.
archivedCardsA stack's archived cards, most recently archived first. Each carries archivedAt and an unarchive link.
notificationsThe caller's notifications, newest first (the most recent 100): mentions, comments on their stacks, stacks shared with them and teams that added them. Each carries kind (MENTION, COMMENT, STACK_SHARED or TEAM_ADDED; older ones may say REMIX, from the retired remix gallery), message, actor, stackId, cardId, url (a path on this site), createdAt, readAt and read, and links to markRead; the collection links to markAllRead.
notificationOne entry in the caller's notifications.

Mutations

RelationMethodMeaning
markReadPOSTMark one notification read (add ?read=false to mark it unread again). Answers the notification.
markAllReadPOSTMark every one of the caller's notifications read. Answers 204.
createPOSTCreate a new resource in a collection.
updatePUTReplace the resource's editable fields.
deleteDELETEDelete the resource.
changePasswordPOSTChange the signed-in user's own password. The current password is required.
exportAccountGETEverything in the signed-in account as one zip, the web's Account, Data, Download everything: a folder per owned stack (the Trash aside) under Stacks/ with its stack code (.wyldcards.xml), Markdown (.md), full backup (.wyldcards.json), images/ and, for stacks that kept recordings from before Wyldcards stopped recording, recordings/, plus README.txt and manifest.json (account, export time, each stack's folder and counts). Streamed as it is written.
deleteAccountDELETEDeletes the signed-in account for good: every stack it owns (including ones shared with others, which disappear for them too, and the Trash), its places on other people's stacks and teams, its passkeys and tokens, and any webhooks kept from before. Send {"password"}, or {"confirm":"delete"} when the account has passkeys, plus {"code"} (an authenticator or recovery code) when it has two-step sign-in. Answers 204; a wrong password or code answers 400 with a sentence, and the only administrator answers 409.
twoFactorGETTwo-step sign-in for the signed-in account, as the web's Account, Security. GET answers {"enabled","enabledAt","recoveryCodesLeft"}. POST /start answers a new {"secret","secretGrouped","otpauthUri"} for the authenticator app (409 when already on); POST /enable with {"secret","code"} turns it on and answers the status with recoveryCodes, shown this once; POST /disable with {"password","code"} turns it off; POST /recovery-codes with {"code"} replaces the recovery codes. A wrong code answers 400 with a sentence; too many tries, 429. With it on, POST /api/token answers 401 {"error":"two_factor_required"} until the body carries {"code"} (an authenticator or recovery code); a wrong one answers 401 two_factor_invalid.
preferencesPUTSave the signed-in user's appearance and text size: {"theme","textSize"}, where theme is system, light, or dark and textSize is a percentage from 80 to 200. Leave either out to keep what is saved. Answers the account, which carries both.
scriptsGET/api/stacks/{uid}/scripts answers {"reviewed","owner","runs"}: whether the stack has been shared (so only approved scripts run), whether the caller is its owner, and whether scripts run for the caller. POST /run or /stop on it to choose, as a collaborator does from the card page.
eventsGET/api/stacks/{uid}/events is a server-sent event stream for anyone who may open the stack: ready when it opens, then card {"cardId"} each time a card's name or text is saved. Read the card again to see what changed; a comment every 25 seconds keeps it open.
archivePOSTPut a stack or card aside: it leaves the list but stays whole and openable. A stack keeps at least one card, and protected cards refuse.
unarchivePOSTBring an archived stack back into the list, or an archived card back to the end of its stack.
restorePOSTBring something back: a stack out of the trash (answers the stack), or a card's saved version from its history (answers the card; the state it replaces is kept as a version first).
purgeDELETEDelete a trashed stack for good, with its cards and parts (and any recordings kept from before). There is no undo.
emptyTrashDELETEDelete every stack in the caller's trash for good. Answers {"purged"} with how many went.
leavePOSTGive up access to a stack someone shared with the caller. The owner cannot leave their own stack.
unlockPOSTEnter a stack's password. A stack whose password still stands between the caller and its cards is listed (in stacks for an owner who protected it from themselves, in sharedStacks for a collaborator) with "locked": true, only what the web's stack list shows (uid, name, description, icon, cardCount, dates, and role when shared), and just this link beside self; GET on it answers 423 with {"password": "…"} and the same link, and everything under it answers 404 as before. POST {"password"}: 200 with the stack (the edition the caller reads) when it matches, 403 {"password": "Incorrect password."} when it does not, 429 after ten tries in ten minutes, 404 for anyone the stack is not shared with or a stack with no password. The unlock belongs to the bearer token that sent it (or to the session, for a cookie client): another device asks again, signing out forgets it, and changing the password locks the stack again. A web session started from the token (/api/account/session) inherits its unlocks. DELETE forgets the unlock (204).
replacePOSTFind and replace across a stack: POST {"find","replacement"} to change every exact, case-sensitive occurrence in card names and field text. Answers {"replaced"} with how many values changed; a shared background field counts once. Editors only.
transferPOSTMove the card to another stack you can edit: {"destination":"STACK-UID"}, with "placement":"start" to land first rather than last, and "copy":true to send an independent copy and keep the original. The card keeps its text; its scripts follow the destination's script review. With "after":CARD-ID it lands right after that card of the destination, which may then be the card's own stack — a paste, where copy is a copied card and its absence a cut one. Answers the card where it landed.
mergeCardsPOSTMerge cards: {"cards":[ID, ID, …]} names two or more cards of the stack, which become one card where the first stood, in stack order, holding their parts, text and pictures. Cards marked Can't delete are refused. Answers the merged card.
duplicatePOSTMake a new card right after this one. With no body it copies the name, script, author notes, date, parts, and field text (not the mark); {"layoutOnly":true} copies just the parts and their layout, with blank text. Answers 201 with the new card.
resetDELETEReturn the home screen to its defaults. Docked stacks stay docked.
pinPOSTDock the stack identified by the uid URI template parameter.
unpinPOSTRemove the stack identified by the uid URI template parameter from the dock.
reorderPOSTReplace one pinned or unpinned sibling group's custom order. POST {"stackUids":[...]}; supply every stack in that group exactly once.
patch-homePATCHChange individual Home preferences with JSON, for example {"title":"AI desk"}. Omitted or null title, layout and sortBy are preserved. An omitted startStackUid is preserved; explicit null clears it. Returns the updated Home resource.
groupPOSTPlace one stack inside another. POST {"stackUid","folderUid"}. Both must be the caller's stacks, have the same dock state, and cannot form a cycle.
moveOutPOSTPromote the stack identified by the uid URI template parameter one group level.
dealerGETWhat Dealer (the Cmd/Ctrl+K launcher) remembers for you: favorites (pinned command ids in order), commands (each id with uses and lastUsedAt, most lately used first) and recent stacks to jump back into.
dealer-usePOSTCount one choice of a command: {"id":"stack.find"}. Answers 204, or 400 for an id Dealer could not have made.
dealer-favoritesPUTReplace the pinned favorites with {"ids":["stack.find","app.trash"]}: up to eight different command ids, in order. Answers the dealer.
dealer-commandsGETThe app-wide commands Dealer offers you, in order: each with the web launcher's id (so favorites and uses are shared), a stable action verb such as new-stack, trash or find-stack for an app to map to its own screen, title, command (a longer phrase the launcher also finds it by), description, keywords, an icon character, the web href to open when an app has no screen for the action (null for the two that are a choice of stack), and destructive, true for a command that deletes or discards, which a launcher runs only on an explicit choice (never by voice).
dealer-stack-featuresGETEverything you may do with a stack, for Dealer to find by name or by what it does: ?stack={uid}, optionally &card={n}. Returns {"features":[…]}, each with the web launcher's id (such as stack.storyboard), an action verb for an app to map to its own screen, title, description, keywords, an icon character, needs (read, edit or own, already applied to you) and the web href that does it. Without card, hrefs keep {card} where the card number goes.
dealer-recent-cardsGETThe cards you opened lately, across stacks, newest first (up to 24): {"cards":[…]}, each with its stack's uid, name and icon, and the card's cardId, cardNumber, cardLabel and when. Cards in a stack whose role hides some of it are left out.
uploadAudioPOSTAdd audio to a card for its EPUB chapter: multipart/form-data with file (MP3 or M4A, 25 MB or less), optional title and durationMs. Answers 201 with the clip; 400 with {"file": "…"} when refused. Up to fifty per card.