@widgetic/cli 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/cli.js +39 -7
- package/dist/commands/completion.js +2 -0
- package/dist/commands/doctor.js +2 -0
- package/dist/commands/schema.js +2 -0
- package/dist/constants.js +6 -0
- package/dist/credentials.js +2 -2
- package/dist/help.js +174 -0
- package/dist/http.js +2 -2
- package/dist/mcp/session.js +2 -1
- package/dist/overlay.js +24 -0
- package/dist/rest/register.js +9 -4
- package/generated/contracts.json +1 -1
- package/generated/mcp-manifest.json +1 -1
- package/generated/rest-manifest.json +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"format":1,"apiUrl":"https://api.widgetic.com/v1","groups":[{"name":"canvases","title":"Canvases","description":"The Canvases operations of the Widgetic API.","hidden":false,"commands":[{"name":"create-canvas","aliases":["create"],"operationId":"createCanvas","method":"POST","path":"/canvases","server":"api","summary":"Create Canvas","description":"Creates a new canvas with the specified properties.\n\nTo make the canvas for one of your managed users, send `managedUserId` or `managedUserExternalId`. The canvas\nstays in your account and is yours to edit; the tag lets you list one user's canvases with `getAllCanvases`.\nThe managed user must be yours and active: one that is not yours, or does not exist, is answered with 404 and\n`MANAGED_USER_NOT_FOUND`, and one that is switched off with 409 and `MANAGED_USER_INACTIVE`. The tag is set\nhere and does not change afterwards. Called with an editor session, the canvas is always made for the\nsession's managed user.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["persistenceKey"],"properties":{"persistenceKey":{"type":"string","description":"Key used for persistent storage and reference","example":"canvas_xyz123"},"canvasContent":{"type":"object","description":"JSON content of the canvas, containing layout and configuration","example":{"version":1,"elements":[],"settings":{}}},"primaryWidgetId":{"type":["string","null"],"format":"uuid","description":"Optional primary widget id (usually set after widget create)"},"managedUserId":{"type":["string","null"],"format":"uuid","description":"Make the canvas for this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both. The tag is set here and does not change afterwards."},"managedUserExternalId":{"type":["string","null"],"maxLength":255,"description":"Make the canvas for the managed user you registered with this `externalId` (your own identifier for the user). Send this or `managedUserId`, not both."},"projectId":{"type":["string","null"],"format":"uuid","description":"Project this canvas belongs to. Empty canvases show on that project's list."}},"example":{"persistenceKey":"canvas_xyz123","canvasContent":{"version":1,"elements":[],"settings":{}},"primaryWidgetId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","managedUserId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","managedUserExternalId":"Example managedUserExternalId","projectId":"3fa85f64-5717-4562-b3fc-2c963f66afa6"}},"flags":[{"flag":"persistence-key","name":"persistenceKey","type":"string","required":true,"description":"Key used for persistent storage and reference","property":"persistenceKey"},{"flag":"primary-widget-id","name":"primaryWidgetId","type":"string","required":false,"description":"Optional primary widget id (usually set after widget create)","format":"uuid","property":"primaryWidgetId"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"Make the canvas for this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both. The tag is set here and does not change afterwards.","format":"uuid","property":"managedUserId"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"Make the canvas for the managed user you registered with this `externalId` (your own identifier for the user). Send this or `managedUserId`, not both.","maxLength":255,"property":"managedUserExternalId"},{"flag":"project-id","name":"projectId","type":"string","required":false,"description":"Project this canvas belongs to. Empty canvases show on that project's list.","format":"uuid","property":"projectId"}]}},{"name":"delete-canvas","aliases":["delete"],"operationId":"deleteCanvas","method":"DELETE","path":"/canvases/{id}","server":"api","summary":"Delete Canvas","description":"Deletes a canvas together with every widget on it.\n\nCalled with an editor session, only a canvas whose widgets were all made for the session's managed user can\nbe deleted; one that also holds somebody else's widgets is refused with 409 and `CANVAS_HAS_OTHER_WIDGETS`,\nand nothing is deleted. Delete such a canvas with your API key.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the canvas","format":"uuid","in":"path"}]},{"name":"get-all-canvases","aliases":["list"],"operationId":"getAllCanvases","method":"GET","path":"/canvases","server":"api","summary":"Get All Canvases","description":"Retrieves all canvases with pagination support and filtering options.\n\nPass `managedUserId` (Widgetic's id for one of your managed users) or `managedUserExternalId` (your own\nidentifier for the user) to list only the canvases made for that managed user. The canvases of your other\nmanaged users, and your own, are left out. A managed user that is not yours, or does not exist, is answered\nwith 404 and `MANAGED_USER_NOT_FOUND`. Called with an editor session, the list is always the session's\nmanaged user's.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"page","name":"page","type":"integer","required":false,"description":"Page number for pagination","default":1,"minimum":1,"in":"query"},{"flag":"size","name":"size","type":"integer","required":false,"description":"Number of items per page","default":20,"minimum":1,"maximum":100,"in":"query"},{"flag":"sort","name":"sort","type":"string","required":false,"description":"Sort field and direction (e.g., \"created_at:desc\")","default":"created_at:desc","in":"query"},{"flag":"created-after","name":"createdAfter","type":"string","required":false,"description":"Filter canvases created after the specified date (ISO 8601 format)","format":"date-time","in":"query"},{"flag":"created-before","name":"createdBefore","type":"string","required":false,"description":"Filter canvases created before the specified date (ISO 8601 format)","format":"date-time","in":"query"},{"flag":"updated-after","name":"updatedAfter","type":"string","required":false,"description":"Filter canvases updated after the specified date (ISO 8601 format)","format":"date-time","in":"query"},{"flag":"updated-before","name":"updatedBefore","type":"string","required":false,"description":"Filter canvases updated before the specified date (ISO 8601 format)","format":"date-time","in":"query"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"List only the canvases made for this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both.","format":"uuid","in":"query"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"List only the canvases made for the managed user you registered with this `externalId` (your own identifier for the user). Send this or `managedUserId`, not both.","maxLength":255,"in":"query"}]},{"name":"get-canvas","aliases":["get"],"operationId":"getCanvas","method":"GET","path":"/canvases/{id}","server":"api","summary":"Get Canvas","description":"Retrieves a specific canvas by ID.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the canvas","format":"uuid","in":"path"}]},{"name":"update-canvas","aliases":["update"],"operationId":"updateCanvas","method":"PUT","path":"/canvases/{id}","server":"api","summary":"Update Canvas","description":"Updates an existing canvas with the specified properties.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the canvas to update","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","properties":{"persistenceKey":{"type":"string","description":"Updated key used for persistent storage and reference","example":"updated_canvas_xyz123"},"canvasContent":{"type":"object","description":"Updated JSON content of the canvas","example":{"version":2,"elements":[{"id":"elem1"}],"settings":{"theme":"dark"}}},"primaryWidgetId":{"type":["string","null"],"format":"uuid","description":"Primary widget for list/deep-link; omit to leave unchanged"}},"example":{"persistenceKey":"updated_canvas_xyz123","canvasContent":{"version":2,"elements":[{"id":"elem1"}],"settings":{"theme":"dark"}},"primaryWidgetId":"3fa85f64-5717-4562-b3fc-2c963f66afa6"}},"flags":[{"flag":"persistence-key","name":"persistenceKey","type":"string","required":false,"description":"Updated key used for persistent storage and reference","property":"persistenceKey"},{"flag":"primary-widget-id","name":"primaryWidgetId","type":"string","required":false,"description":"Primary widget for list/deep-link; omit to leave unchanged","format":"uuid","property":"primaryWidgetId"}]}}]},{"name":"compositions","title":"Compositions","description":"The Compositions operations of the Widgetic API.","hidden":false,"commands":[{"name":"add-ai-answers-knowledge-document","aliases":[],"operationId":"addAiAnswersKnowledgeDocument","method":"POST","path":"/compositions/{id}/adapters/ai-answers/knowledge/documents","server":"api","summary":"Add a document for a widget's AI to answer from","description":"Gives the widget's AI a document to answer visitors' questions from. Send the title and the plain text. The call\nreturns once the document is ready to be used, which takes a few seconds, longer for a long text.\n\n**How it is used.** The text is cleaned, cut into short passages and each passage is turned into numbers (an\nembedding) so the widget can find the passages nearest to a question. When a visitor asks, the nearest passages and\nthe question are shown to an AI model, which writes the answer from them and from nothing else; if the documents\nsay nothing about the question the visitor is told so, and the owner is not charged for that question. The titles of\nthe documents an answer used are shown to the visitor as its sources.\n\n**What leaves Widgetic.** The text of a document is sent to OpenAI to be embedded when it is added, and the\npassages used for a question are sent to the AI model that writes the answer. Say so in your privacy policy.\n\n**Treat every document as public.** An answer can quote or paraphrase anything a document says, to any visitor of\nthe widget. Do not add passwords, keys, personal data or anything that must stay private.\n\n**Limits.** A widget holds up to 20 documents, each up to 100000 characters, and up to 400000 characters in all\n(`getAiAnswersKnowledge` reports the current numbers). This call takes text. To add a text file or a picture of a\ndocument (a PDF too, once PDFs are read) without turning it into text yourself, use `addAiAnswersKnowledgeFile`. Adding a document costs no\ncredits. The same text cannot be added twice to one widget (409).\n\nThe caller must be allowed to change the widget. An API key needs the `content:write` scope. Adds are limited to ten\nin ten minutes per person. A partner's customer in an editor session is one person of its own: its adds are not\ncounted with the partner's other customers'.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The widget (composition) to add the document to","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["title","text"],"additionalProperties":false,"properties":{"title":{"type":"string","maxLength":120,"description":"What the document is called, on one line. Visitors see it as the source of an answer. Line breaks and invisible characters are removed","example":"Shipping and returns"},"text":{"type":"string","maxLength":100000,"description":"The text of the document, as plain text. It is cleaned (line endings are made the same, invisible characters and surplus blank lines are removed) and then counted: up to 100000 characters. Markup is not interpreted, so paste the words, not a web page","example":"Orders ship within 2 business days. Returns are accepted for 30 days after delivery, in the original packaging."}},"example":{"title":"Shipping and returns","text":"Orders ship within 2 business days. Returns are accepted for 30 days after delivery, in the original packaging."}},"flags":[{"flag":"title","name":"title","type":"string","required":true,"description":"What the document is called, on one line. Visitors see it as the source of an answer. Line breaks and invisible characters are removed","maxLength":120,"property":"title"},{"flag":"text","name":"text","type":"string","required":true,"description":"The text of the document, as plain text. It is cleaned (line endings are made the same, invisible characters and surplus blank lines are removed) and then counted: up to 100000 characters. Markup is not interpreted, so paste the words, not a web page","maxLength":100000,"property":"text"}]}},{"name":"add-ai-answers-knowledge-file","aliases":[],"operationId":"addAiAnswersKnowledgeFile","method":"POST","path":"/compositions/{id}/adapters/ai-answers/knowledge/documents/file","server":"api","summary":"Add a text file or a picture of a document (a PDF once PDFs are read) for a widget's AI to answer from","description":"Gives the widget's AI a document made from a file. Send a `multipart/form-data` request with the file in the field\nnamed `file` and, if you like, a `title`. The file is turned into text and then added exactly as\n`addAiAnswersKnowledgeDocument` adds text: the same cleaning, the same limits, the same refusal of a copy of a\ndocument the widget already has. The call returns once the document is ready to be used, which takes a few seconds\n(a PDF is read first, so it takes longer).\n\n**PDFs are not read yet.** For now a PDF is refused with 415, in a sentence that says what to do instead: paste the\ntext, upload it as a text file, or upload its pages as pictures. What this description says about a PDF is what will\nhold from the day PDFs are read.\n\n**Which files.**\n- A **PDF** (not read yet, see above), up to 10 MB and 300 pages. Its text is read and its paragraphs are put back\n together. A PDF that is only pictures of pages (a scan) has no text to read: upload the pages as pictures instead.\n A PDF locked with a password is refused.\n- A **text file**: `.txt`, `.text`, `.md`, `.markdown` or `.csv`, in UTF-8, UTF-16 or Windows-1252.\n- A **picture of a document**: PNG, JPEG or WebP, up to 4 MB. The text in it is read with the same text recognition\n as the `ocr` adapter.\n- Word and other office files are not read yet: save the file as a PDF and upload that.\n\nWhat a file is comes from what is in it, not from its name or the type it is sent as. A file that is none of these is\nrefused with 415.\n\n**The title.** Left out, the name of the file (without its extension) is the title. Visitors see the title as the\nsource of an answer, so send one that means something to them.\n\n**The file is not kept.** Only its text, cut into passages, is kept, and `getAiAnswersKnowledge` never returns it.\n\n**What leaves Widgetic.** As for text, the words of the document are sent to OpenAI to be embedded when it is added,\nand the passages used for a question are sent to the AI model that writes the answer. A picture is also sent to the\ntext recognition service. A PDF is read on Widgetic's own servers and is not sent anywhere to be read. Say so in your\nprivacy policy.\n\n**Treat every document as public.** An answer can quote or paraphrase anything a document says, to any visitor of\nthe widget. Do not add passwords, keys, personal data or anything that must stay private.\n\n**Limits.** The widget's limits are those of `addAiAnswersKnowledgeDocument` (up to 20 documents, each up to 100000\ncharacters of text, and up to 400000 characters in all; `getAiAnswersKnowledge` reports the current numbers), and\nthe file is at most 10 MB. Adding a document costs no credits. The same text cannot be added twice to one widget\n(409).\n\nThe caller must be allowed to change the widget. An API key needs the `content:write` scope. Adds, from text or from\na file, share one limit of ten in ten minutes per person. A partner's customer in an editor session is one person\nof its own: its adds are not counted with the partner's other customers'.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The widget (composition) to add the document to","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"multipart","contentType":"multipart/form-data","schema":{"type":"object","required":["file"],"additionalProperties":false,"properties":{"file":{"type":"string","format":"binary","description":"The file, which is a text file or a picture of a document (a PDF is not read yet). One file only"},"title":{"type":"string","maxLength":120,"description":"What the document is called, on one line. Visitors see it as the source of an answer. Left out, the name of the file stands in for it","example":"Shipping and returns"}}},"flags":[{"flag":"file","name":"file","type":"file","required":true,"description":"The file, which is a text file or a picture of a document (a PDF is not read yet). One file only","property":"file"},{"flag":"title","name":"title","type":"string","required":false,"description":"What the document is called, on one line. Visitors see it as the source of an answer. Left out, the name of the file stands in for it","maxLength":120,"property":"title"}]}},{"name":"check-composition-access","aliases":[],"operationId":"checkCompositionAccess","method":"GET","path":"/compositions/{id}/access","server":"api","summary":"Check Composition Access","description":"Checks if the authenticated user can access a specific composition based on workspace context and permissions.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"Composition ID","format":"uuid","in":"path"}]},{"name":"create-composition","aliases":["create"],"operationId":"createComposition","method":"POST","path":"/compositions","server":"api","summary":"Create Composition","description":"Creates a new composition.\n\nTo make the composition for one of your managed users, send `managedUserId` or `managedUserExternalId`.\nThe composition stays in your account and is yours to edit; the tag lets you list one user's compositions\nwith `getAllCompositions`. The managed user must be yours and active: one that is not yours, or does not\nexist, is answered with 404 and `MANAGED_USER_NOT_FOUND`, and one that is switched off with 409 and\n`MANAGED_USER_INACTIVE`. The tag is set here and does not change afterwards. A copy made with\n`duplicateComposition` keeps the tag of the original.\n\nAn editor session with the `creator` surface may call this to start a composition of a widget it built:\nit sends only `name`, `widgetId` (a widget made for the session's managed user) and `content`, and the\ncomposition is always made for that managed user.\n\nSend `databaseSchema` to give the composition tables of its own for what visitors send it (the shape, the\nallowed types and the limits are described on the field). A `databaseSchema` that cannot be used is refused\nbefore anything is created, with `400` and `INVALID_DATABASE_SCHEMA`. If the composition is created but its\ntables cannot be made just now, the answer is still `201` and `meta.warnings` says so.\n\nLeave `databaseSchema` out and the composition gets the tables its widget declares in its own source\n(`src/lib/meta/database.json`), if it declares any: they are made the same way, in a schema of the\ncomposition's own. A widget that keeps nothing leaves the composition without tables. If the widget's file\ncannot be used, or its tables cannot be made just now, the answer is still `201` and `meta.warnings` says so.\nThe widget is read as it was when it was last published (as it is now, when it never was).\n\nThe `Idempotency-Key` header is required (`400` without it). Send the same key when you retry, so one\ncomposition is created, not two.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","properties":{"name":{"type":"string","description":"Name of the composition","example":"My New Composition"},"widgetId":{"type":"string","format":"uuid","description":"ID of the widget this composition is based on","example":"123e4567-e89b-12d3-a456-426614174002"},"creatorId":{"type":"string","format":"uuid","description":"ID of the user creating the composition","example":"123e4567-e89b-12d3-a456-426614174001"},"content":{"type":"object","description":"Content data for the composition","example":{"key":"value"}},"status":{"type":"string","description":"Initial status of the composition","default":"draft","example":"draft"},"embedSettings":{"type":"object","description":"Settings for embedding the composition","example":{"width":"100%","height":"500px"}},"design":{"type":"object","description":"Design settings for the composition","example":{"theme":"light","colors":{"primary":"#0066ff"}}},"databaseSchema":{"type":"object","description":"The tables this composition keeps what visitors send it in (votes, ratings, answers). They are made when\nthe composition is created, in a schema of their own that no other composition can reach: in Widgetic's\ndatabase, or in your own Neon database when you have set one with `setPartnerDataDatabase`. They are\ndeleted with the composition.\n\nThe shape is `{ \"tables\": [ ... ] }`. A table has a `name`, a list of `columns` and, optionally, a list of\n`indexes`, and optionally `countable`. A column has a `name` and a `type`, and optionally `primaryKey`,\n`notNull`, `unique`, `default` and `readable`. An index has a `name` and the `columns` it covers (1 to 8),\nand optionally `unique`. Any other key is refused, except `source` (see below).\n\n`countable` and `readable` are the read policy of the composition's own page: nothing the visitors sent\ncan be read back by the page until you say so. `countable: true` on a table lets the widget show how many\nrows it has; `readable: true` on a column lets it show or total that column's values (a tally of votes,\nan average rating). Mark only what is safe to show to anyone who opens the page. A column whose name\nholds `email`, `phone`, `address`, `token`, `password`, `secret`, `ip` or the like, and a `json`, `jsonb`\nor `uuid` column, cannot be `readable`.\n\n- Names (tables, columns, indexes) use lower-case letters, digits and underscores, start with a letter and\n are at most 63 characters long. A name that starts with `_` or `pg_` is reserved.\n- `type` is one of `text`, `smallint`, `integer` (or `int`), `bigint`, `numeric`, `real`,\n `double precision`, `boolean` (or `bool`), `date`, `timestamp`, `timestamptz` (or\n `timestamp with time zone`), `uuid`, `json`, `jsonb`, `serial`, `bigserial`.\n- `default` is a number, `true` or `false`, a text of at most 500 characters (no backslash), a JSON value\n for a `json` or `jsonb` column, or one of `now()`, `current_timestamp` and `current_date` (date and\n timestamp columns) and `gen_random_uuid()` (a `uuid` column). A `serial` column numbers itself and has\n no default.\n- A composition declares at most 20 tables, with at most 64 columns and 10 indexes each.\n\nA declaration that cannot be used is refused with `400` and `INVALID_DATABASE_SCHEMA`;\n`error.details.problems` lists what to change. Tables that already exist are never changed or dropped:\nsending more tables later, when you update the composition, adds the ones that are missing. When the\ncomposition is saved but its tables cannot be made just now, the answer says so in `meta.warnings`: send\nthe same `databaseSchema` again to retry.\n\nLeave it out and the composition gets the tables its widget declares in its own source\n(`src/lib/meta/database.json`), if any. Send `{ \"tables\": [] }` to give the composition no tables and\nkeep it from getting its widget's.\n\nA composition whose tables were made from its widget's own file reads back with `\"source\": \"widget\"` next\nto `tables`. When the widget is published again, a table, a field or an index its file gained is added to\nthem; nothing that exists is changed or dropped, and what a page may read does not change. Send `source`\nback as it was and it stays. Leave it out and the tables are yours: no widget's file adds anything to\nthem. It is the only value `source` can have.\n","example":{"tables":[{"name":"votes","columns":[{"name":"id","type":"serial","primaryKey":true},{"name":"choice","type":"text","notNull":true},{"name":"created_at","type":"timestamptz","default":"now()"}],"indexes":[{"name":"votes_choice_idx","columns":["choice"]}]}]}},"projectId":{"type":"string","format":"uuid","description":"ID of the project this composition belongs to","example":"123e4567-e89b-12d3-a456-426614174004"},"managedUserId":{"type":"string","format":"uuid","description":"Make the composition for one of your managed users, by Widgetic's id for the user. Send this or `managedUserExternalId`, not both. The managed user must be yours and active. The composition stays in your account; the tag lets you list one user's compositions and keeps it out of the others' lists. It is set here and does not change afterwards.","example":"b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e"},"managedUserExternalId":{"type":"string","maxLength":255,"description":"The same, by your own identifier for the user, the `externalId` you gave when you created the managed user (answers show it as `externalId`). Send this or `managedUserId`, not both.","example":"user-8841"}},"required":["name","widgetId"],"example":{"name":"My New Composition","widgetId":"123e4567-e89b-12d3-a456-426614174002","creatorId":"123e4567-e89b-12d3-a456-426614174001","content":{"key":"value"},"status":"draft","embedSettings":{"width":"100%","height":"500px"},"design":{"theme":"light","colors":{"primary":"#0066ff"}},"databaseSchema":{"tables":[{"name":"votes","columns":[{"name":"id","type":"serial","primaryKey":true},{"name":"choice","type":"text","notNull":true},{"name":"created_at","type":"timestamptz","default":"now()"}],"indexes":[{"name":"votes_choice_idx","columns":["choice"]}]}]},"projectId":"123e4567-e89b-12d3-a456-426614174004","managedUserId":"b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e","managedUserExternalId":"user-8841"}},"flags":[{"flag":"name","name":"name","type":"string","required":true,"description":"Name of the composition","property":"name"},{"flag":"widget-id","name":"widgetId","type":"string","required":true,"description":"ID of the widget this composition is based on","format":"uuid","property":"widgetId"},{"flag":"creator-id","name":"creatorId","type":"string","required":false,"description":"ID of the user creating the composition","format":"uuid","property":"creatorId"},{"flag":"status","name":"status","type":"string","required":false,"description":"Initial status of the composition","default":"draft","property":"status"},{"flag":"project-id","name":"projectId","type":"string","required":false,"description":"ID of the project this composition belongs to","format":"uuid","property":"projectId"},{"flag":"body-managed-user-id","name":"managedUserId","type":"string","required":false,"description":"Make the composition for one of your managed users, by Widgetic's id for the user. Send this or `managedUserExternalId`, not both. The managed user must be yours and active. The composition stays in your account; the tag lets you list one user's compositions and keeps it out of the others' lists. It is set here and does not change afterwards.","format":"uuid","property":"managedUserId"},{"flag":"body-managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"The same, by your own identifier for the user, the `externalId` you gave when you created the managed user (answers show it as `externalId`). Send this or `managedUserId`, not both.","maxLength":255,"property":"managedUserExternalId"}]}},{"name":"create-composition-content-item","aliases":[],"operationId":"createCompositionContentItem","method":"POST","path":"/compositions/{id}/content/{list}/items","server":"api","summary":"Add a content list item","description":"Creates one item in a widget content list without replacing the whole list. Item field keys are widget data (camelCase or snake_case are kept as sent). Partners use this to keep galleries in sync.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"list","name":"list","type":"string","required":true,"description":"List name from the content schema (`contentItems`) or `items`","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"},{"flag":"if-match","name":"If-Match","type":"string","required":false,"description":"Optional content version from ETag","in":"header"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","additionalProperties":true,"properties":{"position":{"type":"integer","minimum":0}}},"flags":[{"flag":"position","name":"position","type":"integer","required":false,"description":"","minimum":0,"property":"position"}]}},{"name":"create-composition-database-record","aliases":[],"operationId":"createCompositionDatabaseRecord","method":"POST","path":"/compositions/{id}/database/{table}","server":"api","summary":"Create Record In Composition Database","description":"Creates a new record in one of the composition's tables. Only the composition's own tables (made from its `databaseSchema` or from the tables its widget declares) can be reached; any other table name answers `404`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"Name of the database table","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object"},"flags":[]}},{"name":"delete-composition","aliases":["delete"],"operationId":"deleteComposition","method":"DELETE","path":"/compositions/{id}","server":"api","summary":"Delete Composition","description":"Deletes a specific composition, and the data it keeps (the tables it declared and what visitors left in them).","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The ID of the composition to delete","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"delete-composition-content-item","aliases":[],"operationId":"deleteCompositionContentItem","method":"DELETE","path":"/compositions/{id}/content/{list}/items/{itemId}","server":"api","summary":"Delete a content list item","description":"","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"list","name":"list","type":"string","required":true,"description":"","in":"path"},{"flag":"item-id","name":"itemId","type":"string","required":true,"description":"","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"},{"flag":"if-match","name":"If-Match","type":"string","required":false,"description":"","in":"header"}]},{"name":"delete-composition-data-source","aliases":[],"operationId":"deleteCompositionDataSource","method":"DELETE","path":"/compositions/{id}/data-source","server":"api","summary":"Remove the composition data source binding","description":"Removes the binding. Asking again, or asking for a composition that had none, is not an error: the composition has no data source afterwards, and that is all `removed: true` says.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"delete-composition-database-record","aliases":[],"operationId":"deleteCompositionDatabaseRecord","method":"DELETE","path":"/compositions/{id}/database/{table}/{recordId}","server":"api","summary":"Delete Record From Composition Database","description":"Deletes a specific record from one of the composition's tables. Only the composition's own tables (made from its `databaseSchema` or from the tables its widget declares) can be reached; any other table name answers `404`.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"Name of the database table","in":"path"},{"flag":"record-id","name":"recordId","type":"string","required":true,"description":"ID of the record to delete","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"delete-composition-entry-items","aliases":[],"operationId":"deleteCompositionEntryItems","method":"DELETE","path":"/compositions/{id}/entries/{table}/items","server":"api","summary":"Delete composition entry items by id or host context","description":"Delete by comma-separated ids (1–100) or by context filters (not both). Rate limit 30 requests per minute.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"","maxLength":63,"in":"path"},{"flag":"ids","name":"ids","type":"string","required":false,"description":"Comma-separated entry ids (1–100)","in":"query"},{"flag":"context","name":"context","type":"pairs","required":false,"description":"Host context filters as `context[key]=value` (max 5 keys, counting dotted `context.key` too). Use `column.<name>=value` for columns whose names collide with reserved parameters.","in":"query"},{"flag":"filter","name":"filter","type":"pairs","required":false,"description":"Column filters as `filter[columnName]=value` (same meaning as bare `columnName=value` or `column.columnName=value`; max 10 column filters counting all spellings). A plain `filter=value` still filters a column named `filter`.","in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"describe-composition-entries","aliases":[],"operationId":"describeCompositionEntries","method":"GET","path":"/compositions/{id}/entries","server":"api","summary":"Describe What a Composition Keeps","description":"Names the tables a composition's widget keeps entries in (the votes, ratings, answers or sign-ups its\nvisitors left), their columns, and how many entries each holds. Call it first to learn the table and\ncolumn names that `readCompositionEntries` takes.\n\nA widget that only shows content keeps nothing of its own: the answer is `hasData: false` with no tables,\nand that is not an error. A table that could not be counted has `rows: null`, which is not zero.\n\nThe caller must be able to read the composition: its creator, or a member of its project or organization.\nAn editor session cannot call this route.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"duplicate-composition","aliases":[],"operationId":"duplicateComposition","method":"POST","path":"/compositions/{id}/duplicate","server":"api","summary":"Duplicate Composition","description":"Creates a duplicate of an existing composition for the authenticated user.\n\nThe duplicate is made after the answer: the call returns `202` with an `operationId`. Ask\n`GET /operations/{id}` for that operation until its `status` is `completed`; the new composition is\nnamed in `metadata.newCompositionId`. A problem with the duplicate's database (for example, tables that\ncould not be made just now) is listed in `metadata.warnings`, which is absent when there is none.\n\nThe `Idempotency-Key` header is required (`400` without it). Send the same key when you retry, so one\nduplicate is made, not two.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition to duplicate","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":false,"kind":"json","contentType":"application/json","schema":{"type":"object","properties":{"name":{"type":"string","description":"Optional new name for the duplicate (defaults to \"Copy of [original name]\")","example":"My Duplicated Composition"},"includeDatabase":{"type":"boolean","description":"Whether the duplicate gets the tables the original declares (`databaseSchema`), empty and in a schema\nof their own (default false). What visitors left in the original is never copied, and the duplicate\nnever shares the original's data.\n","default":false}}},"flags":[{"flag":"name","name":"name","type":"string","required":false,"description":"Optional new name for the duplicate (defaults to \"Copy of [original name]\")","property":"name"},{"flag":"include-database","name":"includeDatabase","type":"boolean","required":false,"description":"Whether the duplicate gets the tables the original declares (`databaseSchema`), empty and in a schema\nof their own (default false). What visitors left in the original is never copied, and the duplicate\nnever shares the original's data.","default":false,"property":"includeDatabase"}]}},{"name":"export-composition-entry-items","aliases":[],"operationId":"exportCompositionEntryItems","method":"GET","path":"/compositions/{id}/entries/{table}/export","server":"api","summary":"Export composition entry items","description":"Streams up to 50,000 matching rows in batches of 1,000. Tables without cursor paging export at most one batch; when more rows exist the response sets `X-Widgetic-Export-Truncated: true`. Rate limit 20 requests per minute.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"","maxLength":63,"in":"path"},{"flag":"format","name":"format","type":"string","required":false,"description":"","enum":["csv","json"],"default":"csv","in":"query"},{"flag":"order","name":"order","type":"string","required":false,"description":"`desc` (the default) lists the newest entry first, `asc` the oldest. Entries are sorted by their time, then\nby their id (compared as text, byte by byte) in the same direction, so the order of two entries made at the\nsame instant is always the same; an entry with no time counts as the oldest. A table with no time column is\nsorted by its id. The managed-user feed sorts by time, then by composition id, table and id.","enum":["asc","desc"],"default":"desc","in":"query"},{"flag":"since","name":"since","type":"string","required":false,"description":"Only entries made at this time or later: `since` is INCLUSIVE (an entry made at exactly `since` is\nreturned). A date and time with an offset, such as `2026-10-01T00:00:00Z`; anything else answers 400\n`INVALID_SINCE`. An entry with no time never matches, and a table with no time column answers 400\n`ENTRY_TABLE_HAS_NO_TIME`.","format":"date-time","in":"query"},{"flag":"until","name":"until","type":"string","required":false,"description":"Only entries made before this time: `until` is EXCLUSIVE (an entry made at exactly `until` is NOT\nreturned), so `since=A&until=B` followed by `since=B&until=C` counts every entry once. Same form and same\nrefusals as `since` (400 `INVALID_UNTIL`).","format":"date-time","in":"query"},{"flag":"context","name":"context","type":"pairs","required":false,"description":"Host context filters as `context[key]=value` (max 5 keys, counting dotted `context.key` too). Use `column.<name>=value` for columns whose names collide with reserved parameters.","in":"query"},{"flag":"filter","name":"filter","type":"pairs","required":false,"description":"Column filters as `filter[columnName]=value` (same meaning as bare `columnName=value` or `column.columnName=value`; max 10 column filters counting all spellings). A plain `filter=value` still filters a column named `filter`.","in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-ai-answers-knowledge","aliases":[],"operationId":"getAiAnswersKnowledge","method":"GET","path":"/compositions/{id}/adapters/ai-answers/knowledge","server":"api","summary":"See the documents a widget's AI answers from","description":"The `ai-answers` adapter lets a widget answer a visitor's question with text an AI model writes from documents the\nwidget's owner provides. This lists those documents, and how much room is left for more: `totals` carries the widget's\nlimits next to what it uses.\n\nThe list never contains what a document says, only which documents there are: their title, size and state. A\n`processing` document is not used to answer questions yet (or an add of it was interrupted: remove it and add it\nagain).\n\nAdd a document with `addAiAnswersKnowledgeDocument`; remove one with `removeAiAnswersKnowledgeDocument`.\n\nThe caller must be allowed to see the widget: its owner, a partner for a customer it manages, or a Widgetic\nadministrator. An API key needs the `content:read` scope.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The widget (composition) whose documents to list","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-all-compositions","aliases":["list"],"operationId":"getAllCompositions","method":"GET","path":"/compositions","server":"api","summary":"Get All Compositions","description":"Retrieves all compositions with pagination support.\n\nPass `managedUserId` (Widgetic's id for one of your managed users) or `managedUserExternalId` (your own\nidentifier for the user) to list only the compositions made for that managed user. The compositions of\nyour other managed users, and your own, are left out. It combines with the other filters and with paging.\nA managed user that is not yours, or does not exist, is answered with 404 and `MANAGED_USER_NOT_FOUND`.\n\nAn editor session with the `creator` surface may call this to list the compositions of ONE widget it\nbuilt: it must send `widgetId`, and the answer holds only the compositions made for the session's\nmanaged user.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"page","name":"page","type":"integer","required":false,"description":"Page number for pagination","default":1,"minimum":1,"in":"query"},{"flag":"size","name":"size","type":"integer","required":false,"description":"Number of items per page","default":20,"minimum":1,"maximum":100,"in":"query"},{"flag":"sort","name":"sort","type":"string","required":false,"description":"The order of the list, as `<field>:<asc|desc>`. The field is `createdAt`, `updatedAt`, `name` or `status`\n(the snake_case spelling, such as `created_at`, is read too). `asc` or `desc` alone orders by `createdAt`.\nDefault: `createdAt:desc`. Any other field is a 400.","in":"query"},{"flag":"widget-id","name":"widgetId","type":"string","required":false,"description":"Filter compositions by widget ID","format":"uuid","in":"query"},{"flag":"workspace","name":"workspace","type":"string","required":false,"description":"Filter by workspace","enum":["true","false"],"in":"query"},{"flag":"project-folder","name":"projectFolder","type":"string","required":false,"description":"Only the compositions in this project (the project's id).","format":"uuid","in":"query"},{"flag":"status","name":"status","type":"string","required":false,"description":"Only the compositions in this status. Without it, every status is listed.","enum":["draft","published","archived"],"in":"query"},{"flag":"query-managed-user-id","name":"managedUserId","type":"string","required":false,"description":"List only the compositions made for this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both. Send it as `managedUserId`; the snake_case spelling `managed_user_id` is refused with a 400, not ignored.","format":"uuid","in":"query"},{"flag":"query-managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"List only the compositions made for the managed user you registered with this `externalId` (your own identifier for the user). Send this or `managedUserId`, not both.","maxLength":255,"in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-by-id","aliases":["get"],"operationId":"getCompositionById","method":"GET","path":"/compositions/{id}","server":"api","summary":"Get Composition","description":"Retrieves a specific composition by its ID.\n\nThe answer carries `direct`, which says whether the widget the composition is made from is drawn straight\ninto your page (`true`) or inside a frame (`false`), or `null` while the widget has no published version.\nRead it before you go live: a widget drawn without a frame writes its entries from your own page, so the\npublic entry route looks at the website that page is on. While the composition's allowed list is empty (you\nhave not restricted the allowed websites), every website may write; once you list websites, only those may,\nbesides Widgetic's own sites, the visitor's own machine and your own platform. A widget in a frame writes from\nWidgetic's own frame and is accepted whatever the list says. The public JSON of the composition stays open to\nevery website.\n\nFor a composition a team shares (one filed under a project of an organization, or made in a Shopify store's\nworkspace) the answer also carries `lastEditedBy` and `lastEditedAt`: who made the latest change and when, so\nthat a teammate who opens it can be told \"Anna changed this at 14:32\". A composition in a personal project\nleaves both out, and so does an answer given to an editor session or to a call made for one managed user: only\na person is told who a teammate is.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"Composition ID","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-content","aliases":[],"operationId":"getCompositionContent","method":"GET","path":"/compositions/{id}/content","server":"api","summary":"Get Current Content","description":"Retrieves the current content of a composition","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-content-schema","aliases":[],"operationId":"getCompositionContentSchema","method":"GET","path":"/compositions/{id}/schema/content","server":"api","summary":"Get Content Schema","description":"Retrieves the content schema for a composition based on its widget type","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-data-source","aliases":[],"operationId":"getCompositionDataSource","method":"GET","path":"/compositions/{id}/data-source","server":"api","summary":"Read the composition data source binding","description":"The data source that is bound to the composition: `url` and `refreshSeconds`. The answer's `data` is `null` when none is bound, and also when what is stored does not pass the rule for a public https address (a binding written by another path, such as the general embed-settings update, is never shown to a page unless it passes the same checks as this route).","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-database-record","aliases":[],"operationId":"getCompositionDatabaseRecord","method":"GET","path":"/compositions/{id}/database/{table}/{recordId}","server":"api","summary":"Get Record From Composition Database","description":"Retrieves a specific record from one of the composition's tables. Only the composition's own tables (made from its `databaseSchema` or from the tables its widget declares) can be reached; any other table name answers `404`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"Name of the database table","in":"path"},{"flag":"record-id","name":"recordId","type":"string","required":true,"description":"ID of the record to retrieve","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-database-records","aliases":[],"operationId":"getCompositionDatabaseRecords","method":"GET","path":"/compositions/{id}/database/{table}","server":"api","summary":"Get All Records From Composition Database Table","description":"Retrieves all records from one of the composition's tables. Only the composition's own tables (made from its `databaseSchema` or from the tables its widget declares) can be reached; any other table name answers `404`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"Name of the database table","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-design","aliases":[],"operationId":"getCompositionDesign","method":"GET","path":"/compositions/{id}/design","server":"api","summary":"Get Composition Design","description":"Retrieves only the design field from a composition.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-design-schema","aliases":[],"operationId":"getCompositionDesignSchema","method":"GET","path":"/compositions/{id}/schema/design","server":"api","summary":"Get Design Schema","description":"Retrieves the design schema for a composition based on its widget type","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-embed-schema","aliases":[],"operationId":"getCompositionEmbedSchema","method":"GET","path":"/compositions/{id}/schema/embed","server":"api","summary":"Get Embed Schema","description":"Retrieves the definition of the composition's Embed tab: which settings exist, what each one holds, what it\ndefaults to, and when its control is shown. It is the third tab beside `schema/content` and `schema/design`,\nin the same format (`componentGroups` → `components` → `propertyGroups` → `properties`, each property with a\n`type`, a `defaultValue` and an `inputController`), so a client that draws those two tabs draws this one with\nthe same code.\n\nThe schema is the same for every composition (it is the one Widgetic's own Embed Editor draws). The\ncomposition id decides who may ask: the caller must be able to read the composition.\n\nReading and saving the values:\n- `GET /compositions/{id}/embed-settings` returns what the composition saved. A composition whose Embed tab\n was never saved has nothing to return; seed the form from `defaults`, which holds the `defaultValue` of\n every setting that declares one.\n- `PUT /compositions/{id}` with `embedSettings` saves them. The gateway stores `embedSettings` as given and\n does not check it against this schema; the schema is how a client knows what to put there.\n\nA property's `visibilityCondition` says when its control is shown: `condition` is a comparison of the\ncurrent values of the properties named in `dependsOn` (`useIframe === false`, `embedTarget === 'mixed' &&\nkeepDimensions === false`). While it is false the Embed Editor does not show the control.\n\nA colour picker with `showOpacityControls` saves its opacity next to the colour, under the key\n`<name>_opacity` as a number from 0 to 1 (`overlayBackgroundColor` → `overlayBackgroundColor_opacity`).\nIts `defaultTransparency` is a percentage and the saved value a fraction (50 → 0.5).","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-embed-settings","aliases":[],"operationId":"getCompositionEmbedSettings","method":"GET","path":"/compositions/{id}/embed-settings","server":"api","summary":"Get Composition Embed Settings","description":"Retrieves only the embedSettings field from a composition.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-preview","aliases":[],"operationId":"getCompositionPreview","method":"GET","path":"/compositions/{id}/preview","server":"api","summary":"Get Composition Preview","description":"Returns the address of the page an editor shows the composition in: the page of the widget's published\nversion on the CDN, with the widget and version it is. Load it in a frame, then send the frame what to show\n(the content, design and embed settings you read from the composition, and every change the user makes) with\n`postMessage`: `{ type: 'widgetic:update', design }`, `{ type: 'widgetic:update', content }` or\n`{ type: 'widgetic:update', contentItems }`.\n\nThe version is the one embeds show, so the editor previews what a visitor gets. The address does not carry\nthe composition id: an embed uses the id to fetch the saved composition itself, while an editor sends the\ncurrent state (unsaved changes included).\n\nA widget that has never been published has no page to show: the answer is 409 `WIDGET_NOT_PUBLISHED`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-versions","aliases":[],"operationId":"getCompositionVersions","method":"GET","path":"/compositions/{id}/versions","server":"api","summary":"Get Composition Versions","description":"Retrieves version history for a specific composition.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"Composition ID","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-compositions-batch","aliases":[],"operationId":"getCompositionsBatch","method":"POST","path":"/compositions/batch","server":"api","summary":"Get Compositions Batch","description":"Retrieves multiple compositions by their IDs in a single request.\nThis endpoint includes domain validation for embed contexts and is used by the embed SDK.\n\nEach composition is checked on its own. If its owner listed the websites it may appear on (the\nworkspace's domain list, or the `allowedDomains` of a partner the owner administers) and the request\ncomes from another website (the `Origin` header, or the host of `Referer` when there is no `Origin`),\nthat composition is left out of `compositions` and reported in `errors` as\n`Domain access denied: <reason>`; the others are still returned. A request that names no website, and\nan owner who listed nothing, are not restricted, and a page on the visitor's own machine (`localhost`,\n`127.0.0.1` or `[::1]`, on any port) is always allowed. So is the website where a partner integrates\nWidgetic, with every subdomain of it (`platformDomain` in the answer of `GET /partners/mine`), for the\ncompositions of that partner: a partner's list never has to name it.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["compositionIds"],"properties":{"compositionIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Array of composition IDs to retrieve","maxItems":50}}},"flags":[{"flag":"composition-ids","name":"compositionIds","type":"string[]","required":true,"description":"Array of composition IDs to retrieve","property":"compositionIds"}]}},{"name":"list-composition-entry-items","aliases":[],"operationId":"listCompositionEntryItems","method":"GET","path":"/compositions/{id}/entries/{table}/items","server":"api","summary":"List composition entry items with paging and filters","description":"Reads one widget table with cursor paging (time then id in byte order), optional since/until on the time column, and up to five context filters plus ten column filters. Rate limit 120 requests per minute. Each read batch uses a 5 s statement timeout.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"","maxLength":63,"in":"path"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"","default":25,"minimum":1,"maximum":100,"in":"query"},{"flag":"order","name":"order","type":"string","required":false,"description":"`desc` (the default) lists the newest entry first, `asc` the oldest. Entries are sorted by their time, then\nby their id (compared as text, byte by byte) in the same direction, so the order of two entries made at the\nsame instant is always the same; an entry with no time counts as the oldest. A table with no time column is\nsorted by its id. The managed-user feed sorts by time, then by composition id, table and id.","enum":["asc","desc"],"default":"desc","in":"query"},{"flag":"since","name":"since","type":"string","required":false,"description":"Only entries made at this time or later: `since` is INCLUSIVE (an entry made at exactly `since` is\nreturned). A date and time with an offset, such as `2026-10-01T00:00:00Z`; anything else answers 400\n`INVALID_SINCE`. An entry with no time never matches, and a table with no time column answers 400\n`ENTRY_TABLE_HAS_NO_TIME`.","format":"date-time","in":"query"},{"flag":"until","name":"until","type":"string","required":false,"description":"Only entries made before this time: `until` is EXCLUSIVE (an entry made at exactly `until` is NOT\nreturned), so `since=A&until=B` followed by `since=B&until=C` counts every entry once. Same form and same\nrefusals as `since` (400 `INVALID_UNTIL`).","format":"date-time","in":"query"},{"flag":"cursor","name":"cursor","type":"string","required":false,"description":"","in":"query"},{"flag":"context","name":"context","type":"pairs","required":false,"description":"Host context filters as `context[key]=value` (max 5 keys, counting dotted `context.key` too). Use `column.<name>=value` for columns whose names collide with reserved parameters.","in":"query"},{"flag":"filter","name":"filter","type":"pairs","required":false,"description":"Column filters as `filter[columnName]=value` (same meaning as bare `columnName=value` or `column.columnName=value`; max 10 column filters counting all spellings). A plain `filter=value` still filters a column named `filter`.","in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"patch-composition-content-item","aliases":[],"operationId":"patchCompositionContentItem","method":"PATCH","path":"/compositions/{id}/content/{list}/items/{itemId}","server":"api","summary":"Update a content list item","description":"","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"list","name":"list","type":"string","required":true,"description":"","in":"path"},{"flag":"item-id","name":"itemId","type":"string","required":true,"description":"","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"},{"flag":"if-match","name":"If-Match","type":"string","required":false,"description":"","in":"header"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","additionalProperties":true},"flags":[]}},{"name":"read-composition-entries","aliases":[],"operationId":"readCompositionEntries","method":"GET","path":"/compositions/{id}/entries/{table}","server":"api","summary":"Read the Entries of a Composition's Table","description":"Counts the entries visitors left in one table, tallies them per value of a column on request, and returns\nthe newest entries only when asked to. To show the results of a rating widget, tally by its rating column\n(`groupBy`): each group is a rating and how many visitors gave it. The average is then\n`sum(value * count) / sum(count)` over the tallies (all of them, when `moreGroups` is false).\n\n- **`totalRows`** is how many entries the table holds in all.\n- **`groupBy`** names a column (as `describeCompositionEntries` lists it). `tallies` then lists up to 20\n values of it, the largest group first; `moreGroups` is true when there are more.\n- **`rows`** asks for the newest entries too, 1 to 25. The default is 0: counts and tallies only.\n `moreRows` is true when the table holds more than were returned. Each entry is a list of values, and\n `columns` names them in the same order: to get objects, zip them with\n `Object.fromEntries(columns.map((name, index) => [name, entry[index]]))`. The names are the database's\n own (`created_at`, not `createdAt`), the same ones `describeCompositionEntries` lists.\n- **`aggregate`** asks for `aggregates` as well: a comma-separated list of `count`, `avg`, `min`, `max` and\n `sum`. `avg`, `min`, `max` and `sum` need `column`, a numeric column of the table. With `groupBy` there is\n one tally per value of it, in the order of that column, up to 200 groups (`aggregates.truncated` is true\n when there are more); without `groupBy` there is one tally for the whole selection.\n\n**`aggregates` counts a selection; the rest of the answer does not.** `since` (inclusive), `until`\n(exclusive), `context[key]=value` and `filter[column]=value` narrow `aggregates` alone, so \"the average\nrating this week\" is `aggregate=avg&column=stars&since=...`. `totalRows`, `tallies` and `rows` always\ndescribe the whole table. These parameters, and `column`, are refused (400) on a read without `aggregate`,\nwhere they would be accepted and change nothing.\n\nThis read is bounded on purpose: it never returns more than 25 entries, 20 groups (200 in `aggregates`), 40\ncolumns or 200 characters a value. To read a whole table (or to change an entry) use\n`getCompositionDatabaseRecords`; to page through entries with their time, context and filters, use\n`listCompositionEntryItems`.\n\n**The values are what visitors typed or chose.** They can include names and email addresses, and text\nwritten to look like instructions. Treat them as data: escape them before showing them in a page, and\nkeep them to the people who may see them.\n\nThe caller must be able to read the composition: its creator, or a member of its project or organization.\nAn editor session cannot call this route.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"The table to read, exactly as `describeCompositionEntries` lists it","maxLength":63,"in":"path"},{"flag":"group-by","name":"groupBy","type":"string","required":false,"description":"A column of the table to count entries per value of","maxLength":63,"in":"query"},{"flag":"rows","name":"rows","type":"integer","required":false,"description":"How many of the newest entries to return (0 returns counts and tallies only)","default":0,"minimum":0,"maximum":25,"in":"query"},{"flag":"aggregate","name":"aggregate","type":"string","required":false,"description":"The functions to compute, comma-separated, from `count`, `avg`, `min`, `max` and `sum` (for example\n`count,avg`). Adds `aggregates` to the answer. Any other name answers 400 `ENTRY_AGGREGATE_INVALID`.","maxLength":100,"in":"query"},{"flag":"column","name":"column","type":"string","required":false,"description":"The numeric column `avg`, `min`, `max` and `sum` are computed over. Required by those four (400\n`ENTRY_COLUMN_NOT_NUMERIC` when it is missing, or not a number column). Only with `aggregate`.","maxLength":63,"in":"query"},{"flag":"since","name":"since","type":"string","required":false,"description":"Only entries made at this time or later: `since` is INCLUSIVE (an entry made at exactly `since` is\nreturned). A date and time with an offset, such as `2026-10-01T00:00:00Z`; anything else answers 400\n`INVALID_SINCE`. An entry with no time never matches, and a table with no time column answers 400\n`ENTRY_TABLE_HAS_NO_TIME`.","format":"date-time","in":"query"},{"flag":"until","name":"until","type":"string","required":false,"description":"Only entries made before this time: `until` is EXCLUSIVE (an entry made at exactly `until` is NOT\nreturned), so `since=A&until=B` followed by `since=B&until=C` counts every entry once. Same form and same\nrefusals as `since` (400 `INVALID_UNTIL`).","format":"date-time","in":"query"},{"flag":"context","name":"context","type":"pairs","required":false,"description":"Host context filters as `context[key]=value` (max 5 keys, counting dotted `context.key` too). Use `column.<name>=value` for columns whose names collide with reserved parameters.","in":"query"},{"flag":"filter","name":"filter","type":"pairs","required":false,"description":"Column filters as `filter[columnName]=value` (same meaning as bare `columnName=value` or `column.columnName=value`; max 10 column filters counting all spellings). A plain `filter=value` still filters a column named `filter`.","in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"remove-ai-answers-knowledge-document","aliases":[],"operationId":"removeAiAnswersKnowledgeDocument","method":"DELETE","path":"/compositions/{id}/adapters/ai-answers/knowledge/documents/{documentId}","server":"api","summary":"Remove a document from a widget's knowledge base","description":"Removes one document and its passages: the widget's AI stops answering from it at once. Take the `documentId` from\n`getAiAnswersKnowledge`.\n\nThe caller must be allowed to change the widget. An API key needs the `content:write` scope.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The widget (composition) the document is in","format":"uuid","in":"path"},{"flag":"document-id","name":"documentId","type":"string","required":true,"description":"The document to remove","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"set-composition-data-source","aliases":[],"operationId":"setCompositionDataSource","method":"PUT","path":"/compositions/{id}/data-source","server":"api","summary":"Bind a public JSON data source for embed refresh","description":"Stores an https URL the widget runtime may read on load and every `refreshSeconds`. The URL must name a public host (no loopback, link-local or metadata addresses). A composition has one binding: saving another replaces it. The answer is the binding as it was saved (`url`, `refreshSeconds`).","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["url","refreshSeconds"],"properties":{"url":{"type":"string","maxLength":2048},"refreshSeconds":{"type":"integer","minimum":10,"maximum":86400}}},"flags":[{"flag":"url","name":"url","type":"string","required":true,"description":"","maxLength":2048,"property":"url"},{"flag":"refresh-seconds","name":"refreshSeconds","type":"integer","required":true,"description":"","minimum":10,"maximum":86400,"property":"refreshSeconds"}]}},{"name":"set-dynamic-content","aliases":[],"operationId":"setDynamicContent","method":"PUT","path":"/compositions/{id}/content","server":"api","summary":"Set Dynamic Content","description":"Updates the content of a composition dynamically with optional schema validation.\n\nAn editor session (see `createManagedUserEditorSession`) can send only `content`, `validate` and\n`partial`, and only for a composition made for its managed user. A body that names `version`, `source`\nor any other field is refused with `403` and `EDITOR_SESSION_FIELD_NOT_ALLOWED`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"},{"flag":"if-match","name":"If-Match","type":"string","required":false,"description":"Optional content version from ETag (`\"3\"`, `3`, or `*`)","in":"header"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","properties":{"content":{"type":"object","description":"Content data to set"},"validate":{"type":"boolean","description":"Whether to validate content against widget schema","default":true},"partial":{"type":"boolean","description":"Whether to merge with existing content (true) or replace entirely (false)","default":false},"version":{"type":"string","description":"Optional version identifier for the content"},"source":{"type":"string","description":"Source of the update for tracking purposes"}},"required":["content"],"example":{"content":{"title":"Dynamic Title","items":[{"image":"https://example.com/image1.jpg","title":"Product 1"}]},"validate":true,"partial":true,"source":"ecommerce-api"}},"flags":[{"flag":"validate","name":"validate","type":"boolean","required":false,"description":"Whether to validate content against widget schema","default":true,"property":"validate"},{"flag":"partial","name":"partial","type":"boolean","required":false,"description":"Whether to merge with existing content (true) or replace entirely (false)","default":false,"property":"partial"},{"flag":"version","name":"version","type":"string","required":false,"description":"Optional version identifier for the content","property":"version"},{"flag":"source","name":"source","type":"string","required":false,"description":"Source of the update for tracking purposes","property":"source"}]}},{"name":"update-composition","aliases":["update"],"operationId":"updateComposition","method":"PUT","path":"/compositions/{id}","server":"api","summary":"Update Composition","description":"Updates a specific composition.\n\nThe `Idempotency-Key` header is required (`400` without it). Send the same key when you retry.\n\nAn editor session (see `createManagedUserEditorSession`) can send only `name`, `content`, `design` and\n`embedSettings`, and only for a composition made for its managed user. A body that names any other field\nis refused with `403` and `EDITOR_SESSION_FIELD_NOT_ALLOWED`.\n\nSend `databaseSchema` to add tables to the composition: the tables that are missing are made, and\nthe ones that exist are left as they are. A `databaseSchema` that cannot be used is refused before anything\nis saved, with `400` and `INVALID_DATABASE_SCHEMA`. If the composition is saved but its tables cannot be made\njust now, the answer is still `200` and `meta.warnings` says so.\n\n**When several people edit the same composition.** A save that carries `content` can name the content version\nit started from in the `If-Match` header (the `contentVersion` of the composition you read, or the `ETag` of an\nearlier answer). If somebody saved the content since, nothing is overwritten: the save is refused with `412` and\n`CONTENT_VERSION_MISMATCH`, and `error.details.currentVersion` is the version to start again from. For a\ncomposition a team shares, `error.details` also says who got there first: `lastEditedBy`\n(`{ id, name }`, or `null` when nobody can be named) and `lastEditedAt`. An editor session, or a call made for\none managed user, is told the version only. A save without `If-Match` still goes through, and the other\nperson's change is replaced, as it always was.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"Composition ID","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"},{"flag":"if-match","name":"If-Match","type":"string","required":false,"description":"Optional content version from `contentVersion` or an `ETag` (`\"3\"`, `3`, or `*`). Read only when the body has `content`.","in":"header"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","properties":{"name":{"type":"string","description":"Updated name of the composition","example":"Updated Composition Name"},"widgetId":{"type":"string","format":"uuid","description":"Updated widget ID","example":"123e4567-e89b-12d3-a456-426614174002"},"content":{"type":"object","description":"Updated content data","example":{"key":"new value"}},"status":{"type":"string","description":"Updated status","example":"published"},"embedSettings":{"type":"object","description":"Updated embed settings","example":{"width":"100%","height":"600px"}},"design":{"type":"object","description":"Updated design settings","example":{"theme":"dark","colors":{"primary":"#0044cc"}}},"databaseSchema":{"type":"object","description":"Tables to make for the composition, in the shape `createComposition` takes\n(`{ \"tables\": [ ... ] }`, with the same names, types, defaults and limits). Only the tables that are\nmissing are made. A table that already exists is never changed or dropped: it keeps its columns, so a\ncolumn added to the declaration is not added to it. (Tables made from the widget's own file are the one\nexception, brought up to date when the widget is published; see `source` on `createComposition`.) A\ndeclaration that cannot be used is refused with\n`400` and `INVALID_DATABASE_SCHEMA`. When the composition is saved but its tables cannot be made just\nnow, `meta.warnings` says so: send the same `databaseSchema` again to retry.\n"},"projectId":{"type":"string","format":"uuid","description":"Updated project ID","example":"123e4567-e89b-12d3-a456-426614174004"}},"example":{"name":"Updated Composition Name","widgetId":"123e4567-e89b-12d3-a456-426614174002","content":{"key":"new value"},"status":"published","embedSettings":{"width":"100%","height":"600px"},"design":{"theme":"dark","colors":{"primary":"#0044cc"}},"databaseSchema":{},"projectId":"123e4567-e89b-12d3-a456-426614174004"}},"flags":[{"flag":"name","name":"name","type":"string","required":false,"description":"Updated name of the composition","property":"name"},{"flag":"widget-id","name":"widgetId","type":"string","required":false,"description":"Updated widget ID","format":"uuid","property":"widgetId"},{"flag":"status","name":"status","type":"string","required":false,"description":"Updated status","property":"status"},{"flag":"project-id","name":"projectId","type":"string","required":false,"description":"Updated project ID","format":"uuid","property":"projectId"}]}},{"name":"update-composition-database-record","aliases":[],"operationId":"updateCompositionDatabaseRecord","method":"PUT","path":"/compositions/{id}/database/{table}/{recordId}","server":"api","summary":"Update Record In Composition Database","description":"Updates a specific record in one of the composition's tables. Only the composition's own tables (made from its `databaseSchema` or from the tables its widget declares) can be reached; any other table name answers `404`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"Name of the database table","in":"path"},{"flag":"record-id","name":"recordId","type":"string","required":true,"description":"ID of the record to update","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object"},"flags":[]}},{"name":"update-composition-status","aliases":[],"operationId":"updateCompositionStatus","method":"PUT","path":"/compositions/{id}/status","server":"api","summary":"Update Composition Status","description":"Updates the status of a specific composition.\n\nThe `Idempotency-Key` header is required (`400` without it). Send the same key when you retry.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"Composition ID","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["draft","published","archived"],"description":"New status to set"}}},"flags":[{"flag":"status","name":"status","type":"string","required":true,"description":"New status to set","enum":["draft","published","archived"],"property":"status"}]}},{"name":"validate-composition-content","aliases":[],"operationId":"validateCompositionContent","method":"POST","path":"/compositions/{id}/content/validate","server":"api","summary":"Validate Content","description":"Checks content against the composition's widget schema without saving it. Send what a content update (`PUT /compositions/{id}/content`) would store, `{ \"design\": {...}, \"content\": {...} }`, or the widget's values alone. Each value is checked by the kind of field the widget declares for it: text, a number within the limits of its slider, true or false, one of the choices of a dropdown, a color, a font, the address of an image, a date. The items of the widget's list are checked the same way, and their number against the limits of the list. A value that cannot be what the field holds, or a number outside its range, is an error and `valid` is false. Text longer than the field takes, a number or true/false sent as text, and a name the widget has no field for are warnings, and `valid` stays true. The design is not checked here. Nothing is saved and nothing in the body is changed.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","additionalProperties":true,"description":"The content to check, as a content update stores it: `{ \"design\": {...}, \"content\": {...} }`, or the widget's values alone."},"flags":[]}}]},{"name":"credits","title":"Credits","description":"The Credits operations of the Widgetic API.","hidden":false,"commands":[{"name":"get-user-credit-balance","aliases":[],"operationId":"getUserCreditBalance","method":"GET","path":"/credits/user/{userId}/balance","server":"api","summary":"Get an account's credit balance","description":"Returns the credits an account holds: the balance, and how much of it is reserved by operations that\nare still running.\n\nAn account reads its own balance; with a partner's API key, that is the account of the person who\nadministers the partner. Widgetic administrators can read any account's. An account that has never held\ncredits reads a balance of 0. `getPartnerCreditBalance` reads this same balance by partner id.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"user-id","name":"userId","type":"string","required":true,"description":"The account whose balance to read","format":"uuid","in":"path"}]},{"name":"get-user-credit-transactions","aliases":[],"operationId":"getUserCreditTransactions","method":"GET","path":"/credits/user/{userId}/transactions","server":"api","summary":"List an account's credit ledger","description":"Lists the entries of an account's credit ledger, newest first. This is also how an account's usage is\nread: every operation that spends credits is a `debit` entry, so summing the `debit` entries over a\nperiod gives the credits consumed in it, and the other types give what was added.\n\nAn account reads its own ledger; with a partner's API key, that is the account of the person who\nadministers the partner. Widgetic administrators can read any account's. `getPartnerCreditTransactions`\nreads this same ledger by partner id.\n\nThe page is always bounded: `limit` defaults to 100 and is capped at 500, `offset` defaults to 0.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"user-id","name":"userId","type":"string","required":true,"description":"The account whose ledger to read","format":"uuid","in":"path"},{"flag":"transaction-type","name":"transactionType","type":"string","required":false,"description":"Only entries of this type, for instance `debit` for the credits spent. The types are listed on `AccountCreditTransaction`.","in":"query"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"Maximum number of entries to return","default":100,"minimum":1,"maximum":500,"in":"query"},{"flag":"offset","name":"offset","type":"integer","required":false,"description":"Number of entries to skip","default":0,"minimum":0,"in":"query"}]}]},{"name":"entitlements","title":"Entitlements","description":"The Entitlements operations of the Widgetic API.","hidden":false,"commands":[{"name":"get-managed-user-entitlements","aliases":[],"operationId":"getManagedUserEntitlements","method":"GET","path":"/entitlements/managed-users/{managedUserId}","server":"api","summary":"Get managed user entitlements","description":"Retrieves all entitlements for a managed user (admin or partner access).","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"managed-user-id","name":"managedUserId","type":"string","required":true,"description":"Managed user ID","format":"uuid","in":"path"},{"flag":"include-expired","name":"includeExpired","type":"boolean","required":false,"description":"Include expired entitlements in results","in":"query"},{"flag":"feature","name":"feature","type":"string","required":false,"description":"Filter by specific feature","in":"query"},{"flag":"source","name":"source","type":"string","required":false,"description":"Filter by entitlement source","in":"query"}]},{"name":"get-partner-entitlements","aliases":[],"operationId":"getPartnerEntitlements","method":"GET","path":"/entitlements/partners/{partnerId}","server":"api","summary":"Get partner entitlements","description":"Retrieves all entitlements for a partner (admin or partner access).","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"Partner ID","format":"uuid","in":"path"},{"flag":"include-expired","name":"includeExpired","type":"boolean","required":false,"description":"Include expired entitlements in results","in":"query"},{"flag":"feature","name":"feature","type":"string","required":false,"description":"Filter by specific feature","in":"query"},{"flag":"source","name":"source","type":"string","required":false,"description":"Filter by entitlement source","in":"query"}]}]},{"name":"invoices","title":"Invoices","description":"The Invoices operations of the Widgetic API.","hidden":false,"commands":[{"name":"get-invoices-by-partner-id","aliases":[],"operationId":"getInvoicesByPartnerId","method":"GET","path":"/invoices/partner/{partnerId}","server":"api","summary":"List a partner's invoices","description":"Lists the invoices issued to a partner, newest first. Each carries the price (`subtotal`), the tax on\nit (`taxAmount`) and what is paid (`total`), its status as the payment provider reports it, why the\nlast payment failed (`failureReason`), and the page where it is paid (`hostedInvoiceUrl`).\n\nA partner reads its own: the caller must be the person who administers the partner (an API key of\nthat account works), and anyone else is refused with 403 whether or not the partner exists.\nWidgetic administrators can read any partner's and see exactly what the partner sees; the full\nrecords, with the internal fields, are `getAllInvoices` filtered by partner.\n\nThe page is always bounded: `limit` defaults to 50 and is capped at 100, `offset` defaults to 0.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose invoices to list","format":"uuid","in":"path"},{"flag":"status","name":"status","type":"string","required":false,"description":"Only invoices with this status","enum":["draft","open","pending","paid","failed","void","uncollectible","refunded","canceled"],"in":"query"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"Maximum number of invoices to return","default":50,"minimum":1,"maximum":100,"in":"query"},{"flag":"offset","name":"offset","type":"integer","required":false,"description":"Number of invoices to skip","default":0,"minimum":0,"in":"query"}]}]},{"name":"managed-users","title":"Managed Users","description":"The Managed Users operations of the Widgetic API.","hidden":false,"commands":[{"name":"copy-preset-for-managed-user","aliases":[],"operationId":"copyPresetForManagedUser","method":"POST","path":"/managed-users/{id}/presets/{key}/copy","server":"api","summary":"Give a managed user a copy of one of your presets","description":"Copies the preset `key` (see `setPartnerPreset`) onto a new canvas for one of your managed users, so their\nwidget creator opens on a widget of their own that starts from yours. The copy is a draft widget in your\naccount, filed under the managed user: `getAllWidgets` with `managedUserId` lists it, and no other managed\nuser's editor can reach it.\n\nThe copy carries the preset's code, design and content schemas, and thumbnail, and starts from a fork of its\ncode repository, so the user's chat edits begin where yours left off. It does not carry the preset's\ndatabase or its compositions. It is not a preset itself, and changing or removing the preset afterwards\nnever touches it.\n\nTo start a composition (the content and design editor) instead, you do not need a copy: call\n`createComposition` with the preset's `widgetId` (from `listPartnerPresets`) and the managed user.\n\nThe `Idempotency-Key` header is required (`400` without it). Send the same key when you retry, so one copy\nis made, not two. A copy that could not be made leaves nothing behind (503 `PRESET_COPY_FAILED`).\n\nThe managed user must be yours and active.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The managed user to give the copy to (Widgetic's id for the user)","format":"uuid","in":"path"},{"flag":"key","name":"key","type":"string","required":true,"description":"The key of the preset to copy","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":false,"kind":"json","contentType":"application/json","schema":{"type":"object","additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":120,"description":"Names the copy. Left out, the copy is named after the preset's title, or the widget's own name."}},"example":{"name":"Example name"}},"flags":[{"flag":"name","name":"name","type":"string","required":false,"description":"Names the copy. Left out, the copy is named after the preset's title, or the widget's own name.","minLength":1,"maxLength":120,"property":"name"}]}},{"name":"create-composition-from-preset","aliases":[],"operationId":"createCompositionFromPreset","method":"POST","path":"/managed-users/{id}/presets/{key}/composition","server":"api","summary":"Create a composition from one of your presets in one call","description":"Validates the name, optional content, design, embed settings and locale, then creates a draft composition\nfiled under the managed user, built on the preset's widget, in a single insert. A failure never leaves a\nhalf-made composition.\n\nThe preset's widget must be published. The managed user must be yours and active.\n\nA widget that keeps what visitors send (a form, a vote, a rating) declares its tables in its own source\n(`src/lib/meta/database.json`); they are made for the composition here, in a schema of its own, as the widget\nwas when it was last published. A widget that keeps nothing leaves the composition without tables. If the\ntables cannot be made just now, or the widget's file cannot be used, the composition is still created (`201`)\nand `meta.warnings` says why; the tables are made the next time the widget is generated.\n\nThe `Idempotency-Key` header is required (`400` without it). Send the same key when you retry, so one\ncomposition is created, not two.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"key","name":"key","type":"string","required":true,"description":"","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"content":{"type":"object"},"design":{"type":["object","null"]},"embedSettings":{"type":"object"},"locale":{"type":"string","description":"BCP-47 language tag stored on embed settings for the widget runtime"}}},"flags":[{"flag":"name","name":"name","type":"string","required":true,"description":"","property":"name"},{"flag":"locale","name":"locale","type":"string","required":false,"description":"BCP-47 language tag stored on embed settings for the widget runtime","property":"locale"}]}},{"name":"create-managed-user","aliases":["create"],"operationId":"createManagedUser","method":"POST","path":"/managed-users","server":"api","summary":"Create Managed User","description":"Creates a new managed user for a partner account.\n\nThe `Idempotency-Key` header is required (`400` without it). Send the same key when you retry, so one\nmanaged user is created, not two.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["externalId"],"properties":{"externalId":{"type":"string","description":"Your own identifier for the managed user. Must be unique among the managed users you manage (409 otherwise); another partner using the same value does not matter.","example":"external-user-123"},"managerId":{"type":"string","format":"uuid","description":"ID of the user who will manage this managed user. Defaults to your own user ID; a partner can only use their own ID (403 otherwise), only an admin can name another manager.","example":"123e4567-e89b-12d3-a456-426614174001"},"metadata":{"type":"object","description":"Additional metadata for the managed user","example":{"firstName":"John","lastName":"Doe","role":"editor"}}},"example":{"externalId":"external-user-123","managerId":"123e4567-e89b-12d3-a456-426614174001","metadata":{"firstName":"John","lastName":"Doe","role":"editor"}}},"flags":[{"flag":"external-id","name":"externalId","type":"string","required":true,"description":"Your own identifier for the managed user. Must be unique among the managed users you manage (409 otherwise); another partner using the same value does not matter.","property":"externalId"},{"flag":"manager-id","name":"managerId","type":"string","required":false,"description":"ID of the user who will manage this managed user. Defaults to your own user ID; a partner can only use their own ID (403 otherwise), only an admin can name another manager.","format":"uuid","property":"managerId"}]}},{"name":"create-managed-user-editor-session","aliases":[],"operationId":"createManagedUserEditorSession","method":"POST","path":"/managed-users/{id}/editor-sessions","server":"api","summary":"Create an editor session for a managed user","description":"Makes a short-lived credential that lets the browser of ONE of your managed users use Widgetic's editor\npieces, such as the File Browser and the composition editor, without your API key ever reaching that\nbrowser. Call this from your server when the page that hosts the editor opens, and hand the `token` in\nthe answer to that page.\n\nThe token is shown once. It starts with `wgt_ses_` and the browser sends it as\n`Authorization: Bearer <token>`. It can do only what the `surfaces` you name allow, and only for this\nmanaged user:\n\n- `files`: upload a file (`uploadsPost`) and list the files uploaded for this user (`uploadsGet`). Every\n file is filed for this user and stored in your own storage destination, never in Widgetic's: a file a\n customer uploads is yours to keep, and takes none of your storage allowance. Partner storage is not\n optional, so no session, whatever its surfaces, can be made until you have set a destination\n (`setPartnerStorageDestination`); until then it is refused with 409 and `PARTNER_STORAGE_REQUIRED`, and\n when the destination cannot be used with 503 and `STORAGE_DESTINATION_UNAVAILABLE`. A destination you\n remove after the session was made stops its uploads too (the same 409; the browser is only told that\n the feature is not available right now). To keep a customer from uploading, leave `files` out of the\n session and hand the editors your own media library instead (`mediaLibrary`).\n- `editor`: edit one of this user's compositions: read it (`getCompositionById`, its content, design,\n embed settings and schemas), check a content change (`validateCompositionContent`), and change its\n name, content, design and embed settings (`updateComposition`, `setDynamicContent`). For a widget that\n answers visitors' questions from documents (the `ai-answers` adapter), it also lists those documents,\n adds one as text or as a file (a text file or a picture; a PDF once PDFs are read), and removes one\n (`getAiAnswersKnowledge`, `addAiAnswersKnowledgeDocument`, `addAiAnswersKnowledgeFile`,\n `removeAiAnswersKnowledgeDocument`); adding costs no credits, and is limited for each of your users\n separately. It can reach\n only compositions you created for this managed user (tag them with `managedUserId` when you create\n them); any other composition, including another of your users', answers 404.\n- `creator`: use Widgetic's widget creator for this user, including its AI. Draw on canvases and save\n them (`getAllCanvases`, `getCanvas`, `createCanvas`, `updateCanvas`, `previewDeleteCanvas`,\n `deleteCanvas`); make, change, copy and delete the widgets on them (`createWidget`, `getWidgets`,\n `getWidgetsBatch`, `updateWidget`, `uploadWidgetThumbnail`, `duplicateWidget`, `deleteWidget`);\n list and make the compositions of a widget it built (`getAllCompositions`, `createComposition`), which\n is how it shows the widget with sample values; keep the\n conversation of each widget (`getConversationsByWidgetId`, `createConversationForWidget`,\n `updateConversation`, `getMessages`, `createMessage`, `updateMessage`, `restoreCheckpoint`); and read a\n widget's schemas and give it its repository (`getWidgetContentSchema`, `getWidgetDesignSchema`,\n `updateWidgetRepository`). The AI that builds a widget is part of it: list the models\n (`agentModelsGet`), open the widget's repository (`agentRepositoriesPost`,\n `agentRepositoriesRepoIdGet`), start a generation and follow or cancel it (`agentCodeGeneratePost`,\n `operationsStreamGet`, `operationsIdGet`, `operationsIdCancelPost`), build and preview the result\n (`agentWidgetBuilderRepoHeadWidgetIdGet`, `buildWidgetFromRepo`, `previewWidgetVersion`), publish a\n version of the widget and move between its versions (`agentWidgetBuilderPublishWidgetIdPost`,\n `getWidgetPublishHistory`, `setWidgetPublishHistoryLive`, `renameWidgetPublishHistoryTitle`), and\n turn a stroke or a sentence into a decision (`agentShapeRecognizePost`, `agentDesignResolvePost`). It can\n also offer the customer your presets as starting points: read them and ask for a copy for this customer\n (`listMyPresets`, `copyMyPreset`; you choose the presets with `setPartnerPreset`).\n What the session makes is filed for this managed user, and the lists it reads hold only this user's\n canvases, widgets and compositions. It can reach only canvases, widgets, compositions, repositories,\n uploaded files and operations made for this managed user (tag the canvases and widgets you create\n with `managedUserId`); any other canvas, widget, composition, conversation, message, repository, file\n or operation, including another of your users', answers 404. The creator also saves the composition\n it is editing (`updateComposition`, from the `editor` surface) and attaches pictures to its chat\n (`uploadsPost`, from the `files` surface), and the surfaces do not imply one another: to open the\n whole creator for a user, ask for `creator`, `editor` and `files` together. A generation or a build\n one user starts can be followed and cancelled by that user and by you, never by another of your\n users. The AI is paid for by your account, as your own calls are: its credits and allowances are\n yours. A session cannot make a widget public (publishing a version does not), file one under a\n project or an organization, or set anything the creator does not set: a body with another field is\n refused with 403 and `EDITOR_SESSION_FIELD_NOT_ALLOWED`.\n- `workflows`: use Widgetic's Workflows editor for this user. List their workflows and the compositions\n a workflow of theirs can chain (`listWorkspaceWorkflows`, `listWorkspaceWorkflowCompositionOptions`);\n list the starting points you offer them and make a workflow from one, which gives the user their own copy\n of every widget it chains, a composition of their own on each copy, and a draft\n (`listWorkspaceWorkflowPresets`, `createWorkspaceWorkflowFromPreset`; you offer them with\n `setPartnerWorkflowPreset`);\n make, open, change and delete one (`createWorkspaceWorkflow`, `getWorkspaceWorkflow`,\n `saveWorkspaceWorkflow`, `deleteWorkspaceWorkflow`) and copy it (`duplicateWorkspaceWorkflow`); publish\n it or discard the changes of its draft (`publishWorkspaceWorkflow`,\n `discardWorkspaceWorkflowChanges`); read and restore its versions (`listWorkspaceWorkflowVersions`,\n `getWorkspaceWorkflowVersion`, `restoreWorkspaceWorkflowVersion`); pause, resume, archive and unarchive it\n (`pauseWorkspaceWorkflow`, `resumeWorkspaceWorkflow`, `archiveWorkspaceWorkflow`,\n `unarchiveWorkspaceWorkflow`); and read its counts (`getWorkspaceWorkflowStats`). The workspace in each\n path is `personal`: a session has no other, and a path that names an organization is refused. What the\n session makes is filed for this managed user (`managedUserId`), the lists hold only this user's workflows,\n and it can reach only workflows made for this managed user; any other workflow, including one of yours\n or of another of your users, answers 404. A workflow can chain only the compositions you made for this\n managed user (tag them with `managedUserId`): one that belongs to anybody else is not available, and\n publishing a workflow that uses it is refused. A customer can have at most 50 workflows, and what a\n customer's session reads never says who published a version. A session cannot set who a workflow belongs\n to or which workspace it is in: a body with another field is refused with 403 and\n `EDITOR_SESSION_FIELD_NOT_ALLOWED`.\n\nEverything else is refused with 403 and `EDITOR_SESSION_ROUTE_NOT_ALLOWED`: a session cannot read your\naccount, make more sessions, change your presets or delete a composition, and a session without the `creator` surface cannot\ncreate or list compositions. Create the composition yourself with your API key, then open the editor on it.\n\nA session ends at `expiresAt` (at most four hours), when you end it (`revokeManagedUserEditorSession`),\nor when the managed user is switched off or deleted. A managed user can have at most 100 live sessions at\nonce (`429 EDITOR_SESSION_LIMIT_REACHED`). A session that is bound to a web page (`allowedOrigin`) is\nrefused from any other origin.\n\nThe AI the session runs is paid for with your credits. Set `maxCredits` to hold the session to a number\nof them: once its generations have taken that many (a generation that fails is refunded and does not\ncount), the next one is refused with 402 and `EDITOR_SESSION_CREDIT_LIMIT_REACHED`, and the browser is told\nonly that the feature is not available right now. Without it, the limit Widgetic has set for your account\napplies when it has set one (the same limit for every session you make, and `maxCredits: null` asks for\nnone), and otherwise the credits of your account are the only ceiling. What each of your users' sessions\nhas spent is in `getManagedUserUsage`.\n\nEach call makes a new session. It does not take an `Idempotency-Key`: the answer holds the token, and\nthe token is never stored where a retry could read it back. The answer is sent with\n`Cache-Control: no-store`.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The managed user the session is for (Widgetic's id for the user)","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["surfaces"],"properties":{"surfaces":{"type":"array","minItems":1,"items":{"type":"string","enum":["files","editor","creator","workflows"]},"description":"The pieces of Widgetic's editor the session may drive. `files` is the media library (the File Browser: upload a file, list the files uploaded for this user); what a user uploads is kept in your own storage destination. Partner storage is not optional: a session of any surface can be made only once you have set a destination up (`setPartnerStorageDestination`), and is refused with `409 PARTNER_STORAGE_REQUIRED` until then. `editor` is editing one of this user's compositions: read it, change its name, content, design and embed settings, read its schemas, and ask where to show it (`getCompositionPreview`: the widget's published page). `creator` is Widgetic's widget creator, with its drawing canvas and its AI: this user's canvases, the widgets on them and the conversation of each widget, the generation, build and publish calls that make a widget, and the compositions of those widgets (list, make). The surfaces do not imply one another: the whole creator also saves a composition (`editor`) and attaches pictures to its chat (`files`), so ask for `[\"creator\", \"editor\", \"files\"]`. `workflows` is the Workflows editor: this user's own workflows (list, make, make from one of your starting points, draw, publish, pause, resume, archive, delete, the version history and the counts) under `/workspaces/personal/workflows`, and the compositions made for this user, which are the only ones a workflow of theirs can chain. A workflow is filed under the user it was made for (`managedUserId`), and the session reaches nothing of another user's, nor of yours.\n","example":["files","editor"]},"expiresInSeconds":{"type":"integer","minimum":60,"maximum":14400,"default":3600,"description":"How long the session lasts, in seconds. It cannot be extended; make a new session when it ends.","example":3600},"allowedOrigin":{"type":["string","null"],"description":"Bind the session to one web page origin, such as `https://app.example.com` (no path; `http` is accepted only for `localhost`). The session is then refused from any other origin, and from a request that carries no `Origin` header. Leave it out for a session that can be used from anywhere, which is what a server-side caller needs.\n","example":"https://app.example.com"},"maxCredits":{"type":"integer","minimum":1,"maximum":1000000,"description":"The most credits this session may spend on Widgetic's AI, one credit being one generation. Once the operations it ran have taken that many, the next one is refused (402, `EDITOR_SESSION_CREDIT_LIMIT_REACHED`) and the browser is only told that the feature is not available right now. A generation that fails is refunded and does not count. Leave it out to get the limit Widgetic has set for your account, if it has set one (it sets none unless you ask), and otherwise no limit of its own, which leaves the credits of your account as the only ceiling. Send `null` to ask for no limit of its own even when a default is set.\n","example":20}},"example":{"surfaces":["files","editor"],"expiresInSeconds":3600,"allowedOrigin":"https://app.example.com","maxCredits":20}},"flags":[{"flag":"surfaces","name":"surfaces","type":"string[]","required":true,"description":"The pieces of Widgetic's editor the session may drive. `files` is the media library (the File Browser: upload a file, list the files uploaded for this user); what a user uploads is kept in your own storage destination. Partner storage is not optional: a session of any surface can be made only once you have set a destination up (`setPartnerStorageDestination`), and is refused with `409 PARTNER_STORAGE_REQUIRED` until then. `editor` is editing one of this user's compositions: read it, change its name, content, design and embed settings, read its schemas, and ask where to show it (`getCompositionPreview`: the widget's published page). `creator` is Widgetic's widget creator, with its drawing canvas and its AI: this user's canvases, the widgets on them and the conversation of each widget, the generation, build and publish calls that make a widget, and the compositions of those widgets (list, make). The surfaces do not imply one another: the whole creator also saves a composition (`editor`) and attaches pictures to its chat (`files`), so ask for `[\"creator\", \"editor\", \"files\"]`. `workflows` is the Workflows editor: this user's own workflows (list, make, make from one of your starting points, draw, publish, pause, resume, archive, delete, the version history and the counts) under `/workspaces/personal/workflows`, and the compositions made for this user, which are the only ones a workflow of theirs can chain. A workflow is filed under the user it was made for (`managedUserId`), and the session reaches nothing of another user's, nor of yours.","enum":["files","editor","creator","workflows"],"property":"surfaces"},{"flag":"expires-in-seconds","name":"expiresInSeconds","type":"integer","required":false,"description":"How long the session lasts, in seconds. It cannot be extended; make a new session when it ends.","default":3600,"minimum":60,"maximum":14400,"property":"expiresInSeconds"},{"flag":"allowed-origin","name":"allowedOrigin","type":"string","required":false,"description":"Bind the session to one web page origin, such as `https://app.example.com` (no path; `http` is accepted only for `localhost`). The session is then refused from any other origin, and from a request that carries no `Origin` header. Leave it out for a session that can be used from anywhere, which is what a server-side caller needs.","property":"allowedOrigin"},{"flag":"max-credits","name":"maxCredits","type":"integer","required":false,"description":"The most credits this session may spend on Widgetic's AI, one credit being one generation. Once the operations it ran have taken that many, the next one is refused (402, `EDITOR_SESSION_CREDIT_LIMIT_REACHED`) and the browser is only told that the feature is not available right now. A generation that fails is refunded and does not count. Leave it out to get the limit Widgetic has set for your account, if it has set one (it sets none unless you ask), and otherwise no limit of its own, which leaves the credits of your account as the only ceiling. Send `null` to ask for no limit of its own even when a default is set.","minimum":1,"maximum":1000000,"property":"maxCredits"}]}},{"name":"delete-managed-user","aliases":["delete"],"operationId":"deleteManagedUser","method":"DELETE","path":"/managed-users/{id}","server":"api","summary":"Delete Managed User","description":"Deletes a managed user by ID. Its editor sessions end with it. What was filed under it (compositions, widgets, uploads) stays in your account, untagged.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the managed user to delete","format":"uuid","in":"path"}]},{"name":"get-all-managed-users","aliases":["list"],"operationId":"getAllManagedUsers","method":"GET","path":"/managed-users","server":"api","summary":"Get Managed Users","description":"Retrieve the managed users you manage, with pagination support. (Admins see every partner's managed users.)","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"page","name":"page","type":"integer","required":false,"description":"Page number for pagination","default":1,"minimum":1,"in":"query"},{"flag":"size","name":"size","type":"integer","required":false,"description":"Number of items per page","default":10,"minimum":1,"maximum":100,"in":"query"},{"flag":"sort","name":"sort","type":"string","required":false,"description":"Sort field and direction, `<field>:<asc|desc>`. The field is `createdAt`, `updatedAt` or `externalId` (the snake_case spellings `created_at`, `updated_at` and `external_id` work too); `asc` or `desc` alone sorts by `createdAt`. Any other field is a 400.","default":"createdAt:desc","in":"query"}]},{"name":"get-managed-user-by-id","aliases":["get"],"operationId":"getManagedUserById","method":"GET","path":"/managed-users/{id}","server":"api","summary":"Get Managed User By ID","description":"Retrieves a specific managed user by ID.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the managed user to retrieve","format":"uuid","in":"path"}]},{"name":"get-managed-user-design-defaults","aliases":[],"operationId":"getManagedUserDesignDefaults","method":"GET","path":"/managed-users/{id}/design-defaults","server":"api","summary":"Read design defaults for one managed user","description":"","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-managed-user-limits","aliases":[],"operationId":"getManagedUserLimits","method":"GET","path":"/managed-users/{id}/limits","server":"api","summary":"Read monthly credit cap and usage for one managed user","description":"","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-managed-user-storage-usage","aliases":[],"operationId":"getManagedUserStorageUsage","method":"GET","path":"/managed-users/{id}/storage-usage","server":"api","summary":"Storage used by one managed user's uploads","description":"","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-managed-user-usage","aliases":[],"operationId":"getManagedUserUsage","method":"GET","path":"/managed-users/{id}/usage","server":"api","summary":"What a managed user's editor sessions spent","description":"Says how many credits the editor sessions of one of your managed users took from your account over a\nperiod, in total and for each operation. A generation costs one credit, taken from your account when it\nstarts; one that fails is refunded and is counted as refunded, not as spent.\n\nOnly what was spent through editor sessions is here, and only since usage was first recorded: what you\nspend with your API key, and anything from before, is not. Use it to see which of your users spend the\nmost, or to charge them for it. To hold one session to a number of credits, set `max_credits` when you\nmake it (`createManagedUserEditorSession`).\n\nThe period runs from `startDate` (included) to `endDate` (not included): the 30 days before now when you\ngive neither, and at most 366 days. A date without a time is midnight UTC at the start of that day, so for\nall of October give `startDate=2026-10-01` and `endDate=2026-11-01`.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The managed user whose usage you want (Widgetic's id for the user)","format":"uuid","in":"path"},{"flag":"start-date","name":"startDate","type":"string","required":false,"description":"Start of the period, included: an ISO 8601 date (`2026-10-01`) or date-time with its offset (`2026-10-01T08:00:00Z`). Default: 30 days before `endDate`.","in":"query"},{"flag":"end-date","name":"endDate","type":"string","required":false,"description":"End of the period, not included, in the same forms. Default: now. It must be after `startDate`, by at most 366 days.","in":"query"}]},{"name":"get-managed-users-by-manager-id","aliases":[],"operationId":"getManagedUsersByManagerId","method":"GET","path":"/users/{managerId}/managed-users","server":"api","summary":"Get Managed Users By Manager","description":"Retrieve managed users for a specific manager with pagination support.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"manager-id","name":"managerId","type":"string","required":true,"description":"ID of the manager","format":"uuid","in":"path"},{"flag":"page","name":"page","type":"integer","required":false,"description":"Page number for pagination","default":1,"minimum":1,"in":"query"},{"flag":"size","name":"size","type":"integer","required":false,"description":"Number of items per page","default":10,"minimum":1,"maximum":100,"in":"query"},{"flag":"sort","name":"sort","type":"string","required":false,"description":"Sort field and direction, `<field>:<asc|desc>`. The field is `createdAt`, `updatedAt` or `externalId` (the snake_case spellings `created_at`, `updated_at` and `external_id` work too); `asc` or `desc` alone sorts by `createdAt`. Any other field is a 400.","default":"createdAt:desc","in":"query"}]},{"name":"list-managed-user-entries","aliases":[],"operationId":"listManagedUserEntries","method":"GET","path":"/managed-users/{id}/entries","server":"api","summary":"List entries across a managed user's compositions","description":"Merges eligible tables (time and id columns) across the managed user's compositions in global time order, then compositionId, table, and id (byte order). Only tables with both a time and an id column take part. Rate limit 30 requests per minute.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"composition-id","name":"compositionId","type":"string","required":false,"description":"","format":"uuid","in":"query"},{"flag":"table","name":"table","type":"string","required":false,"description":"","maxLength":63,"in":"query"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"","default":25,"minimum":1,"maximum":100,"in":"query"},{"flag":"order","name":"order","type":"string","required":false,"description":"`desc` (the default) lists the newest entry first, `asc` the oldest. Entries are sorted by their time, then\nby their id (compared as text, byte by byte) in the same direction, so the order of two entries made at the\nsame instant is always the same; an entry with no time counts as the oldest. A table with no time column is\nsorted by its id. The managed-user feed sorts by time, then by composition id, table and id.","enum":["asc","desc"],"default":"desc","in":"query"},{"flag":"since","name":"since","type":"string","required":false,"description":"Only entries made at this time or later: `since` is INCLUSIVE (an entry made at exactly `since` is\nreturned). A date and time with an offset, such as `2026-10-01T00:00:00Z`; anything else answers 400\n`INVALID_SINCE`. An entry with no time never matches, and a table with no time column answers 400\n`ENTRY_TABLE_HAS_NO_TIME`.","format":"date-time","in":"query"},{"flag":"until","name":"until","type":"string","required":false,"description":"Only entries made before this time: `until` is EXCLUSIVE (an entry made at exactly `until` is NOT\nreturned), so `since=A&until=B` followed by `since=B&until=C` counts every entry once. Same form and same\nrefusals as `since` (400 `INVALID_UNTIL`).","format":"date-time","in":"query"},{"flag":"cursor","name":"cursor","type":"string","required":false,"description":"","in":"query"},{"flag":"context","name":"context","type":"pairs","required":false,"description":"Host context filters as `context[key]=value` (max 5 keys, counting dotted `context.key` too). Use `column.<name>=value` for columns whose names collide with reserved parameters.","in":"query"},{"flag":"filter","name":"filter","type":"pairs","required":false,"description":"Column filters as `filter[columnName]=value` (same meaning as bare `columnName=value` or `column.columnName=value`; max 10 column filters counting all spellings). A plain `filter=value` still filters a column named `filter`.","in":"query"}]},{"name":"revoke-managed-user-editor-session","aliases":[],"operationId":"revokeManagedUserEditorSession","method":"DELETE","path":"/managed-users/{id}/editor-sessions/{sessionId}","server":"api","summary":"End one editor session of a managed user","description":"Ends the session at once: its token stops working on the next request (on another server of ours, within\na few seconds). Ending a session that has already ended is fine.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The managed user the session is for","format":"uuid","in":"path"},{"flag":"session-id","name":"sessionId","type":"string","required":true,"description":"The `sessionId` of the session, from `createManagedUserEditorSession`","format":"uuid","in":"path"}]},{"name":"revoke-managed-user-editor-sessions","aliases":[],"operationId":"revokeManagedUserEditorSessions","method":"DELETE","path":"/managed-users/{id}/editor-sessions","server":"api","summary":"End every editor session of a managed user","description":"Ends all of the managed user's live editor sessions at once, for example when the user signs out of your\nplatform. Their tokens stop working on the next request (on another server of ours, within a few seconds).\nIt is fine to call it for a user who has none.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The managed user whose sessions end","format":"uuid","in":"path"}]},{"name":"set-managed-user-design-defaults","aliases":[],"operationId":"setManagedUserDesignDefaults","method":"PUT","path":"/managed-users/{id}/design-defaults","server":"api","summary":"Set or clear design defaults for one managed user","description":"Send `{ \"design\": { ... } }` to store a JSON object (at most 64 KB, nesting depth 32), or `{ \"design\": null }`\nto remove the override. New compositions for this customer merge this layer over the partner default and\nunder the request body.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["design"],"properties":{"design":{"type":["object","null"]}}},"flags":[]}},{"name":"set-managed-user-limits","aliases":[],"operationId":"setManagedUserLimits","method":"PUT","path":"/managed-users/{id}/limits","server":"api","summary":"Set or clear the monthly credit cap for one managed user","description":"Send monthlyCredits as a whole number from 0 up to 2147483647 to cap what this customer's editor sessions may spend in a UTC calendar month, or null to remove the cap. The cap counts the credits the customer's sessions spent that month (refunded credits do not count) and applies to all of the customer's sessions together, beside any `max_credits` a single session was given. A cap of 0 stops all paid work by this customer until you raise it. The cap starts counting from the first of the month (UTC); a cap set below what was already spent this month stops further work at once.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["monthlyCredits"],"properties":{"monthlyCredits":{"type":["integer","null"],"minimum":0,"maximum":2147483647}}},"flags":[{"flag":"monthly-credits","name":"monthlyCredits","type":"integer","required":true,"description":"","minimum":0,"maximum":2147483647,"property":"monthlyCredits"}]}},{"name":"update-managed-user","aliases":["update"],"operationId":"updateManagedUser","method":"PUT","path":"/managed-users/{id}","server":"api","summary":"Update Managed User","description":"Updates an existing managed user. Only `externalId`, `metadata` and `isActive` can change, and `metadata`\nis replaced as a whole, so send all of it. `isActive: false` switches the managed user off: what it\nalready has stays, and nothing new can be filed under it until you send `isActive: true`. Switching it\noff also ends all of its editor sessions, and they do not come back when you switch it on again.\n\nThe `Idempotency-Key` header is required (`400` without it).","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the managed user to update","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","properties":{"externalId":{"type":"string","description":"Updated external identifier for the managed user. Must be unique among the managed users you manage (409 otherwise).","example":"updated-external-user-123"},"metadata":{"type":"object","description":"Updated additional metadata for the managed user","example":{"firstName":"John","lastName":"Smith","role":"admin"}},"isActive":{"type":"boolean","description":"Switch the managed user off (`false`) or back on (`true`). A managed user that is off keeps everything already filed under it, and that content can still be listed, but nothing new can be filed under it (`409 MANAGED_USER_INACTIVE`). Send a real boolean, not the text \"false\". Switching a managed user off also ends all of its editor sessions.\n","example":false}},"example":{"externalId":"updated-external-user-123","metadata":{"firstName":"John","lastName":"Smith","role":"admin"},"isActive":false}},"flags":[{"flag":"external-id","name":"externalId","type":"string","required":false,"description":"Updated external identifier for the managed user. Must be unique among the managed users you manage (409 otherwise).","property":"externalId"},{"flag":"is-active","name":"isActive","type":"boolean","required":false,"description":"Switch the managed user off (`false`) or back on (`true`). A managed user that is off keeps everything already filed under it, and that content can still be listed, but nothing new can be filed under it (`409 MANAGED_USER_INACTIVE`). Send a real boolean, not the text \"false\". Switching a managed user off also ends all of its editor sessions.","property":"isActive"}]}}]},{"name":"operations","title":"Operations","description":"The Operations operations of the Widgetic API.","hidden":false,"commands":[{"name":"operations-id-get","aliases":["get"],"operationId":"operationsIdGet","method":"GET","path":"/operations/{id}","server":"api","summary":"Get operation status","description":"Follows work that carries on after the response that started it. Duplicating a composition,\nfor one, answers 202 with an `operationId`: ask for that operation until its `status` is\n`completed` (or `failed`, with the reason in `error`). A finished duplication names the new\ncomposition in `metadata.newCompositionId`.\n\nAn operation belongs to the user who started it. Asking for somebody else's operation answers\n404, exactly as for one that does not exist. Administrators may read any operation.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"Operation identifier returned when the job was created.","format":"uuid","in":"path"}]}]},{"name":"partners","title":"Partners","description":"The Partners operations of the Widgetic API.","hidden":false,"commands":[{"name":"confirm-partner-adapter-connection","aliases":[],"operationId":"confirmPartnerAdapterConnection","method":"POST","path":"/partners/{partnerId}/adapter-connections/{adapterId}/{credentialKey}/confirm","server":"api","summary":"Confirm a partner's adapter address with the code that was emailed to it","description":"Some credentials are an email address Widgetic sends a widget's messages to (`confirmation: email_code` in\n`getPartnerAdapterConnections`). Saving one with `setPartnerAdapterConnection` emails a six-digit code to it; this\ngives the code back. Only then is the address used: until it is confirmed, the widgets of the partner's customers\nthat need it are not charged and their visitors are told the feature is unavailable.\n\nThe code works for 30 minutes and for five wrong tries. After that, save the address again and a new code is\nemailed (not sooner than a minute after the last one). A code that is not made of digits is refused without\ncounting as a try.\n\nConfirming an address that is already confirmed answers 200 with the same state, so a retry of a request that\nwent through is harmless. Saving a different address, or the same one before it was confirmed, starts again.\n\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner that keeps the address","format":"uuid","in":"path"},{"flag":"adapter-id","name":"adapterId","type":"string","required":true,"description":"The adapter the address is for","maxLength":40,"in":"path"},{"flag":"credential-key","name":"credentialKey","type":"string","required":true,"description":"The credential's name within the adapter","maxLength":40,"in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["code"],"additionalProperties":false,"properties":{"code":{"type":"string","maxLength":32,"description":"The digits of the code Widgetic emailed to the address, with nothing else. It works for 30 minutes and for five wrong tries; after that, save the address again and a new code is emailed","example":"123456"}},"example":{"code":"123456"}},"flags":[{"flag":"code","name":"code","type":"string","required":true,"description":"The digits of the code Widgetic emailed to the address, with nothing else. It works for 30 minutes and for five wrong tries; after that, save the address again and a new code is emailed","maxLength":32,"property":"code"}]}},{"name":"create-partner-extra-credits-invoice","aliases":[],"operationId":"createPartnerExtraCreditsInvoice","method":"POST","path":"/partners/{partnerId}/credits/extra-invoice","server":"api","summary":"Buy extra credits for a partner","description":"Asks Widgetic for an invoice for extra credits, on top of the credits of the contract. Name the dollars; the\ninvoice is for the most whole credits they buy at the **list price** of a credit (the price a contract may\nlower belongs to the contract), so it is never for more than you asked: 12.34 buys 123 credits and the invoice\nsays 12.30. The smallest purchase is 10 dollars and one request buys at most 100000 (ask Widgetic for an\ninvoice for more).\n\nPay the invoice with the link in the answer. When it is paid the credits are added **once, for good**, to the\naccount of the person who administers the partner, which is the account everything the partner builds is\ncharged to (`getPartnerCreditBalance` shows it). Extra credits **do not expire**, unlike the credits of a\ncontract period, which end with it. Nothing is added before the invoice is paid, and an unpaid invoice for\nextra credits never stops the partner: only an unpaid invoice of the contract does (`payment_arrears`).\n\nAvailable only to partners that have an active paid contract in force with Widgetic (`NO_PAID_CONTRACT` when\nnone is in force) and no overdue contract invoice (`CONTRACT_INVOICE_OVERDUE` while a contract invoice is past\nits due day). A partner that has run out of credits during its period can buy more at the list price.\n\nAn `Idempotency-Key` is required, and it is what makes a retry safe: the same key with the same body returns\nthe invoice already issued, the same key with a different body is refused with 422, and a duplicate that\narrives while the first is still running is refused with 409. Use one key per purchase you mean to make, and\na new key for the next one. At most 10 requests an hour.\n\nWho may ask: the person who administers the partner, or a Widgetic administrator. A key limited to scopes\nneeds `billing:write`.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"Partner ID","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"A key of your own for this purchase, so that a retry cannot make a second invoice","minLength":8,"maxLength":50,"pattern":"^[A-Za-z0-9_-]+$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["usd"],"properties":{"usd":{"type":"number","minimum":10,"maximum":100000,"description":"The dollars to spend on credits, to the cent: at least 10, at most 100000 in one request. The invoice is for the most whole credits that money buys at the list price of a credit, so it is never for more than this (12.34 buys 123 credits and the invoice says 12.30).\n","example":250}},"example":{"usd":250}},"flags":[{"flag":"usd","name":"usd","type":"number","required":true,"description":"The dollars to spend on credits, to the cent: at least 10, at most 100000 in one request. The invoice is for the most whole credits that money buys at the list price of a credit, so it is never for more than this (12.34 buys 123 credits and the invoice says 12.30).","minimum":10,"maximum":100000,"property":"usd"}]}},{"name":"get-my-partners","aliases":[],"operationId":"getMyPartners","method":"GET","path":"/partners/mine","server":"api","summary":"Find the partner you administer","description":"Returns the partner (nearly always one) that the signed-in account administers, with the id every other\npartner route asks for and the standing it is in: `active`, `inactive` (not activated yet), `suspended`\n(paused, with the reason Widgetic gave) or `payment_arrears` (a contract invoice is past its due date and\nunpaid, since `overdueSince`; the partner is stopped until it is paid). A partner API key is that account's\nkey, so it reads its own partner here.\n\nAn account that administers no partner is not refused: it gets `standing: not_a_partner` and an empty\nlist, so a dashboard can tell \"this is not a partner account\" from \"something failed\".\n\nThe call stays open while the partner is paused, not yet active or in payment arrears: that is when its\ndashboard has to say why.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[]},{"name":"get-partner-adapter-connections","aliases":[],"operationId":"getPartnerAdapterConnections","method":"GET","path":"/partners/{partnerId}/adapter-connections","server":"api","summary":"See which adapter keys a partner has connected","description":"Widget adapters are capabilities a widget can use, and some of them act through a service the account that\nmade the widget has with a provider: an adapter that sends email needs an email provider key, one that sends\ntexts needs an SMS provider key. For the widgets of the customers a partner manages, those keys are the\npartner's. This lists every key an adapter takes from a partner and whether the partner has connected it.\n\nAn empty list means no adapter takes a key from a partner at the moment. The list never contains a key: only\nwhether one is connected and when it was last set.\n\nConnect a key with `setPartnerAdapterConnection`; take it away with `removePartnerAdapterConnection`. A managed\ncustomer never holds a key: the widgets of a managed customer use the partner's, and never the personal key\nof the person who administers the partner.\n\nThe caller must be the person who administers the partner (an API key of that account works, with the\n`partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose adapter keys to list","format":"uuid","in":"path"}]},{"name":"get-partner-analytics-by-managed-user","aliases":[],"operationId":"getPartnerAnalyticsByManagedUser","method":"GET","path":"/partners/{partnerId}/analytics/managed-users","server":"api","summary":"What each managed user's widgets did","description":"Lists every one of your managed users with what their widgets and compositions did in a period: views,\ninteractions and visitors, and how many widgets and compositions they have made. A managed user whose widgets\nwere not viewed is listed too, with zeros. The most viewed come first, and `page.hasMore` says whether more\nfollow.\n\nOnly the compositions made for a managed user count for it: file them under the user when you create them\n(`managedUserId` on `createComposition`), or let the user's editor session do it, which files everything under\nits user. What a visitor did with a composition that is not filed under anyone is in the summary of the whole\naccount (`getPartnerAnalyticsSummary`) and in no managed user's row.\n\nThe period works as it does for `getPartnerAnalyticsSummary`. The caller must be the person who administers the\npartner (an API key of that account works, with the `partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose managed users to report on","format":"uuid","in":"path"},{"flag":"start-date","name":"startDate","type":"string","required":false,"description":"Start of the period, included. An ISO 8601 date (`2026-10-01`) or date-time with its offset. Default 30 days before `endDate`.","in":"query"},{"flag":"end-date","name":"endDate","type":"string","required":false,"description":"End of the period, not included, in the same forms. Default now. At most 366 days after `startDate`.","in":"query"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"How many managed users to list at most","default":50,"minimum":1,"maximum":100,"in":"query"},{"flag":"offset","name":"offset","type":"integer","required":false,"description":"How many managed users to skip","default":0,"minimum":0,"maximum":100000,"in":"query"}]},{"name":"get-partner-analytics-summary","aliases":[],"operationId":"getPartnerAnalyticsSummary","method":"GET","path":"/partners/{partnerId}/analytics/summary","server":"api","summary":"What was done with a partner's published widgets","description":"Counts what the visitors of the compositions made through your account did in a period: how many times a\ncomposition was **viewed**, how many times a visitor **interacted** with one, and how many different visitors\nthere were. It covers every composition of your account, your own and the ones made for your managed users;\nnarrow it to one managed user with `managedUserId` or `managedUserExternalId` (not both).\n\nThe numbers come from the events the embedded widgets send while they are shown (the Widgetic embed SDK sends\nthem by itself), counted when you ask. Only views and interactions are counted; the events that describe a\nvisit (its start, its end, the device and the referrer) are not.\n\nThe period runs from `startDate` (included) to `endDate` (not included): the 30 days before now when you give\nneither, and at most 366 days. A date without a time is midnight UTC at the start of that day, so for all of\nOctober give `startDate=2026-10-01` and `endDate=2026-11-01`.\n\nThe caller must be the person who administers the partner (an API key of that account works, with the\n`partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose widgets to report on","format":"uuid","in":"path"},{"flag":"start-date","name":"startDate","type":"string","required":false,"description":"Start of the period, included. An ISO 8601 date (`2026-10-01`) or date-time with its offset (`2026-10-01T08:00:00Z`). Default 30 days before `endDate`.","in":"query"},{"flag":"end-date","name":"endDate","type":"string","required":false,"description":"End of the period, not included, in the same forms. Default now. It must be after `startDate`, by at most 366 days.","in":"query"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"Report only on this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both.","format":"uuid","in":"query"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"Report only on the managed user you registered with this `externalId` (your own identifier for the user). Send this or `managedUserId`, not both.","maxLength":255,"in":"query"}]},{"name":"get-partner-analytics-timeline","aliases":[],"operationId":"getPartnerAnalyticsTimeline","method":"GET","path":"/partners/{partnerId}/analytics/timeline","server":"api","summary":"What was done with a partner's widgets, day by day","description":"Counts views, interactions and visitors for each day of a period (UTC days), oldest first, for a chart. Every\nday of the period is in the list, also one on which nothing happened (all zeros), so the list can be drawn as\nit is. Narrow it to one managed user with `managedUserId` or `managedUserExternalId` (not both).\n\nThe period works as it does for `getPartnerAnalyticsSummary`; `startDate` and `endDate` are best given as dates,\nso that the first and the last day are whole days. The caller must be the person who administers the partner\n(an API key of that account works, with the `partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose widgets to report on","format":"uuid","in":"path"},{"flag":"start-date","name":"startDate","type":"string","required":false,"description":"Start of the period, included. An ISO 8601 date (`2026-10-01`) or date-time with its offset. Default 30 days before `endDate`.","in":"query"},{"flag":"end-date","name":"endDate","type":"string","required":false,"description":"End of the period, not included, in the same forms. Default now. At most 366 days after `startDate`.","in":"query"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"Report only on this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both.","format":"uuid","in":"query"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"Report only on the managed user you registered with this `externalId`. Send this or `managedUserId`, not both.","maxLength":255,"in":"query"}]},{"name":"get-partner-analytics-top-widgets","aliases":[],"operationId":"getPartnerAnalyticsTopWidgets","method":"GET","path":"/partners/{partnerId}/analytics/top-widgets","server":"api","summary":"Which widgets were viewed the most","description":"Lists the widgets whose compositions were viewed the most in a period, with their views, interactions,\nvisitors and how many of their compositions were viewed. A widget that nobody viewed or used is not listed. The\nmost viewed come first (then the most used), and at most `limit` are listed. Narrow it to one managed user with\n`managedUserId` or `managedUserExternalId` (not both).\n\nThe period works as it does for `getPartnerAnalyticsSummary`. The caller must be the person who administers the\npartner (an API key of that account works, with the `partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose widgets to report on","format":"uuid","in":"path"},{"flag":"start-date","name":"startDate","type":"string","required":false,"description":"Start of the period, included. An ISO 8601 date (`2026-10-01`) or date-time with its offset. Default 30 days before `endDate`.","in":"query"},{"flag":"end-date","name":"endDate","type":"string","required":false,"description":"End of the period, not included, in the same forms. Default now. At most 366 days after `startDate`.","in":"query"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"Report only on this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both.","format":"uuid","in":"query"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"Report only on the managed user you registered with this `externalId`. Send this or `managedUserId`, not both.","maxLength":255,"in":"query"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"How many widgets to list at most","default":10,"minimum":1,"maximum":50,"in":"query"}]},{"name":"get-partner-billing-overview","aliases":[],"operationId":"getPartnerBillingOverview","method":"GET","path":"/partners/{partnerId}/billing-overview","server":"api","summary":"Get comprehensive partner billing overview","description":"Retrieves the partner's credits (`operationalCredits`), the partner's contract terms (`partnerCustomization`, the same fields as `getPartnerContract`) and billing integration status. Internal negotiation notes are not part of it.\n`operationalCredits` is the one account a partner has for its credits: the account of the person who administers it. It holds `balance` (the same balance `getPartnerCreditBalance` reads), `reservedBalance`, `availableBalance` (what a new operation can use) and `recentTransactions` (the ten newest entries of the ledger `getPartnerCreditTransactions` lists). A partner with no administrator has no account, and reads 0 and an empty list.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"Partner ID","format":"uuid","in":"path"}]},{"name":"get-partner-contract","aliases":[],"operationId":"getPartnerContract","method":"GET","path":"/partners/{partnerId}/contract","server":"api","summary":"Get a partner's contract","description":"Returns the commercial terms agreed with the partner: price, included credits and storage, billing\ncycle, commitment, support level, and the dates the contract runs between.\n\nA partner reads its own: the caller must be the person who administers the partner (an API key of\nthat account works), and anyone else is refused with 403 whether or not the partner exists. Widgetic\nadministrators can read any partner's. Responds 404 while no contract has been recorded for the\npartner, which is the case until Widgetic sets its terms.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose contract to read","format":"uuid","in":"path"}]},{"name":"get-partner-credit-balance","aliases":[],"operationId":"getPartnerCreditBalance","method":"GET","path":"/partners/{partnerId}/credits/balance","server":"api","summary":"Get partner credit balance","description":"Returns the credits the partner has, and how much of that is reserved by operations that are still running.\nA partner has ONE account for its credits: the account of the person who administers it, the same account\nits API key acts as. There is nothing else to create or to fund. What its customers' editor sessions and its\nown API calls spend comes out of that account, and the credits a paid invoice carries are added to it, so\nthis is the same balance `getUserCreditBalance` reads for that person.\n\nA partner with no administrator has no account to hold credits, and reads a balance of 0.\n\nAccess: the person who administers the partner, or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"Partner ID","format":"uuid","in":"path"}]},{"name":"get-partner-credit-transactions","aliases":[],"operationId":"getPartnerCreditTransactions","method":"GET","path":"/partners/{partnerId}/credits/transactions","server":"api","summary":"Get partner credit transaction history","description":"Lists what happened to the partner's credits, newest first: the entries of the credit ledger of the account of\nthe person who administers it, the one account a partner has for its credits. Every operation that spends\ncredits is a `debit` entry, and the credits an invoice carried arrive as `credit` entries when the invoice is\npaid, so this is also how the partner's usage is read. It is the same ledger `getUserCreditTransactions`\nreads for that person.\n\nA partner with no administrator has no account, and an empty ledger.\n\nThe page is always bounded: `limit` defaults to 20 and is capped at 100, `offset` defaults to 0.\n\nAccess: the person who administers the partner, or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"Partner ID","format":"uuid","in":"path"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"Number of transactions to return","default":20,"minimum":1,"maximum":100,"in":"query"},{"flag":"offset","name":"offset","type":"integer","required":false,"description":"Number of transactions to skip","default":0,"minimum":0,"in":"query"},{"flag":"type","name":"type","type":"string","required":false,"description":"Only entries of this type, for instance `debit` for the credits spent. The types are listed on `AccountCreditTransaction`.","in":"query"}]},{"name":"get-partner-data-database","aliases":[],"operationId":"getPartnerDataDatabase","method":"GET","path":"/partners/{partnerId}/data-database","server":"api","summary":"See the Neon database a partner keeps the data of its compositions in","description":"Returns the database set with `setPartnerDataDatabase`, without its password: the host, the database and the\nrole, when it last passed the check, and why the latest re-check failed if it did. Responds 404 while the\npartner has no database of its own, which means the data of its compositions is kept by Widgetic.\n\nThe caller must be the person who administers the partner (an API key of that account works, with the\n`partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose database to read","format":"uuid","in":"path"}]},{"name":"get-partner-design-defaults","aliases":[],"operationId":"getPartnerDesignDefaults","method":"GET","path":"/partners/{partnerId}/design-defaults","server":"api","summary":"Read partner-wide design defaults","description":"","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-partner-entry-context-signing","aliases":[],"operationId":"getPartnerEntryContextSigning","method":"GET","path":"/partners/{partnerId}/entry-context-signing","server":"api","summary":"See how the context of your embeds is signed","description":"Says whether you have a secret to sign the `context` of your embeds with, when it was made, whether the secret\nit replaced is still accepted, and whether an entry without a valid signature is refused (`required`). The\nsecret itself is never part of the answer: it is shown once, when it is made\n(`rotatePartnerEntryContextSecret`, which also explains what signing is for and how to sign).\n\nSigning covers the entries written through the gateway into the compositions of your managed users and into\nthose of your own account. The caller must be the person who administers the partner (an API key of that\naccount works, with the `partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose context signing to read","format":"uuid","in":"path"}]},{"name":"get-partner-notification-settings","aliases":[],"operationId":"getPartnerNotificationSettings","method":"GET","path":"/partners/{partnerId}/notifications/settings","server":"api","summary":"Where your notifications go","description":"Returns who gets the emails, which events you want by email and by webhook, the address of your webhook, and\nwhether it has a secret to sign with. The secret itself is never returned: it is shown once, when it is made.\n`events` lists every kind of event there is, for a settings screen.\n\nThe caller must be the person who administers the partner (an API key of that account works, with the\n`partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose notification settings to read","format":"uuid","in":"path"}]},{"name":"get-partner-storage-destination","aliases":[],"operationId":"getPartnerStorageDestination","method":"GET","path":"/partners/{partnerId}/storage","server":"api","summary":"Get where a partner's uploaded files are stored","description":"Returns the partner's storage destination without its secret: the endpoint, bucket, folder and public\naddress, the last four characters of the access key id, when the destination last passed the check, and\nwhy the latest re-check failed if it did. Responds 404 while the partner has no destination, which means\nthe partner cannot be activated, its uploads are refused, and no editor session is made for its customers\nuntil a destination is set.\n\nThe caller must be the person who administers the partner, or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose storage destination to read","format":"uuid","in":"path"}]},{"name":"get-partner-storage-usage","aliases":[],"operationId":"getPartnerStorageUsage","method":"GET","path":"/partners/{partnerId}/storage/usage","server":"api","summary":"What a partner's customers store","description":"Says how many files, and how many bytes, your account keeps, in total and for each of your managed users, and\nhow much of it is in Widgetic's storage and how much in your own storage destination\n(`setPartnerStorageDestination`). Use it to see which customers store the most, or to charge them for it.\n\nA file is counted for a managed user when it was filed under that user: by naming `managed_user_id` or\n`managed_user_external_id` when you upload it (`uploadsPost`, or the metadata of a resumable upload), or\nbecause it was uploaded through that user's editor session, which files everything under its user. The files\nthat are not filed under anyone (your own, and the ones uploaded without naming a managed user) are the row\nwhose `managedUserId` is null. Deleted files are not counted. To list the files of one managed user, use the\n`managed_user_id` filter of `uploadsGet`.\n\nThe answer is counted when you ask, so it is always current. The groups are ordered by size, the heaviest\nfirst, and `page.hasMore` says whether more follow.\n\nThe caller must be the person who administers the partner (an API key of that account works, with the\n`partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose storage to report on","format":"uuid","in":"path"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"How many managed users to list at most","default":50,"minimum":1,"maximum":100,"in":"query"},{"flag":"offset","name":"offset","type":"integer","required":false,"description":"How many managed users to skip","default":0,"minimum":0,"maximum":100000,"in":"query"}]},{"name":"list-partner-notification-deliveries","aliases":[],"operationId":"listPartnerNotificationDeliveries","method":"GET","path":"/partners/{partnerId}/notifications/deliveries","server":"api","summary":"How your notifications reached you","description":"Lists the sends of your notifications, the newest first: each email to one address and each call to your\nwebhook, with whether it was delivered, when it is tried again if not, how many attempts it had, and why the\nlast one failed. Use it to find out why a webhook of yours did not receive an event.\n\nAn email counts as delivered when it was handed to the mail service; a call to your webhook counts as\ndelivered when your endpoint answered with a 2xx status. Anything else is tried again, after 1 minute, then\n5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours and a day, eight attempts in all, because your address is\nread again at each attempt and you may have mended it. Two answers end the delivery at once: `410 Gone` and\n`413 Payload Too Large`.\n\nThe caller must be the person who administers the partner (an API key of that account works, with the\n`partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose deliveries to list","format":"uuid","in":"path"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"How many deliveries to list at most","default":100,"minimum":1,"maximum":200,"in":"query"}]},{"name":"list-partner-notifications","aliases":[],"operationId":"listPartnerNotifications","method":"GET","path":"/partners/{partnerId}/notifications","server":"api","summary":"What your partner account was told","description":"Lists the notifications of a partner, the newest first: the invoices Widgetic issued and their payments, the\nreminders to pay them, a warning when your credits run low, and the changes to the standing of the account\n(activated, paused, resumed). It is the same list your dashboard shows, and each entry is what your webhook\nreceives as `data` when you have one (`updatePartnerNotificationSettings`).\n\nA page holds `limit` notifications (20 by default, at most 100). When there are older ones `nextBefore` is\nset: ask again with it as `before` to get the next page.\n\nThe caller must be the person who administers the partner (an API key of that account works, with the\n`partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose notifications to list","format":"uuid","in":"path"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"How many notifications to list at most","default":20,"minimum":1,"maximum":100,"in":"query"},{"flag":"before","name":"before","type":"string","required":false,"description":"Only the notifications made before this moment. Give the `nextBefore` of the previous page.","format":"date-time","in":"query"}]},{"name":"list-partner-presets","aliases":[],"operationId":"listPartnerPresets","method":"GET","path":"/partners/{partnerId}/presets","server":"api","summary":"List the widgets a partner offers as presets","description":"Returns the partner's presets, ordered by `position` and then by `key`: the widgets it offers its managed\nusers as starting points. The list is the same for every managed user.\n\nUse a preset in one of two ways:\n- **Make a composition of it** (the content and design editor): call `createComposition` with the preset's\n `widgetId` and the managed user. The widget itself stays yours; a composition is the user's own content.\n- **Give the user a copy to build on** (the widget creator): call `copyPresetForManagedUser`. The user gets\n a widget and a canvas of their own.\n\nPresets are not the same as the widgets Widgetic puts on every new Widgetic account. Those are chosen by\nWidgetic and are copied at sign-up. A preset here is yours, and is copied only when you ask.\n\nThe caller must be the person who administers the partner (an API key of that account works), or a\nWidgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose presets to list","format":"uuid","in":"path"}]},{"name":"list-partner-workflow-presets","aliases":[],"operationId":"listPartnerWorkflowPresets","method":"GET","path":"/partners/{partnerId}/workflow-presets","server":"api","summary":"List the workflows a partner offers as places to start","description":"The partner's starting points, ordered by `position` and then by `key`: the published workflows it offers its managed\nusers when they make a new workflow. Each says whether people can start from it now and, when they cannot, why. The\nlist is the same for every managed user, and a managed user is shown only what is available.\n\nA starting point is one of the partner account's own published workflows, built from compositions the account made\nfor itself (not for a customer or a team) and from widgets the partner already offers as presets\n(`setPartnerPreset`), and it uses at most 10 widgets. A partner offers up to 50.\n\nThe caller must be the person who administers the partner (an API key of that account works), or a Widgetic\nadministrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose starting points to list","format":"uuid","in":"path"}]},{"name":"remove-partner-adapter-connection","aliases":[],"operationId":"removePartnerAdapterConnection","method":"DELETE","path":"/partners/{partnerId}/adapter-connections/{adapterId}/{credentialKey}","server":"api","summary":"Remove an adapter key from a partner","description":"Removes a key an adapter was given. The adapter stops working for the widgets of the partner's customers that\nneed it: their visitors are told the feature is unavailable. Nothing else of the widgets changes, and connecting\na key again brings them back.\n\nRemoving a key that is not connected answers 200 with `removed: false`: the state asked for is the state it is\nin. A key of an adapter that has since been retired from the catalog can still be removed.\n\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner that keeps the key","format":"uuid","in":"path"},{"flag":"adapter-id","name":"adapterId","type":"string","required":true,"description":"The adapter the key is for","maxLength":40,"in":"path"},{"flag":"credential-key","name":"credentialKey","type":"string","required":true,"description":"The key's name within the adapter","maxLength":40,"in":"path"}]},{"name":"remove-partner-data-database","aliases":[],"operationId":"removePartnerDataDatabase","method":"DELETE","path":"/partners/{partnerId}/data-database","server":"api","summary":"Stop keeping the data of a partner's compositions in its own database","description":"Removes the partner's database from Widgetic: Widgetic forgets the connection string, and from then on the data\nof the partner's compositions is kept in Widgetic's database again. Nothing in your database is deleted.\n\nRemoving is refused with 409 `PARTNER_DATABASE_HOLDS_DATA` while any composition of the partner keeps its data in\nyour database: it would read as empty from then on, and what it holds would stay where nothing reads it. Delete\nthe compositions you no longer need, or keep the database. If Widgetic cannot check what your database holds\n(it cannot be reached) the answer is 503 `PARTNER_DATABASE_UNAVAILABLE`; check it with\n`verifyPartnerDataDatabase`, or set the same database again to repair its connection.\n\nRemoving a database the partner does not have is not an error: the answer is `removed: false`.\n\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose database to remove","format":"uuid","in":"path"}]},{"name":"remove-partner-preset","aliases":[],"operationId":"removePartnerPreset","method":"DELETE","path":"/partners/{partnerId}/presets/{key}","server":"api","summary":"Take a widget off the partner's presets","description":"Removes the preset under `key`. The widget itself is not touched, and neither are the copies and the\ncompositions already made from it.\n\nRemoving a preset the partner does not have is not an error: the answer is `removed: false`.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose preset to remove","format":"uuid","in":"path"},{"flag":"key","name":"key","type":"string","required":true,"description":"The key of the preset to remove","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","in":"path"}]},{"name":"remove-partner-storage-destination","aliases":[],"operationId":"removePartnerStorageDestination","method":"DELETE","path":"/partners/{partnerId}/storage","server":"api","summary":"Stop storing a partner's uploaded files in its own bucket","description":"Removes the partner's storage destination. Partner storage is not optional, so after this every upload of\nthe account, on any route, and the uploads of your customers' editor sessions are refused (409\n`PARTNER_STORAGE_REQUIRED`) until a destination is set again, and no editor session can be made. Files\nalready in the partner's bucket are not touched and are not moved: they stay where they are, at the\naddresses they were given.\n\nRemoving a destination the partner does not have is not an error: the answer is `removed: false`.\n\nThe caller must be the person who administers the partner, or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose storage destination to remove","format":"uuid","in":"path"}]},{"name":"remove-partner-workflow-preset","aliases":[],"operationId":"removePartnerWorkflowPreset","method":"DELETE","path":"/partners/{partnerId}/workflow-presets/{key}","server":"api","summary":"Stop offering a workflow as a place to start","description":"Removes the starting point under `key`. The workflow itself is not touched, and neither are the workflows people\nalready started from it.\n\nRemoving a starting point the partner does not offer is not an error: the answer is `removed: false`.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose starting point to remove","format":"uuid","in":"path"},{"flag":"key","name":"key","type":"string","required":true,"description":"The key of the starting point to remove","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","in":"path"}]},{"name":"replay-partner-notification-delivery","aliases":[],"operationId":"replayPartnerNotificationDelivery","method":"POST","path":"/partners/{partnerId}/notifications/deliveries/{deliveryId}/replay","server":"api","summary":"Send a webhook delivery again","description":"Sends one earlier **webhook** delivery again to your **current** webhook address, with the same event `id`\n(`webhook-id`) as the original notification so a receiver that remembers ids can ignore a duplicate, and a\nnew `createdAt` and signature made with your current secret. The attempt is recorded in the delivery log as a\nnew row that points at the delivery you replayed.\n\nSet a webhook address (and secret) under notification settings before you replay. Changing the settings needs\nan API key with full access. The caller must be the person who administers the partner, or a Widgetic\nadministrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose delivery to replay","format":"uuid","in":"path"},{"flag":"delivery-id","name":"deliveryId","type":"string","required":true,"description":"The delivery to send again (must be a webhook delivery of this partner)","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}]},{"name":"rotate-partner-entry-context-secret","aliases":[],"operationId":"rotatePartnerEntryContextSecret","method":"POST","path":"/partners/{partnerId}/entry-context-signing/secret","server":"api","summary":"Make a secret to sign the context of your embeds","description":"Makes a secret to sign the `context` of your embeds with, and returns it in `secret`: **keep it now, it is\nshown once** and cannot be read again. Keep it on your server only: whoever holds it can write contexts your\ncompositions accept. Call this again to make a new secret; the one it replaces is still accepted for a day, so\nthat your server can switch without an entry being refused (making another within that day ends the overlap\nwith the first).\n\n**What signing is for.** `<widgetic composition-id=\"...\" context='{\"customerId\":\"c_42\"}'>` hands attribution to\na widget, which sends it with every entry it saves. The page is yours but the browser is the visitor's:\nanybody can change that attribute and file an entry under another customer's id. When your server signs the\ncontext, the gateway refuses an entry whose context was changed, and the entries you keep hold attribution\nyour server wrote.\n\n**How to sign** (on your server, for each page you render):\n\n1. Take the context to give the widget. It may hold text and whole numbers only (within ±2^53, written without\n a fraction or an exponent).\n2. Choose when the signature stops being good: `widgetic_exp`, in seconds since 1970 (UTC), no more than\n seven days from now. A minute of difference between clocks is forgiven.\n3. Write the text to sign, with no spaces except those inside values:\n `widgetic-context.v1.<compositionId>.<widgetic_exp>.<json>`. `<compositionId>` is the id of the composition\n in lower case. `<json>` is the context as JSON with the keys in character-code order (so `Z` comes before\n `a`, and `10` before `9`), no spaces, strings escaped as JSON escapes them, and the two keys of the\n signature left out.\n4. Sign that text: HMAC-SHA256 with the 32 bytes the secret stands for (the base64 text after `ctxsec_`), and\n write the result as base64url without padding.\n5. Give the widget the context with two keys added: `widgetic_exp` and `widgetic_sig` (the signature). The whole\n attribute, the two keys included, stays within the limits of any context: at most 20 keys, 2048 bytes of\n JSON, strings up to 500 characters. The two keys take two of the 20 keys and 87 bytes of the 2048.\n\nA test vector, to check your code against before you rely on it. Secret\n`ctxsec_AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=` (the bytes 0 to 31), composition\n`7c9e6679-7425-40de-944b-e07fc1f90ae7`, `widgetic_exp` 1792000000 and context\n`{\"customerId\":\"c_42\",\"plan\":\"pro\",\"seats\":3}` sign the text\n`widgetic-context.v1.7c9e6679-7425-40de-944b-e07fc1f90ae7.1792000000.{\"customerId\":\"c_42\",\"plan\":\"pro\",\"seats\":3}`\nand give the signature `j6g7151F54tl9C8oEY9levaYSppHME4fAPZ7WPwL8C8`. (The secret is the example's, not a\nreal one.)\n\n**What the gateway does with an entry.** When you have a secret, an entry whose context carries a signature is\nchecked: a signature that does not match is refused with 403 `CONTEXT_SIGNATURE_INVALID`, and one that is past\n`widgetic_exp` with 403 `CONTEXT_SIGNATURE_EXPIRED`. When you also require signatures\n(`updatePartnerEntryContextSigning`) an entry whose context carries none is refused with 403\n`CONTEXT_SIGNATURE_REQUIRED`. The two keys are taken out before the entry is stored or announced: your entries\nand your `entry.created` events hold the context you wrote. A change you make reaches every part of the\ngateway within a minute.\n\n**What it does not do.** A signed context that has been seen (a page's source is public) can be sent again by\nanyone until `widgetic_exp`; when you must not accept the same context twice, put a value that is new for each\npage view in it (a `submissionId` you made up) and keep the ones you have seen. The signature covers the\nentries that reach Widgetic's gateway: a row that gets into a table another way (your own code writing\nstraight into a database you brought, or the record calls of the composition's owner) does not pass\nthrough it, and carries no context.\n\nRetry this call with the same `Idempotency-Key` if its answer was lost: the answer, secret included, is then\ngiven again, and no second secret is made. This needs an API key with full access. The caller must be the\nperson who administers the partner, or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose secret to make","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}]},{"name":"rotate-partner-webhook-secret","aliases":[],"operationId":"rotatePartnerWebhookSecret","method":"POST","path":"/partners/{partnerId}/notifications/webhook-secret","server":"api","summary":"Make a new secret for your webhook","description":"Makes a new secret to sign your webhook with, and returns it in `webhookSecret`: **keep it now, it is shown\nonce** and cannot be read again. Use it when the secret was lost, or may have leaked.\n\nThe secret you had keeps signing beside the new one for a day, so that your receiver can switch without a\ncall being rejected: during that day each call carries two signatures (see `updatePartnerNotificationSettings`\nfor how they are verified). Rotating again within the day ends the overlap with the first secret.\n\nRetry this call with the same `Idempotency-Key` if its answer was lost: the answer, secret included, is then\ngiven again, and no second secret is made.\n\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose webhook secret to replace","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}]},{"name":"send-partner-notification-test-event","aliases":[],"operationId":"sendPartnerNotificationTestEvent","method":"POST","path":"/partners/{partnerId}/notifications/test-event","server":"api","summary":"Send a test event to your webhook","description":"Sends ONE signed call, right now, to the webhook address you saved under notification settings, with a sample\nof what an event of the type you name carries, and answers how it went: whether your receiver answered with a\n2xx status, which status, how long it took, and why not when it did not arrive. Use it to write and try your\nreceiver (above all the signature check) before a real event happens.\n\nThe call is made like every other: it is signed with your current secret (and the previous one while a\nrotation overlaps), it carries the headers `webhook-id`, `webhook-timestamp`, `webhook-signature` and\n`x-widgetic-event` (the event type), and its body is `{ \"id\", \"type\", \"createdAt\", \"partnerId\", \"data\" }`.\nThree things differ. `id` starts with `evt_test_` and is new every time, so a receiver that remembers ids can\ntell a test from an event and drop it. `data` holds made-up values of the shape a real event of that type\ncarries (`PartnerNotification` describes each), never anything about your account. And the call is made once:\nit is not tried again, nothing is stored, and it appears neither in the delivery log nor in the notification\nlist.\n\nIt is sent whatever `webhookEvents` says: a test is not filtered by what you subscribed to. The answer is 200\nwhether or not your receiver took the call (read `delivered`); a call that gets no answer within ten seconds\nis given up and reported as a `timeout`.\n\nA webhook address and a signing secret must be saved first (`updatePartnerNotificationSettings`); until then\nthe answer is 409. This needs an API key with full access, and `Idempotency-Key` is required: a retry with the\nsame key gets the first answer back and no second call is made. The caller must be the person who\nadministers the partner, or a Widgetic administrator. Five tests a minute.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose webhook to try","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","description":"Which kind of event to send a sample of.","required":["eventType"],"properties":{"eventType":{"$ref":"#/$defs/PartnerNotificationEventType"}},"example":{"eventType":null},"$defs":{"PartnerNotificationEventType":{"type":"string","description":"What happened. The event types you can be told about.","enum":["invoice.issued","invoice.due_soon","invoice.overdue","invoice.paid","invoice.failed","credits.low","credits.managed_user_cap_reached","partner.activated","partner.paused","partner.resumed","storage.check_failed","composition.created","composition.updated","widget.published","upload.created","entry.created","workflow.created","workflow.published","workflow.state_changed","workflow.deleted"]}}},"flags":[{"flag":"event-type","name":"eventType","type":"string","required":true,"description":"What happened. The event types you can be told about.","enum":["invoice.issued","invoice.due_soon","invoice.overdue","invoice.paid","invoice.failed","credits.low","credits.managed_user_cap_reached","partner.activated","partner.paused","partner.resumed","storage.check_failed","composition.created","composition.updated","widget.published","upload.created","entry.created","workflow.created","workflow.published","workflow.state_changed","workflow.deleted"],"property":"eventType"}]}},{"name":"set-partner-adapter-connection","aliases":[],"operationId":"setPartnerAdapterConnection","method":"PUT","path":"/partners/{partnerId}/adapter-connections/{adapterId}/{credentialKey}","server":"api","summary":"Connect an adapter key to a partner","description":"Gives an adapter the key it needs to act through a service the partner has an account with, for the widgets of\nthe customers the partner manages. Take the `adapterId` and `credentialKey` from\n`getPartnerAdapterConnections`. Saving a key that is already connected replaces it.\n\n**What Widgetic does with it.** The key is stored encrypted, bound to this partner, this adapter and this\ncredential, so that a stored value cannot be moved to another. It is never returned, never logged and never\nshown to a visitor of a widget or to your customers. A server of Widgetic opens it for the one call a widget\nmakes through the adapter, uses it to call the provider on the partner's behalf, and discards it. To cut that\noff, remove the key here, or revoke it with the provider.\n\n**What is checked.** That the adapter exists and takes this credential, that a partner may keep it (some are\nfor an owner only) and that the text is one line of at most 2048 characters. Whether the provider accepts the\nkey is not checked here: it shows the first time a widget uses it.\n\n**An address that has to be confirmed.** Some credentials are not a key but the email address Widgetic sends a\nwidget's messages to (`confirmation: email_code` in `getPartnerAdapterConnections`). Saving one checks that it is\none email address and emails a six-digit code to it; the answer says `awaitingConfirmation: true`, and nothing is\nsent to the address until the code is given back with `confirmPartnerAdapterConnection`. Saving the same address\nagain before it is confirmed emails a new code (not sooner than a minute after the last); saving the address that\nis already confirmed changes nothing. If the code cannot be emailed, nothing is saved.\n\nA widget whose adapter has no key connected, or whose address is not confirmed yet, is not charged and does not\nrun that adapter: its visitors are told the feature is unavailable, and the partner is told once a day what to do.\n\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator. A retry is harmless: saving the same key twice only refreshes when it was set.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner that keeps the key","format":"uuid","in":"path"},{"flag":"adapter-id","name":"adapterId","type":"string","required":true,"description":"The adapter the key is for","maxLength":40,"in":"path"},{"flag":"credential-key","name":"credentialKey","type":"string","required":true,"description":"The key's name within the adapter","maxLength":40,"in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["credential"],"additionalProperties":false,"properties":{"credential":{"type":"string","format":"password","writeOnly":true,"maxLength":2048,"description":"The key exactly as its provider gave it: one line of text, at most 2048 characters. It is stored encrypted and is never returned or logged. For a credential that is an email address (`confirmation: email_code`), write that one address, with nothing else","example":"YOUR_PROVIDER_KEY"}},"example":{"credential":"YOUR_PROVIDER_KEY"}},"flags":[{"flag":"credential","name":"credential","type":"string","required":true,"description":"The key exactly as its provider gave it: one line of text, at most 2048 characters. It is stored encrypted and is never returned or logged. For a credential that is an email address (`confirmation: email_code`), write that one address, with nothing else","maxLength":2048,"format":"password","secret":true,"property":"credential"}]}},{"name":"set-partner-data-database","aliases":[],"operationId":"setPartnerDataDatabase","method":"PUT","path":"/partners/{partnerId}/data-database","server":"api","summary":"Keep the data of a partner's compositions in a Neon database of its own","description":"Points the partner at a Neon database of its own. From then on, what visitors leave in the compositions of\nthe partner's managed users, and of the compositions made in the partner's own account (the votes, ratings,\nanswers and form submissions of the widgets that collect them), is stored in that database and nowhere else.\nWidgetic's own database is not used for them any more, and nothing of it is copied there.\n\n**What to give.** The connection string of a Neon database and a role made for this, as Neon shows it under\n*Connect* (`postgresql://<role>:<password>@ep-<name>.<region>.aws.neon.tech/<database>`). Only Neon databases\nare accepted; a string for any other host is refused with 400. What the database needs:\n- an empty database of its own, so that nothing else of yours shares it. Widgetic makes one schema in it for\n each composition (named `comp_` followed by the composition's id). In that schema go the tables the widget\n declares for what its visitors leave, and two small tables of Widgetic's own, whose names start with\n `_widgetic_`: the context a host page sent with an entry, and how far Widgetic has read the entries to tell\n you about new ones. It touches nothing else;\n- a role that may create schemas and tables in it (the CREATE privilege on the database). The role Neon makes\n with a project has it. A role made in the Neon console can also read and write every other database of the\n same project, so if you keep other data in Neon give Widgetic a project of its own, or make the role with\n SQL (`CREATE ROLE ... LOGIN PASSWORD ...`) and grant it only the CREATE privilege on this database;\n- connections from anywhere: Widgetic's addresses are not fixed, so a Neon project that only accepts\n connections from an allow list of addresses cannot be used. A suspended compute is fine: it wakes on the\n first request.\n\n**It is checked before it is saved.** Widgetic makes a schema in your database, creates a table in it, writes\na row, reads it back, and removes the schema again, all in one step, so that nothing is left if anything\nfails. If it cannot be done nothing is saved and the answer is 422 `PARTNER_DATABASE_UNVERIFIED`:\n`details.reason` says why (`INVALID_CREDENTIALS`, `DATABASE_NOT_FOUND`, `ACCESS_DENIED`, `ACCESS_BLOCKED`,\n`ENDPOINT_NOT_FOUND`, `ENDPOINT_UNREACHABLE`, `TIMEOUT`, `READ_ONLY`, `RATE_LIMITED`, `DATABASE_UNAVAILABLE`,\n`PROBE_MISMATCH` or `DATABASE_ERROR`) and `message` says what to do.\n\n**Existing data does not move.** The data of a composition lives in one database. If any composition of the\npartner already keeps data somewhere else (in Widgetic's database, or in the database you had set), and the\nnew database does not hold it, the change is refused with 409 `PARTNER_DATABASE_HOLDS_DATA` and nothing is\nchanged: those compositions would read as empty from then on. `details.compositionsWithData` says how many.\nSet your database before your customers collect data, or delete the compositions you no longer need. Saving\nthe same database again (a new password) moves nothing and is never refused.\n\n**It is never a fallback.** Once a database is set, a request that needs it while it cannot be used (the\npassword was changed, the project was deleted) is refused with 503 `PARTNER_DATABASE_UNAVAILABLE`. What the\nvisitor sent is not stored with Widgetic instead. Fix the connection and set the database again, or check it\nwith `verifyPartnerDataDatabase`.\n\n**When it takes effect.** A change reaches every part of Widgetic within about a minute.\n\n**What Widgetic keeps.** The connection string is stored encrypted and is never returned or logged; a response\nshows the host, the database and the role, never the password. Widgetic's servers open it to read and write the\ndata of your compositions while they handle a request for it. To cut that off, change the role's password in\nNeon: the compositions stop saving until you set the new string.\n\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator. A retry is harmless: saving the same database twice only refreshes when it was\nchecked and saved.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose database to set","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["connectionString"],"properties":{"connectionString":{"type":"string","format":"password","writeOnly":true,"description":"The connection string of a Neon database and role you made for this, as Neon shows it under Connect: `postgresql://<role>:<password>@ep-<name>.<region>.aws.neon.tech/<database>`. Either the direct or the pooled host works. Everything after the database name (`?sslmode=...`) is ignored. It is stored encrypted and is never returned or logged.","example":"postgresql://widgetic_data:YOUR_PASSWORD@ep-cool-name-123456.eu-central-1.aws.neon.tech/neondb"}},"example":{"connectionString":"postgresql://widgetic_data:YOUR_PASSWORD@ep-cool-name-123456.eu-central-1.aws.neon.tech/neondb"}},"flags":[{"flag":"connection-string","name":"connectionString","type":"string","required":true,"description":"The connection string of a Neon database and role you made for this, as Neon shows it under Connect: `postgresql://<role>:<password>@ep-<name>.<region>.aws.neon.tech/<database>`. Either the direct or the pooled host works. Everything after the database name (`?sslmode=...`) is ignored. It is stored encrypted and is never returned or logged.","format":"password","secret":true,"property":"connectionString"}]}},{"name":"set-partner-design-defaults","aliases":[],"operationId":"setPartnerDesignDefaults","method":"PUT","path":"/partners/{partnerId}/design-defaults","server":"api","summary":"Set or clear partner-wide design defaults","description":"Send `{ \"design\": { ... } }` or `{ \"design\": null }` to remove. New compositions merge this layer over the\nwidget base and under each managed user's default and the request body.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["design"],"properties":{"design":{"type":["object","null"]}}},"flags":[]}},{"name":"set-partner-preset","aliases":[],"operationId":"setPartnerPreset","method":"PUT","path":"/partners/{partnerId}/presets/{key}","server":"api","summary":"Make a widget a preset, or replace a preset","description":"Offers one of the partner's own widgets as a preset under `key`. If the partner already has a preset under\nthat key it is replaced, so sending the same request twice is safe. Saving replaces the whole preset: a\n`title`, `description` or `position` you leave out goes back to its default.\n\nKeys are 1 to 64 characters: lowercase letters, digits, hyphens and underscores, the first a letter or a\ndigit (`rating`, `quiz-3`).\n\nThe widget must belong to the partner's account; a widget that does not exist and one that belongs to\nsomebody else are refused in the same way (404 `PRESET_WIDGET_NOT_FOUND`). A widget is a preset under at\nmost one key (409 `PRESET_WIDGET_ALREADY_USED`; `details.key` says which), and an account can have at\nmost 100 presets (409 `PRESET_LIMIT_REACHED`).\n\nLater changes to the widget reach new copies and new compositions; copies made earlier are not touched.\nDeleting the widget removes its preset.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose preset to set","format":"uuid","in":"path"},{"flag":"key","name":"key","type":"string","required":true,"description":"The name your code uses for the preset","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","additionalProperties":false,"required":["widgetId"],"properties":{"widgetId":{"type":"string","format":"uuid","description":"One of the partner's own widgets. A widget is a preset under at most one key."},"title":{"type":["string","null"],"minLength":1,"maxLength":120,"description":"What your picker shows. Leave it out to show the widget's own name."},"description":{"type":["string","null"],"maxLength":500},"position":{"type":"integer","minimum":0,"maximum":10000,"default":0,"description":"Where the preset sits in the list. Smallest first; presets with the same position are ordered by key."}},"example":{"widgetId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","title":"Example title","description":"Example description","position":42}},"flags":[{"flag":"widget-id","name":"widgetId","type":"string","required":true,"description":"One of the partner's own widgets. A widget is a preset under at most one key.","format":"uuid","property":"widgetId"},{"flag":"title","name":"title","type":"string","required":false,"description":"What your picker shows. Leave it out to show the widget's own name.","minLength":1,"maxLength":120,"property":"title"},{"flag":"description","name":"description","type":"string","required":false,"description":"","maxLength":500,"property":"description"},{"flag":"position","name":"position","type":"integer","required":false,"description":"Where the preset sits in the list. Smallest first; presets with the same position are ordered by key.","default":0,"minimum":0,"maximum":10000,"property":"position"}]}},{"name":"set-partner-storage-destination","aliases":[],"operationId":"setPartnerStorageDestination","method":"PUT","path":"/partners/{partnerId}/storage","server":"api","summary":"Set where a partner's uploaded files are stored","description":"Points the partner at a bucket of its own on an S3-compatible service (Cloudflare R2, Amazon S3,\nWasabi, MinIO and the like). From then on, every file the account that administers the\npartner stores is kept in that bucket instead of with Widgetic, whichever way it arrives: a file sent with\n`uploadsPost` (with an API key, an SDK token, an editor session or from the Widgetic website), a resumable\nupload, the media library, and the files the agent tools and the MCP server save. It takes none of the\naccount's storage allowance, and its `url` is served from `publicBaseUrl`. The chunks of a resumable\nupload wait in Widgetic's temporary staging area until it is completed and are removed then: the finished\nfile is stored in your bucket and nowhere else.\n\nA destination is required. Partner storage is not optional: your customers' files, and your own however they\narrive, are kept in this destination and nowhere else. An editor session\n(`createManagedUserEditorSession`) of any surface cannot be made until a destination is set (409\n`PARTNER_STORAGE_REQUIRED`), a destination removed later stops the uploads of the sessions already made\n(the same 409), and your own upload with no destination (on any route, including a resumable upload, which\nis refused when it is started) is refused in the same way instead of being stored with Widgetic. A partner\ncannot be activated until it has a destination that works.\n\nThe destination is checked before it is saved. Widgetic writes a small test file under\n`<keyPrefix>widgetic-check/`, reads it back from `<publicBaseUrl>/<its key>`, and deletes it. If any\nstep fails nothing is saved and the answer is 422 `STORAGE_DESTINATION_UNVERIFIED`: `details.step` says\nwhere (`endpoint`, `write` or `public_read`), `details.reason` says why, and `param` names the field most\nlikely to be wrong.\n\nWhat the bucket needs:\n- an access key that may put objects into it (it may also delete objects, which lets the test file be\n cleaned up, but that is not required);\n- its files publicly readable at `publicBaseUrl`, either because the bucket is public or through a CDN.\n\nThe secret access key is stored encrypted and is never returned or logged; a response shows only the last\nfour characters of the access key id (`accessKeyHint`). Saving replaces the whole destination, so send\nevery field again, including the secret. Files already stored stay where they are, and keep working for\nas long as that bucket stays publicly readable.\n\nOnce a destination is set, an upload is never stored with Widgetic instead: when the destination cannot be\nused the upload is refused with 503 `STORAGE_DESTINATION_UNAVAILABLE`, and when the bucket rejects the\nfile with 502 `STORAGE_DESTINATION_WRITE_FAILED`.\n\nThe caller must be the person who administers the partner (an API key of that account works); anyone else\nis refused with 403 whether or not the partner exists. Widgetic administrators can set any partner's.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose storage destination to set","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","description":"The bucket to keep the partner's files in, and the key that may write to it. The field names are camelCase, like every answer of the API; the snake_case spelling (`keyPrefix`) is read too.\n","additionalProperties":false,"required":["endpoint","region","bucket","publicBaseUrl","accessKeyId","secretAccessKey"],"properties":{"endpoint":{"type":"string","description":"The HTTPS address of the S3-compatible service, scheme and host only (no path, port or credentials). It must be a host name, not an IP address, and must resolve to the public internet.\n","example":"https://abc123.r2.cloudflarestorage.com"},"region":{"type":"string","description":"The region the service signs requests for. Use auto where the service says so (such as Cloudflare R2).","example":"auto"},"bucket":{"type":"string","example":"acme-widget-media"},"keyPrefix":{"type":"string","default":"","description":"A folder inside the bucket to write every file under, for example widgetic/uploads. Folders are made of letters, digits, dots, hyphens and underscores. Leave it out to write at the root of the bucket.\n","example":"widgetic/"},"publicBaseUrl":{"type":"string","description":"Where the stored files are served from: the bucket's public address, or a CDN in front of it. The address of a file is this value, a slash and the file's key in the bucket, which starts with the key prefix. A path is allowed (https://media.example.com/files/acme-widget-media).\n","example":"https://media.example.com"},"forcePathStyle":{"type":"boolean","default":true,"description":"Put the bucket name in the path of a request. Every S3-compatible service supports this; set it to false only for a service that needs the bucket name in the host name.\n"},"accessKeyId":{"type":"string","description":"The id of an access key that may put objects into the bucket"},"secretAccessKey":{"type":"string","format":"password","writeOnly":true,"description":"The secret of that access key. It is stored encrypted and is never returned."}},"example":{"endpoint":"https://abc123.r2.cloudflarestorage.com","region":"auto","bucket":"acme-widget-media","keyPrefix":"widgetic/","publicBaseUrl":"https://media.example.com","forcePathStyle":true,"accessKeyId":"Example accessKeyId","secretAccessKey":"Example secretAccessKey"}},"flags":[{"flag":"endpoint","name":"endpoint","type":"string","required":true,"description":"The HTTPS address of the S3-compatible service, scheme and host only (no path, port or credentials). It must be a host name, not an IP address, and must resolve to the public internet.","property":"endpoint"},{"flag":"region","name":"region","type":"string","required":true,"description":"The region the service signs requests for. Use auto where the service says so (such as Cloudflare R2).","property":"region"},{"flag":"bucket","name":"bucket","type":"string","required":true,"description":"","property":"bucket"},{"flag":"key-prefix","name":"keyPrefix","type":"string","required":false,"description":"A folder inside the bucket to write every file under, for example widgetic/uploads. Folders are made of letters, digits, dots, hyphens and underscores. Leave it out to write at the root of the bucket.","default":"","property":"keyPrefix"},{"flag":"public-base-url","name":"publicBaseUrl","type":"string","required":true,"description":"Where the stored files are served from: the bucket's public address, or a CDN in front of it. The address of a file is this value, a slash and the file's key in the bucket, which starts with the key prefix. A path is allowed (https://media.example.com/files/acme-widget-media).","property":"publicBaseUrl"},{"flag":"force-path-style","name":"forcePathStyle","type":"boolean","required":false,"description":"Put the bucket name in the path of a request. Every S3-compatible service supports this; set it to false only for a service that needs the bucket name in the host name.","default":true,"property":"forcePathStyle"},{"flag":"access-key-id","name":"accessKeyId","type":"string","required":true,"description":"The id of an access key that may put objects into the bucket","property":"accessKeyId"},{"flag":"secret-access-key","name":"secretAccessKey","type":"string","required":true,"description":"The secret of that access key. It is stored encrypted and is never returned.","format":"password","secret":true,"property":"secretAccessKey"}]}},{"name":"set-partner-workflow-preset","aliases":[],"operationId":"setPartnerWorkflowPreset","method":"PUT","path":"/partners/{partnerId}/workflow-presets/{key}","server":"api","summary":"Offer a workflow as a place to start, or replace what a key offers","description":"Offers one of the partner account's own published workflows to its managed users under `key`. If the partner already\noffers something under that key it is replaced, so sending the same request twice is safe. Saving replaces the whole\nstarting point: a `title`, `description` or `position` you leave out goes back to its default.\n\nKeys are 1 to 64 characters: lowercase letters, digits, hyphens and underscores, the first a letter or a digit.\n\nA workflow is offered once. What people get is the workflow's live version as it is when they start, so publishing\nthe workflow changes what new starts get; workflows people already started are never touched. The offer is\nrefused, and `error.details.reason` says why, when the workflow:\n\n- is not the partner account's own (a workflow of a team or of a customer, or of somebody else), or does not exist:\n 404, the same for both;\n- is archived (409 `workflow_archived`), or was never published (409 `workflow_not_published`);\n- shows no composition (422 `workflow_empty`), or a block shows a composition made for a customer or a team\n (422 `composition_not_own`);\n- chains more than 10 widgets (422 `too_many_widgets`), or a widget that the partner does not offer as a preset\n (422 `widget_not_preset`; `error.details.widgets` lists them, each with its `id` and `name`, so they can be offered\n first with `setPartnerPreset`);\n- is already offered under another key (409 `workflow_already_offered`; `error.details.key` says which), or the\n partner already offers 50 (409 `too_many_presets`).\n\nThe caller must be the person who administers the partner (an API key of that account works), or a Widgetic\nadministrator, who offers the partner's workflow on its behalf.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose starting point to set","format":"uuid","in":"path"},{"flag":"key","name":"key","type":"string","required":true,"description":"The name your code and the picker use for the starting point","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","additionalProperties":false,"required":["workflowId"],"properties":{"workflowId":{"type":"string","format":"uuid","description":"One of your own published workflows."},"title":{"type":["string","null"],"minLength":1,"maxLength":120,"description":"What the picker shows. Leave it out to show the workflow's name."},"description":{"type":["string","null"],"maxLength":500},"position":{"type":"integer","minimum":0,"maximum":10000,"default":0,"description":"Where it sits in the list. Smallest first; the same position is ordered by key."}},"example":{"workflowId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","title":"Example title","description":"Example description","position":42}},"flags":[{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"One of your own published workflows.","format":"uuid","property":"workflowId"},{"flag":"title","name":"title","type":"string","required":false,"description":"What the picker shows. Leave it out to show the workflow's name.","minLength":1,"maxLength":120,"property":"title"},{"flag":"description","name":"description","type":"string","required":false,"description":"","maxLength":500,"property":"description"},{"flag":"position","name":"position","type":"integer","required":false,"description":"Where it sits in the list. Smallest first; the same position is ordered by key.","default":0,"minimum":0,"maximum":10000,"property":"position"}]}},{"name":"update-partner-entry-context-signing","aliases":[],"operationId":"updatePartnerEntryContextSigning","method":"PUT","path":"/partners/{partnerId}/entry-context-signing","server":"api","summary":"Require a signed context, or stop requiring it","description":"Chooses what the gateway does with an entry whose `context` carries no valid signature.\n\nWith `required: false` (the default) an entry that carries a signature is checked and refused when the\nsignature is wrong or out of date, and an entry that carries none is accepted, as it always was. With\n`required: true` an entry without a valid signature is refused with 403 `CONTEXT_SIGNATURE_REQUIRED`: use it\nonce every page of yours that embeds a composition signs its context.\n\nSignatures cannot be required before you have made a secret (`rotatePartnerEntryContextSecret`): the answer\nis 409, so that you cannot lock your own compositions out of taking entries. The change reaches every part of\nthe gateway within a minute.\n\nSaving the same choice twice leaves the same state, so a retry is harmless and `Idempotency-Key` is optional.\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose context signing to change","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":false,"description":"Optional: without it the call is carried out every time it is sent. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["required"],"properties":{"required":{"type":"boolean","description":"True to refuse every entry whose context carries no valid signature. False to check the signature of the entries that carry one and accept the others, as before."}},"example":{"required":true}},"flags":[{"flag":"required","name":"required","type":"boolean","required":true,"description":"True to refuse every entry whose context carries no valid signature. False to check the signature of the entries that carry one and accept the others, as before.","property":"required"}]}},{"name":"update-partner-notification-settings","aliases":[],"operationId":"updatePartnerNotificationSettings","method":"PUT","path":"/partners/{partnerId}/notifications/settings","server":"api","summary":"Choose where your notifications go","description":"Changes who gets the emails, which events you want by email and by webhook, and the address of your webhook.\nA key you leave out of the body is left as it was; send only what changes.\n\n**Email.** `emailRecipients` is the list of addresses (at most five). While it is empty the emails about\ninvoices go to the billing emails of your billing profile (your contact email when there are none), and the\nothers to your contact email. The events about money and about the standing of your account are always\nemailed, whatever `emailEvents` says; it narrows the others.\n\n**Webhook.** `webhookUrl` is where Widgetic sends a signed `POST` for each event, so that your own system\ncan act on it (mark an invoice paid, warn your team that credits are low). The address must be `https` on the\nstandard port, on a public site, and must not redirect. Setting an address when you have no secret yet makes\none, and the answer carries it as `webhookSecret`: **keep it now, it is shown once**. `webhookEvents` narrows\nwhat is sent.\n\nEach call has a JSON body, `{ \"id\": \"evt_...\", \"type\": \"invoice.paid\", \"createdAt\": \"...\", \"partnerId\": \"...\",\n\"data\": { ... } }` (`data` is the `data` of `PartnerNotification`), and these headers:\n`webhook-id` (the same as `id`, and the same on every retry), `webhook-timestamp` (seconds since 1970) and\n`webhook-signature` (`v1,` and a base64 HMAC-SHA256). It is the Standard Webhooks scheme, so any of its\nlibraries verifies it. By hand: take your secret without the `whsec_` prefix, decode it from base64, and sign\nthe text `<webhook-id>.<webhook-timestamp>.<the raw body>` with it; the header holds `v1,<base64 of the\nresult>` (two signatures separated by a space while a rotated secret overlaps). Reject a `webhook-timestamp`\nthat is more than five minutes old.\n\nAnswer with a 2xx status within ten seconds. Anything else is tried again, eight attempts in all over about\ntwo days (`listPartnerNotificationDeliveries` shows each). Delivery is at least once: if your answer was lost\nthe same event arrives again with the same `id`, so drop what you have already handled by it.\n\nChanging the settings needs an API key with full access: they decide where your notifications go.\nThe caller must be the person who administers the partner, or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose notification settings to change","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":false,"description":"Optional: without it the call is carried out every time it is sent. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","description":"What to change. A key you leave out is left as it was.","properties":{"emailRecipients":{"type":"array","maxItems":5,"description":"The addresses the emails go to, at most five. An empty list goes back to the defaults.","items":{"type":"string"}},"emailEvents":{"type":["array","null"],"description":"The events you want by email. Null for every event.","items":{"$ref":"#/$defs/PartnerNotificationEventType"}},"webhookEvents":{"type":["array","null"],"description":"The events to send to your webhook. Null for every event.","items":{"$ref":"#/$defs/PartnerNotificationEventType"}},"webhookUrl":{"type":["string","null"],"maxLength":2000,"description":"Where to send the signed calls. It must be an https address on the standard port, on a public site, and it must not redirect. Null or an empty text switches the webhook off. Setting an address when you have no secret yet makes one."}},"example":{"emailRecipients":[],"emailEvents":["Example PartnerNotificationEventType"],"webhookEvents":["Example PartnerNotificationEventType"],"webhookUrl":"Example webhookUrl"},"$defs":{"PartnerNotificationEventType":{"type":"string","description":"What happened. The event types you can be told about.","enum":["invoice.issued","invoice.due_soon","invoice.overdue","invoice.paid","invoice.failed","credits.low","credits.managed_user_cap_reached","partner.activated","partner.paused","partner.resumed","storage.check_failed","composition.created","composition.updated","widget.published","upload.created","entry.created","workflow.created","workflow.published","workflow.state_changed","workflow.deleted"]}}},"flags":[{"flag":"email-recipients","name":"emailRecipients","type":"string[]","required":false,"description":"The addresses the emails go to, at most five. An empty list goes back to the defaults.","property":"emailRecipients"},{"flag":"email-events","name":"emailEvents","type":"string[]","required":false,"description":"The events you want by email. Null for every event.","enum":["invoice.issued","invoice.due_soon","invoice.overdue","invoice.paid","invoice.failed","credits.low","credits.managed_user_cap_reached","partner.activated","partner.paused","partner.resumed","storage.check_failed","composition.created","composition.updated","widget.published","upload.created","entry.created","workflow.created","workflow.published","workflow.state_changed","workflow.deleted"],"property":"emailEvents"},{"flag":"webhook-events","name":"webhookEvents","type":"string[]","required":false,"description":"The events to send to your webhook. Null for every event.","enum":["invoice.issued","invoice.due_soon","invoice.overdue","invoice.paid","invoice.failed","credits.low","credits.managed_user_cap_reached","partner.activated","partner.paused","partner.resumed","storage.check_failed","composition.created","composition.updated","widget.published","upload.created","entry.created","workflow.created","workflow.published","workflow.state_changed","workflow.deleted"],"property":"webhookEvents"},{"flag":"webhook-url","name":"webhookUrl","type":"string","required":false,"description":"Where to send the signed calls. It must be an https address on the standard port, on a public site, and it must not redirect. Null or an empty text switches the webhook off. Setting an address when you have no secret yet makes one.","maxLength":2000,"property":"webhookUrl"}]}},{"name":"verify-partner-data-database","aliases":[],"operationId":"verifyPartnerDataDatabase","method":"POST","path":"/partners/{partnerId}/data-database/verify","server":"api","summary":"Check that a partner's own database still works","description":"Runs the same check as `setPartnerDataDatabase` against the database already saved: a schema is made in it, a\ntable created, a row written and read back, and the schema removed again. Use it after changing the role's\npassword or the project's settings in Neon, or when saving is failing with 503 `PARTNER_DATABASE_UNAVAILABLE`.\n\nThe result is recorded either way. A pass refreshes `verifiedAt` and clears `lastVerificationError`; a failure\nanswers 422 `PARTNER_DATABASE_UNVERIFIED` and is recorded in `lastVerificationError` and\n`lastVerificationFailedAt`, so the database shows what went wrong the next time it is read. A failed check does\nnot remove the database. `details.reason` is one of the reasons `setPartnerDataDatabase` lists, or\n`STORED_STRING_UNREADABLE` when the connection string Widgetic stored can no longer be read: set the database\nagain, with its connection string.\n\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose database to check","format":"uuid","in":"path"}]},{"name":"verify-partner-storage-destination","aliases":[],"operationId":"verifyPartnerStorageDestination","method":"POST","path":"/partners/{partnerId}/storage/verify","server":"api","summary":"Check that a partner's storage destination still works","description":"Runs the same check as `setPartnerStorageDestination` against the destination already saved: a test\nfile is written, read back from the public address and deleted. Use it after rotating a key or changing\na bucket policy, or when uploads are failing with 502 or 503.\n\nThe result is recorded either way. A pass refreshes `verifiedAt` and clears `lastVerificationError`; a\nfailure answers 422 `STORAGE_DESTINATION_UNVERIFIED` and is recorded in `lastVerificationError` and\n`lastVerificationFailedAt`, so the destination shows what went wrong the next time it is read. A failed\ncheck does not remove the destination or stop uploads from using it.\n\nThe caller must be the person who administers the partner, or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose storage destination to check","format":"uuid","in":"path"}]}]},{"name":"public-embed","title":"Public Embed","description":"The Public Embed operations of the Widgetic API.","hidden":true,"commands":[{"name":"call-public-composition-adapter","aliases":[],"operationId":"callPublicCompositionAdapter","method":"POST","path":"/embed/compositions/{compositionId}/adapters/{adapterId}/{operation}","server":"root","summary":"Call a gateway operation of an adapter a published widget uses","description":"The door a widget's adapter module calls, from the visitor's browser, for the operations that cannot run in the browser: anything with a secret, a charge in credits or the owner's storage behind it. A widget that uses such an adapter is handed the address of this route as `adapters.url` in its `widgetic:host` message (see the host bridge contract) and calls `{adapters.url}/{adapterId}/{operation}`. It is not meant to be called by hand, and it takes no API key.\n**Address.** `https://api.widgetic.com/embed/compositions/{compositionId}/adapters/{adapterId}/{operation}`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.\n**What it serves.** Only an adapter, and a version of it, that the widget's LIVE manifest (what the version that is published uses) lists, and only an operation that runs on the gateway. Anything else is one 404 `ADAPTER_NOT_AVAILABLE`, whatever the reason, so the route is no way to ask what a widget contains. An adapter an administrator stopped offering to NEW widgets keeps serving the widgets that already use it.\n**Who may call.** The same rule as the entry route: a permitted `Origin` header, which is the artifact origin the widget runs from or any website when the owner has not restricted the allowed websites (when the owner has, one of the websites the owner listed, Widgetic's own sites, the visitor's own machine or the owner's own platform). Page script cannot forge it. A request without a permitted `Origin` is 403 `ORIGIN_NOT_ALLOWED`. A composition that is not served (unpublished, or its owner's plan does not serve it) is 404, with the codes of the other public embed routes. When the partner of the owner signs the host context, a context that is not signed as the partner requires is refused with 403 `CONTEXT_SIGNATURE_REQUIRED`, `CONTEXT_SIGNATURE_INVALID` or `CONTEXT_SIGNATURE_EXPIRED`.\n**Cost and retries.** An operation priced in credits is charged to the owner of the widget (for a customer a partner manages, to the partner's administrator, within the monthly credits set for that customer), once for each `requestId`, before it runs, and given back if it does not answer. A `requestId` that was charged and not given back is 409 `REQUEST_ALREADY_USED` and the operation is NOT run a second time; one whose charge was given back may be tried again with the same `requestId`. A visitor never sees an owner's balance: a feature that cannot be used because its owner has no credits left, because the customer's monthly credits are spent, or because an administrator stopped the adapter, is the same 503 `ADAPTER_UNAVAILABLE` as every other \"not right now\". The owner is told through a notice.\n**Limits.** In front of everything, 120 calls a minute for each visitor address and composition and 600 a minute for a composition (429 `RATE_LIMITED`). Then, for each operation of a widget, a rate for each visitor address a minute and a cap for the widget a day: 30 and 5000 for an operation that is free, 10 and 500 for one that costs credits, unless the adapter says otherwise (never above 120 and 100000). A visitor over the rate is 429 `RATE_LIMITED`; a widget over its cap is 429 `WIDGET_LIMIT_REACHED`. Both carry a `Retry-After` header. The body is at most 64 KiB (413 `PAYLOAD_TOO_LARGE`) and JSON (415 `UNSUPPORTED_MEDIA_TYPE`); an operation has 30 seconds to answer (504 `ADAPTER_TIMEOUT`, and the charge is given back).","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"composition-id","name":"compositionId","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"adapter-id","name":"adapterId","type":"string","required":true,"description":"The id of the adapter, the name its module is known by, for example `upload`.","maxLength":40,"pattern":"^[a-z][a-z0-9]*(-[a-z0-9]+)*$","in":"path"},{"flag":"operation","name":"operation","type":"string","required":true,"description":"The name of one operation of the adapter, as its documentation gives it.","maxLength":40,"pattern":"^[a-z][a-z0-9]*(-[a-z0-9]+)*$","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["requestId"],"additionalProperties":false,"properties":{"requestId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_-]{7,63}$","description":"Names this attempt: 8 to 64 characters, letters, digits, `_` and `-`, the first a letter or a digit (a UUID fits). A module makes a new one for each action of the visitor. An operation that costs credits is charged once for each `requestId`, so a repeat of one that was charged and not given back is refused (409 `REQUEST_ALREADY_USED`) and does no work."},"version":{"type":"integer","minimum":1,"maximum":1000,"description":"The version of the adapter the widget's code was built against (the `vN` of its import). When it is left out, the newest version the widget's live manifest lists."},"input":{"type":"object","additionalProperties":true,"description":"What the operation is asked to do. Each operation says what it accepts and answers 400, 413 or 422 with a code of its own for what it cannot use. Defaults to an empty object."},"context":{"type":["object","null"],"additionalProperties":true,"description":"The host context the embedding page gave the widget, as the entry route takes it (see the description of `createPublicCompositionEntry`, with its **Signed context**). It is not authenticated unless the partner signs it. At most 4096 bytes of JSON, the two signature keys included."},"captchaToken":{"type":["string","null"],"maxLength":4096,"description":"The visitor's CAPTCHA token, for an operation that asks for one (the answer `CAPTCHA_REQUIRED` says so)."}},"example":{"requestId":"Example requestId","version":42,"input":{},"context":{},"captchaToken":"Example captchaToken"}},"flags":[{"flag":"request-id","name":"requestId","type":"string","required":true,"description":"Names this attempt: 8 to 64 characters, letters, digits, `_` and `-`, the first a letter or a digit (a UUID fits). A module makes a new one for each action of the visitor. An operation that costs credits is charged once for each `requestId`, so a repeat of one that was charged and not given back is refused (409 `REQUEST_ALREADY_USED`) and does no work.","pattern":"^[A-Za-z0-9][A-Za-z0-9_-]{7,63}$","property":"requestId"},{"flag":"version","name":"version","type":"integer","required":false,"description":"The version of the adapter the widget's code was built against (the `vN` of its import). When it is left out, the newest version the widget's live manifest lists.","minimum":1,"maximum":1000,"property":"version"},{"flag":"captcha-token","name":"captchaToken","type":"string","required":false,"description":"The visitor's CAPTCHA token, for an operation that asks for one (the answer `CAPTCHA_REQUIRED` says so).","maxLength":4096,"property":"captchaToken"}]}},{"name":"create-public-composition-entry","aliases":[],"operationId":"createPublicCompositionEntry","method":"POST","path":"/embed/compositions/{compositionId}/entries/{table}","server":"root","summary":"Append a visitor entry through the gateway","description":"Public append-only write into a published composition database. No API key. The browser must send a permitted `Origin` header: the artifact iframe origin, or any website when the owner has not restricted the allowed websites (when the owner has, one of the websites the owner listed, Widgetic's own sites, the visitor's own machine or the owner's own platform). When the request comes from the shared artifact origin, it must also carry the per-composition write token (`X-Widgetic-Write-Token` header or `writeToken` body field, handed to the widget in `widgetic:host` as `entries.writeToken` and returned as `entryWriteToken` on `getPublicCompositionEmbed`). Host `context` is stored for partner attribution; it is not authenticated unless the partner signs it (see **Signed context**). Request bodies are limited to 16 KB before parsing.\n**Address.** `https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.\n**Signed context.** A partner that wants to trust `context` (to bill a customer by it, say) makes a signing secret (`rotatePartnerEntryContextSecret`), and its server signs the context it hands to the embed. The signature travels inside `context` as two reserved keys, `widgetic_exp` (UTC seconds, at most seven days ahead) and `widgetic_sig`. The gateway checks them after the `Origin` and before anything else is judged, stored or announced, and keeps and announces the context WITHOUT them. A context that is not signed as the partner requires is refused with 403 `CONTEXT_SIGNATURE_REQUIRED`, `CONTEXT_SIGNATURE_INVALID` or `CONTEXT_SIGNATURE_EXPIRED`; when the check cannot be made just now the answer is a retryable 503 `CONTEXT_SIGNATURE_UNAVAILABLE`, never an unchecked entry. Until a partner makes a secret nothing changes: the two keys are dropped and the rest is taken as it came. A signature is good until its `widgetic_exp` and may be used again until then, so a partner that must count each submission once keeps a unique value of its own (a `submissionId`, say) in the context. The recipe and a test vector are in the description of `rotatePartnerEntryContextSecret`.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"composition-id","name":"compositionId","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"","in":"path"},{"flag":"write-token","name":"X-Widgetic-Write-Token","type":"string","required":false,"description":"Signed per-composition write token handed to the widget in `widgetic:host` as `entries.writeToken` (and returned by `GET /embed/compositions/{compositionId}` as `entryWriteToken`). Required when the request comes from the shared Widgetic artifact origin; verified whenever present.","in":"header"},{"flag":"visitor-id","name":"X-Widgetic-Visitor-Id","type":"string","required":false,"description":"A random id the widget's browser makes once and keeps (16 to 64 characters of `A-Za-z0-9_-`), sent so a table's per-visitor `limit` (declared in the widget database file) can count this browser. Not an identity and not a secret. Without it, or with a malformed value, the limit counts only by network address.","pattern":"^[A-Za-z0-9_-]{16,64}$","in":"header"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["values"],"properties":{"values":{"type":"object","additionalProperties":true,"description":"Column values to insert (never id or time columns)."},"context":{"type":["object","null"],"description":"Host attribution from the embedding page. It is not authenticated unless the partner signs it (see the description of this operation): never use it for authorization on its own. At most 20 keys; string values up to 500 characters or finite numbers; 2048 bytes JSON total. A signed context carries two more keys, `widgetic_exp` and `widgetic_sig`: the gateway checks them and drops them, so they are neither stored nor announced."},"writeToken":{"type":["string","null"],"description":"Optional per-composition write token (`entries.writeToken` from `widgetic:host`, or `entryWriteToken` from `GET /embed/compositions/{compositionId}`) when not passed in the `X-Widgetic-Write-Token` header."}},"example":{"values":{},"context":{},"writeToken":"Example writeToken"}},"flags":[{"flag":"body-write-token","name":"writeToken","type":"string","required":false,"description":"Optional per-composition write token (`entries.writeToken` from `widgetic:host`, or `entryWriteToken` from `GET /embed/compositions/{compositionId}`) when not passed in the `X-Widgetic-Write-Token` header.","property":"writeToken"}]}},{"name":"get-public-composition-embed","aliases":[],"operationId":"getPublicCompositionEmbed","method":"GET","path":"/embed/compositions/{compositionId}","server":"root","summary":"Resolve a composition UUID to a published widget URL","description":"Public customer embed lookup. No API key. Used by embed-sdk.js on third-party sites.\nIf the composition's owner listed the websites it may appear on (the workspace's domain list, or the `allowedDomains` of a partner the owner administers), a request from any other website is refused with 403 `DOMAIN_NOT_ALLOWED`. The website is read from the `Origin` header, or the host of `Referer` when there is no `Origin`; a request that names neither (a server, a tool) is not refused. An owner who listed nothing is not restricted, and a page on the visitor's own machine (`localhost`, `127.0.0.1` or `[::1]`, on any port) is always allowed. So is the website where a partner integrates Widgetic, with every subdomain of it (`platformDomain` in the answer of `GET /partners/mine`), for the widgets of that partner: a partner's list never has to name it. The list stops other websites from showing the embed in a visitor's browser; it does not hide the published content from someone who holds the composition ID.\n**Address.** `https://api.widgetic.com/embed/compositions/{compositionId}`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.\n**Limit.** 500 calls for each visitor address and browser in one counting window, shared with the frame and the status routes. Over it the answer is 429 `RATE_LIMITED` with a `Retry-After` header.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"composition-id","name":"compositionId","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-public-composition-iframe","aliases":[],"operationId":"getPublicCompositionIframe","method":"GET","path":"/embed/compositions/{compositionId}/frame","server":"root","summary":"Embed a published composition with its saved values","description":"Public HTML wrapper with an inner published artifact iframe. No API key or SDK required.\nIf the composition's owner listed the websites it may appear on, the page is sent with `Content-Security-Policy: frame-ancestors` naming those websites (and Widgetic's own, and the website where the partner the owner administers integrates Widgetic, with its subdomains), so a browser refuses to show it inside any other website whatever `Referer` policy that website uses. Opening the address directly still works. An owner who listed nothing is not restricted.\n**Address.** `https://api.widgetic.com/embed/compositions/{compositionId}/frame`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"composition-id","name":"compositionId","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-public-workflow","aliases":[],"operationId":"getPublicWorkflow","method":"GET","path":"/embed/workflows/{workflowId}","server":"root","summary":"Read the published Workflow of an embed","description":"Public read of a workflow for the embed that carries `workflow-id`. No API key. The answer is the live version (its graph, the composition a new visitor starts on, the time zone, the languages its compositions can be shown in, and the token that lets the browser report counts), or `status: paused` with nothing else, in which case the embed shows only its first composition. A workflow that is a draft, archived or unknown answers 404, and the embed then behaves as a plain embed of its `composition-id`. So does a workflow whose owner is not being served (a plan that lapsed, a partner whose agreement is not in force): the answer is the one the compositions of that owner give.\n**Address.** `https://api.widgetic.com/embed/workflows/{workflowId}`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.\nThe answer may be reused for a few seconds (`Cache-Control: public, max-age=15`), so a publish or a pause reaches visitors within moments. The graph is readable by anyone who has the workflow's id: its rules run in the visitor's browser, so a rule must never hold a secret.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-public-workflow-frame","aliases":[],"operationId":"getPublicWorkflowFrame","method":"GET","path":"/embed/workflows/{workflowId}/frame","server":"root","summary":"Show a published Workflow in a page of its own","description":"A page that runs a published workflow, for a place that can only be given an address or an iframe: a chat messenger, a link page, a site builder, a blog that follows oEmbed. No API key, no SDK to add. The page holds the Embed SDK and the same tag a website carries (`<widgetic workflow-id composition-id>`), so a visitor moves from one composition to the next in place, by the same rules, exactly as on a website. A paused workflow shows its first composition alone, as its embeds do.\n**What the frame can count.** Only what happens inside it: entries, a widget's goals and actions, and waits. The page events a website can send to a workflow (`widgetic:workflow-event`) are events of the page around the embed, and they do not cross an iframe, so a connection that waits for one never fires from a frame.\nA workflow that is a draft, is archived, or does not exist, and one whose owner is not being served, answers 404 with a short page that gives no reason. If the owner of the workflow's first composition listed the websites it may appear on, the page is sent with `Content-Security-Policy: frame-ancestors` naming those websites (and Widgetic's own), so a browser refuses to show it inside any other website. Opening the address directly still works. An owner who listed nothing is not restricted.\nThe page links its oEmbed card, so a site that follows oEmbed turns the address of the frame into an embed.\n**Address.** `https://api.widgetic.com/embed/workflows/{workflowId}/frame`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-public-workflow-version","aliases":[],"operationId":"getPublicWorkflowVersion","method":"GET","path":"/embed/workflows/{workflowId}/versions/{versionId}","server":"root","summary":"Read one published version of a Workflow","description":"Public read of one version of a workflow, for a visitor who started on it before a newer one was published: they finish on the version they started on (for up to 30 days; after that they get the latest). The graph of a version never changes once published; the languages its compositions can be shown in (`translations`) are read each time, as for the live version. Like the read of the live version, it answers `status: paused` with nothing else while the workflow is paused, and 404 when the workflow is a draft or archived, or has no such version.\n**Address.** `https://api.widgetic.com/embed/workflows/{workflowId}/versions/{versionId}`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"version-id","name":"versionId","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"post-public-composition-embed-status","aliases":[],"operationId":"postPublicCompositionEmbedStatus","method":"POST","path":"/embed/compositions/status","server":"root","summary":"Batch-check whether compositions are currently served for embed","description":"Public batch health check for embed-sdk.js. No API key. Uses the same serving gate and domain policy as `GET /embed/compositions/{compositionId}`: the caller's website is read from `Origin`, or the host of `Referer` when there is no `Origin`. A request that names neither (a server or tool) is not refused for domain reasons.\n**Address.** `https://api.widgetic.com/embed/compositions/status`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.\n**Limit.** The route counts against the same limit as the lookup and the frame routes: 500 calls for each visitor address and browser in one counting window. A call is ONE hit whatever the number of `ids` it carries (up to 100), so a server that checks many compositions spends one call for each hundred of them. A visitor who is over the limit gets 429 `RATE_LIMITED` for the WHOLE call, with a `Retry-After` header and no state for any id: there is no per-composition rate-limit state in the answer.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["ids"],"properties":{"ids":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"string","format":"uuid"}}}},"flags":[{"flag":"ids","name":"ids","type":"string[]","required":true,"description":"","property":"ids"}]}},{"name":"report-public-workflow-events","aliases":[],"operationId":"reportPublicWorkflowEvents","method":"POST","path":"/embed/workflows/{workflowId}/events","server":"root","summary":"Report what visitors did in a Workflow, as counts","description":"Public report of counts for a workflow, sent by the embed. No API key. Each event is one id and a number: a visitor reached a block (`n`), took a connection (`e`), or could not be moved on (`f`). Nothing else is kept: no visitor, no value a visitor typed, no address. A batch carries at most 100 events and 16 KB. The same report may carry `heights`: how tall a step settled at in the browser, which the embed code of the workflow uses to start its box at a real height (a composition, a whole number of pixels, nothing about the visitor).\nA report is accepted only with the `token` the workflow's own read handed out for that `versionId`, so a page that never read the workflow cannot raise its numbers. An expired token (`EVENT_TOKEN_EXPIRED`) is refused with 401: read the workflow again for a new one. Counts for ids that are not in that version, or for a workflow that is not live, are not recorded and the answer says so (`recorded: false`) with 200: the visitor's page has nothing to fix.\nThe body is JSON, sent as `application/json` or as `text/plain` (a \"simple\" cross-origin request, which is what lets a report leave a page that is being closed).\n**Address.** `https://api.widgetic.com/embed/workflows/{workflowId}/events`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","description":"One report from a visitor's browser: counts (`events`), the heights steps settled at (`heights`), or both. A report\nwith neither is refused.\n","required":["versionId","token"],"properties":{"versionId":{"type":"string","format":"uuid"},"token":{"type":"string","description":"The `eventToken` of the workflow, read for the same version."},"events":{"type":"array","maxItems":100,"description":"The counts of what visitors did. At least one of `events` and `heights` is needed.","items":{"type":"object","required":["k","id"],"properties":{"k":{"type":"string","enum":["n","e","f"],"description":"`n` a visitor reached a block, `e` a visitor took a connection, `f` moving a visitor on failed."},"id":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$","description":"The block or connection counted."},"c":{"type":"integer","minimum":1,"maximum":50,"description":"How many times (1 when not given)."}}}},"heights":{"type":"array","maxItems":10,"description":"How tall steps of this version settled in the visitor's browser, so that the embed code of the workflow can\nstart its box at the height visitors saw instead of a guess. The Embed SDK sends one for a visitor who has no\nremembered height for the step. The gateway keeps the latest few for each composition (the median is what the\ncode gets); a composition that this version does not show is left out. At least one of `events` and `heights`\nis needed.\n","items":{"type":"object","required":["compositionId","height"],"properties":{"compositionId":{"type":"string","format":"uuid","description":"The composition the step shows."},"height":{"type":"integer","minimum":1,"maximum":100000,"description":"The height the step settled at, in pixels. One under 100 is not kept, one over 2000 is kept as 2000."}}}}},"example":{"versionId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","token":"Example token","events":[],"heights":[]}},"flags":[{"flag":"version-id","name":"versionId","type":"string","required":true,"description":"","format":"uuid","property":"versionId"},{"flag":"token","name":"token","type":"string","required":true,"description":"The `eventToken` of the workflow, read for the same version.","property":"token"}]}},{"name":"upload-public-composition-adapter-file","aliases":[],"operationId":"uploadPublicCompositionAdapterFile","method":"PUT","path":"/embed/compositions/{compositionId}/adapters/upload/files/{ticket}","server":"root","summary":"Send the file a visitor chose through the upload adapter of a published widget","description":"The second of the two calls the `upload` adapter's module makes from the visitor's browser (the first, the `begin-upload` operation of `callPublicCompositionAdapter`, answers with the ticket this route takes). The module sends the file itself, as the body, with its media type as the `Content-Type`. It is not meant to be called by hand, and it takes no API key.\n**Address.** `https://api.widgetic.com/embed/compositions/{compositionId}/adapters/upload/files/{ticket}`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.\n**The ticket.** Single-use and valid for ten minutes, given for one file: its name, its media type and its exact size are what the visitor said when the ticket was asked for, after the widget's owner's settings (the kinds of file they take, the largest size) were applied. The `Content-Type` and the `Content-Length` of this request must be those, and the body must be exactly that many bytes (400 `UPLOAD_FILE_CHANGED` otherwise). The gateway never believes the type it was told: the first bytes of the file must be a file of that type and one the owner accepts (422 otherwise). A ticket whose file was stored and is sent again is answered with the same file, so a visitor whose connection dropped after the file landed does not store it twice; a ticket whose attempt failed is spent, and the module asks for a new one.\n**The virus check.** Before anything is stored, the file is checked for viruses, wherever it would be kept (Widgetic's storage or a partner's own bucket). A file that fails the check is refused with 422 `UPLOAD_FILE_UNSAFE` and nothing is stored. When the check cannot be done, nothing is stored either, and the answer is the same neutral 503 `UPLOAD_UNAVAILABLE` as for any other cause the visitor is not told. The ticket of a refused file is spent.\n**What it stores.** The file goes to the widget owner's storage: Widgetic's, or the partner's own bucket for a widget of a customer a partner manages. It counts against the owner's storage allowance and is filed in the folder the owner chose, as a file sent by a visitor, which the owner's own file list leaves out. A full allowance, a partner's storage that cannot be used and a stopped feature are all the same 503 `UPLOAD_UNAVAILABLE` to a visitor, and the owner is told through a notice.\n**Who may call.** The same rule as the entry route: a permitted `Origin` header, which is the artifact origin the widget runs from or any website when the owner has not restricted the allowed websites (when the owner has, one of the websites the owner listed, Widgetic's own sites, the visitor's own machine or the owner's own platform). A request without a permitted `Origin` is 403 `ORIGIN_NOT_ALLOWED`. A composition that is not served (unpublished, or its owner's plan does not serve it) is 404, with the codes of the other public embed routes.\n**Limits.** 30 requests a minute for each visitor address and composition and 240 a minute for a composition (429 `RATE_LIMITED`, with a `Retry-After` header); at most 10 MB for a file, and only so many files held in memory at once on each gateway process (503 `UPLOAD_BUSY`, with a `Retry-After`). A file that does not arrive within two minutes is 408 `UPLOAD_TIMEOUT`. Every answer but a success closes the connection, so a client that is refused before it has sent the file reads the answer and stops.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"composition-id","name":"compositionId","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"ticket","name":"ticket","type":"string","required":true,"description":"The ticket the `begin-upload` operation answered with.","minLength":20,"maxLength":128,"pattern":"^[A-Za-z0-9_-]{20,128}$","in":"path"}],"body":{"required":true,"kind":"binary","contentType":"application/pdf","contentTypes":["application/pdf","audio/aac","audio/flac","audio/mp4","audio/mpeg","audio/ogg","audio/wav","audio/webm","image/gif","image/heic","image/heif","image/jpeg","image/png","image/webp","video/mp4","video/quicktime","video/webm"],"flags":[]}}]},{"name":"tokens","title":"Tokens","description":"The Tokens operations of the Widgetic API.","hidden":false,"commands":[{"name":"validate-token","aliases":[],"operationId":"validateToken","method":"GET","path":"/tokens/validate","server":"api","summary":"Validate current authentication token","description":"","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[]}]},{"name":"uploads","title":"Uploads","description":"The Uploads operations of the Widgetic API.","hidden":false,"commands":[{"name":"uploads-get","aliases":["list"],"operationId":"uploadsGet","method":"GET","path":"/uploads","server":"api","summary":"List the account's uploads","description":"The files the account has uploaded, newest first. Deleted files are not listed. Returns at most `limit` files and has no further pages: a client that needs more narrows the list instead, for example to one managed user's files with `managedUserId` or `managedUserExternalId`. The files that visitors sent through a widget's upload adapter (`source` is `widget-visitor-upload`) are not part of this list, so that a popular widget does not push the account's own files out of it; naming one widget with `widgetId` lists what visitors sent to that widget as well.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"limit","name":"limit","type":"integer","required":false,"description":"Maximum number of files to return","default":50,"minimum":1,"maximum":100,"in":"query"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"List only the files uploaded for this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both.","format":"uuid","in":"query"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"List only the files uploaded for the managed user you registered with this `externalId` (your own identifier for the user). Send this or `managedUserId`, not both.","maxLength":255,"in":"query"}]},{"name":"uploads-id-delete","aliases":["delete"],"operationId":"uploadsIdDelete","method":"DELETE","path":"/uploads/{id}","server":"api","summary":"Delete an upload","description":"Removes the stored file and the upload row. A file in Widgetic's own storage gives back the space it took from\nyour storage allowance; a file in your own bucket is removed from your bucket. Deleting an upload that is\nalready gone succeeds the same way as the first delete. An upload that belongs to another account is not found.\nIf the storage cannot be reached the call fails and nothing is marked deleted, so it can be repeated.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"uploads-id-get","aliases":["get"],"operationId":"uploadsIdGet","method":"GET","path":"/uploads/{id}","server":"api","summary":"Get an upload by id","description":"The details of one of the account's uploads. An upload that belongs to another account is not found.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The upload to read","format":"uuid","in":"path"}]},{"name":"uploads-limits-get","aliases":[],"operationId":"uploadsLimitsGet","method":"GET","path":"/uploads/limits","server":"api","summary":"Upload size limits and accepted file types","description":"The size limit and the accepted file types of the upload endpoints. Read them from here rather than hard-coding thresholds: they are configuration and can change. The limits are the same for every account, whatever its plan: `multipart_max_bytes` for a file sent in one request (10 MB) and `tus_max_bytes` for a resumable upload (250 MB).","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[]},{"name":"uploads-post","aliases":["create"],"operationId":"uploadsPost","method":"POST","path":"/uploads","server":"api","summary":"Upload a file","description":"Stores one file for the account and returns its public address. This is how a user's own images,\nvideo and PDFs reach a widget's content.\n\nSend a `multipart/form-data` request with the file in the field named `file`. The file counts\nagainst the storage of the workspace it is uploaded in, and a full workspace is refused with 413 and\n`QUOTA_STORAGE_EXCEEDED`. For a person signed in on widgetic.com that is the workspace they are currently\nworking in: inside a team, the team's shared storage; inside their own workspace, their own allowance. An\norganization API key, and a Shopify store's session, always work in that team's shared storage. A personal\nAPI key or an SDK token always uses the account's own allowance. The two never add up and one never lends\nroom to the other. A file over the size limit is refused with 413 and `UPLOAD_TOO_LARGE`, and a\ntype that is not accepted with 415 and `UPLOAD_UNSUPPORTED_TYPE`; `uploadsLimitsGet` lists the size\nlimit and the accepted types.\n\nA file is judged by what is in it, not by the type it is sent as. A file that is not the type it claims (a\nweb page sent as `image/png`) is refused with 415 and `UPLOAD_UNSUPPORTED_TYPE`, and a picture sent under\nthe wrong picture type is stored under the type it really is, which `mime_type` in the answer says. An SVG\nis rewritten as it is stored: script, `foreignObject`, animation and references to other addresses are\ntaken out, it must be UTF-8 and at most 2 MB, and the stored file can be larger or smaller than the one\nsent. Anything that is not an image, audio or video (a PDF, for example) is served as a download rather\nthan shown in a page.\n\nWhen the full storage is a Shopify store's team's, extra storage cannot be added through Shopify yet, so the\nmessage ends by saying so and what to do instead: delete files, or contact support. On a Solo plan it names\nthe Teams plan (which holds more) and `error.details.planPageUrl` is the address of the plan page on Shopify.\n`error.suggestedAction` says the same in a few words, instead of the usual \"upgrade your storage plan\". Any\nother team, and a person's own workspace, get the shorter message.\n\nTo upload the file for one of your managed users, send `managedUserId` (Widgetic's id for the user) or\n`managedUserExternalId` (your own identifier for the user), not both. The file stays in your account and\ncounts against its allowance; the tag lets you list one user's files with `uploadsGet`. The managed user\nmust be yours and active: one that is not yours, or does not exist, is refused with 404 and\n`MANAGED_USER_NOT_FOUND`, and one that is switched off with 409 and `MANAGED_USER_INACTIVE`, before the\nfile is stored. The tag is set here and does not change afterwards.\n\nCalled with an editor session (`createManagedUserEditorSession`), the file is always filed for the\nsession's own managed user and is always media for widget content: naming another managed user is\nrefused with 403 and `MANAGED_USER_SESSION_MISMATCH`, and `contextType`, `source`, `widgetId` and\n`conversationId` are ignored. A full storage allowance is refused with the same 413 and\n`QUOTA_STORAGE_EXCEEDED`, but the message only says that it is not available right now (not how much is\nstored: that is the partner's business) and `error.details.limit` is `storage`.\n\nAn account that administers a partner with a storage destination (`setPartnerStorageDestination`) has\nits files stored in the partner's own bucket instead. Such a file takes none of the allowance, its `url`\nis served from the partner's public address, and its `storage_key` is the key in the partner's bucket.\nThe file is never stored with Widgetic instead: when the destination cannot be used the upload is refused\nwith 503 and `STORAGE_DESTINATION_UNAVAILABLE`, and when the partner's storage rejects the file with\n502 and `STORAGE_DESTINATION_WRITE_FAILED`.\n\nPartner storage is not optional, and it does not depend on how the file arrives. A partner's own uploads (with\nits API key, an SDK token, or from the Widgetic website, the resumable route and the media library alike)\nand the uploads of its customers' editor sessions are kept in the partner's own storage or not at all, so a\npartner with no destination (or one that removed it after the session was made) has them refused with 409\nand `PARTNER_STORAGE_REQUIRED`; they are never stored with Widgetic. An editor session's browser is only\ntold that the feature is not available right now.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[],"body":{"required":true,"kind":"multipart","contentType":"multipart/form-data","schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"The file to store"},"contextType":{"type":"string","enum":["widget-codegen","browser-ic","content-property"],"default":"content-property","description":"What the file is for. Send `content-property` for media that becomes widget content (images, video and PDF); the other values are used by Widgetic's own editor.\n"},"source":{"type":"string","enum":["chat-upload","chat-screenshot","browser-ic-screenshot","preview-screenshot","file-browser-upload"],"default":"file-browser-upload","description":"Which part of the product, or which client, the file comes from"},"managedUserId":{"type":"string","format":"uuid","description":"Upload the file for one of your managed users, by Widgetic's id for the user. Send this or `managedUserExternalId`, not both."},"managedUserExternalId":{"type":"string","maxLength":255,"description":"The same, by your own identifier for the user, the `externalId` you gave when you created the managed user. Send this or `managedUserId`, not both."},"purpose":{"type":"string","enum":["canvas","chat","field"],"description":"Where in your product you use the file. It is kept with the file, so that a customer's storage can be counted by purpose (`getManagedUserStorageUsage`). Optional; any other value is refused with 400 and `UPLOAD_INVALID_PURPOSE`."}}},"flags":[{"flag":"file","name":"file","type":"file","required":true,"description":"The file to store","property":"file"},{"flag":"context-type","name":"contextType","type":"string","required":false,"description":"What the file is for. Send `content-property` for media that becomes widget content (images, video and PDF); the other values are used by Widgetic's own editor.","enum":["widget-codegen","browser-ic","content-property"],"default":"content-property","property":"contextType"},{"flag":"source","name":"source","type":"string","required":false,"description":"Which part of the product, or which client, the file comes from","enum":["chat-upload","chat-screenshot","browser-ic-screenshot","preview-screenshot","file-browser-upload"],"default":"file-browser-upload","property":"source"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"Upload the file for one of your managed users, by Widgetic's id for the user. Send this or `managedUserExternalId`, not both.","format":"uuid","property":"managedUserId"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"The same, by your own identifier for the user, the `externalId` you gave when you created the managed user. Send this or `managedUserId`, not both.","maxLength":255,"property":"managedUserExternalId"},{"flag":"purpose","name":"purpose","type":"string","required":false,"description":"Where in your product you use the file. It is kept with the file, so that a customer's storage can be counted by purpose (`getManagedUserStorageUsage`). Optional; any other value is refused with 400 and `UPLOAD_INVALID_PURPOSE`.","enum":["canvas","chat","field"],"property":"purpose"}]}}]},{"name":"users","title":"Users","description":"The Users operations of the Widgetic API.","hidden":false,"commands":[{"name":"add-personal-workspace-domain","aliases":[],"operationId":"addPersonalWorkspaceDomain","method":"POST","path":"/users/{id}/workspace/domains","server":"api","summary":"Allow a website to show your widgets","description":"Adds a website to an account's own list. Give a website name such as `example.com`, or `*.example.com` for a website and all of its subdomains. A scheme, a port, a path, capital letters and a leading `www.` are dropped, so `https://www.Example.com/shop` is kept as `example.com`, and `example.com` covers `www.example.com` too. Something that is not a website name is refused, not stored.\nUntil the first website is added nothing is restricted, and a widget can be shown anywhere. Once the list has one, a widget of the account is shown on the listed websites (and on Widgetic's own) and nowhere else. A page on your own machine (`localhost`, `127.0.0.1` or `[::1]`, on any port) is always allowed, so there is nothing to add to test locally. So is the website where a partner your account administers integrates Widgetic, with every subdomain of it (`platformDomain` in the answer of `GET /partners/mine`), so there is nothing to add for it either and a list can never lock it out. A change can take about 15 seconds to reach every server.\nThe list is a safeguard against other websites showing your widgets in a visitor's browser. It is not access control: anyone who knows a widget's id can still fetch its published content with a plain request that names no website.\nThere is no limit on the list of an account that administers a partner, so a partner can add the website of each of its customers as they publish on their own domain, one call each. Any other account can list at most 50 websites, and the 51st is answered with a 409 (`BUSINESS_STATE_CONFLICT`). A wildcard such as `*.example.com` counts as one website, not one per subdomain, so use it instead of listing each subdomain.\nA page that frames a widget can name only so many websites to the browser (about fifty of ordinary length). Past that, the browser no longer checks the list for that page; the lookup that the Embed SDK makes for the widget still refuses every website that is not on the list.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the list belongs to. For your own, your own user id","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["domain"],"properties":{"domain":{"type":"string","description":"Domain pattern to allow (supports wildcards like *.example.com)","example":"example.com"}},"example":{"domain":"example.com"}},"flags":[{"flag":"domain","name":"domain","type":"string","required":true,"description":"Domain pattern to allow (supports wildcards like *.example.com)","property":"domain"}]}},{"name":"confirm-user-adapter-connection","aliases":[],"operationId":"confirmUserAdapterConnection","method":"POST","path":"/users/{userId}/adapter-connections/{adapterId}/{credentialKey}/confirm","server":"api","summary":"Confirm an adapter address with the code that was emailed to it","description":"Some credentials are an email address Widgetic sends a widget's messages to (`confirmation: email_code` in\n`getUserAdapterConnections`). Saving one with `setUserAdapterConnection` emails a six-digit code to it; this gives\nthe code back. Only then is the address used: until it is confirmed, the widgets that need it are not charged and\ntheir visitors are told the feature is unavailable.\n\nThe code works for 30 minutes and for five wrong tries. After that, save the address again and a new code is\nemailed (not sooner than a minute after the last one). A code that is not made of digits is refused without\ncounting as a try.\n\nConfirming an address that is already confirmed answers 200 with the same state, so a retry of a request that\nwent through is harmless. Saving a different address, or the same one before it was confirmed, starts again.\n\nThis needs an API key with full access. The caller must be the account itself or a Widgetic administrator.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"user-id","name":"userId","type":"string","required":true,"description":"The account that keeps the address. For your own, your own user id","format":"uuid","in":"path"},{"flag":"adapter-id","name":"adapterId","type":"string","required":true,"description":"The adapter the address is for","maxLength":40,"in":"path"},{"flag":"credential-key","name":"credentialKey","type":"string","required":true,"description":"The credential's name within the adapter","maxLength":40,"in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["code"],"additionalProperties":false,"properties":{"code":{"type":"string","maxLength":32,"description":"The digits of the code Widgetic emailed to the address, with nothing else. It works for 30 minutes and for five wrong tries; after that, save the address again and a new code is emailed","example":"123456"}},"example":{"code":"123456"}},"flags":[{"flag":"code","name":"code","type":"string","required":true,"description":"The digits of the code Widgetic emailed to the address, with nothing else. It works for 30 minutes and for five wrong tries; after that, save the address again and a new code is emailed","maxLength":32,"property":"code"}]}},{"name":"create-user-api-key","aliases":[],"operationId":"createUserApiKey","method":"POST","path":"/users/{id}/keys","server":"api","summary":"Create an API key","description":"Makes a new API key for an account and returns its secret in `key`. That is the only time the secret is shown: Widgetic keeps a hash, so a key that was not stored cannot be recovered, only replaced. To rotate a key, make the new one, switch the integration over to it, then delete the old one.\nOnly a person signed in on widgetic.com can make a key: a request made with an API key, or with an SDK token, is refused with 403. That way a leaked key cannot be used to make a second one that survives its revocation. Make keys from the dashboard; a key can still list, rename, switch off and delete keys.\nAn `Idempotency-Key` header is required (8 to 50 characters: letters, digits, `-` and `_`). A retry with the same header and the same body returns the same answer instead of making a second key, and the same header with a different body is refused with 422. Make keys one at a time: two requests for the same account that run at the same moment can overwrite each other.\nA key can be limited to what it needs. Send a `preset` (`read_only`, `content` or `full_access`) or a list of `scopes`, never both. Send neither for a key that can do everything its owner can, as every key made before scopes existed. A limited key is refused with 403 and the code `API_KEY_SCOPE_MISSING` on anything outside its scopes, so a key held by a build server or by a partner's backend can be given only the part of the API it uses. The scopes of a key are fixed when it is made: to widen one, make a new key and delete the old one. A limited key cannot make, change or delete keys, exchange tokens or reach the administration routes. The list of scopes is in the `ApiKeyScope` schema.\nAccounts with API access (Solo, Teams and partner accounts) have no limit on how many keys they hold. An account on a plan without API access cannot make one (a partner account is not asked about a plan).","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the key is for. For your own, your own user id","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"A name for the key, to tell it from the account's other keys","example":"Production"},"expiresAt":{"type":"string","format":"date-time","description":"When the key stops working, as a date and time in the future with a zone. Omit it for a key that does not expire","example":"2027-01-31T00:00:00Z"},"preset":{"type":"string","enum":["read_only","content","full_access"],"description":"A ready-made list of scopes. `read_only` looks at everything the account holds and changes nothing. `content` builds and manages widgets and compositions and runs the AI, and touches no billing, no people and no partners. `full_access` is the same as sending neither `preset` nor `scopes`. Send either `preset` or `scopes`, not both","example":"content"},"scopes":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/ApiKeyScope"},"description":"What the key may do, when a preset is not enough. Naming a write scope includes its read scope, and a list that holds every scope is a key with full access. Send either `scopes` or `preset`, not both","example":["content:read","generate"]}},"required":["name"],"example":{"name":"Production","expiresAt":"2027-01-31T00:00:00Z","preset":"content","scopes":["content:read","generate"]},"$defs":{"ApiKeyScope":{"type":"string","enum":["content:read","content:write","generate","partner:read","partner:write","billing:read","billing:write","account:read","account:write"],"description":"One thing an API key may be allowed to do. A key made with a list of scopes does only what the list\nnames; a key made without one has full access, and does everything its owner can.\n\n- `content:read` reads widgets, compositions, canvases, projects, folders, uploads, translations and the\n analytics of your content.\n- `content:write` creates, changes, publishes and deletes the same. It includes `content:read`.\n- `generate` runs the AI that builds widgets and compositions, and follows or cancels what it is doing.\n This spends credits.\n- `partner:read` reads managed users, what they spent, presets, the contract and the storage destination.\n- `partner:write` creates, changes and deletes managed users and presets. It includes `partner:read`.\n- `billing:read` reads credits, usage, invoices, subscriptions and payment methods.\n- `billing:write` buys credits, pays invoices and changes a subscription or a payment method. It\n includes `billing:read`.\n- `account:read` reads the profile, organizations and members, allowed domains, notices and the names\n of API keys.\n- `account:write` changes the profile, organizations and members, allowed domains and integrations. It\n includes `account:read`.\n\nStarting an editor session for a managed user needs `partner:write`, `content:write` and `generate`\ntogether. Making, changing and deleting keys, exchanging tokens, signing in and the administration\nroutes need full access: a limited key cannot make itself a bigger one.\n\nA request outside the scopes of the key is refused with 403 and the code `API_KEY_SCOPE_MISSING`.\n`error.details.missingScopes` names what a new key needs, and `error.details.fullAccessRequired` is true\nwhen no list of scopes would have been enough. The scopes of a key cannot be changed once it is made."}}},"flags":[{"flag":"name","name":"name","type":"string","required":true,"description":"A name for the key, to tell it from the account's other keys","minLength":1,"maxLength":100,"property":"name"},{"flag":"expires-at","name":"expiresAt","type":"string","required":false,"description":"When the key stops working, as a date and time in the future with a zone. Omit it for a key that does not expire","format":"date-time","property":"expiresAt"},{"flag":"preset","name":"preset","type":"string","required":false,"description":"A ready-made list of scopes. `read_only` looks at everything the account holds and changes nothing. `content` builds and manages widgets and compositions and runs the AI, and touches no billing, no people and no partners. `full_access` is the same as sending neither `preset` nor `scopes`. Send either `preset` or `scopes`, not both","enum":["read_only","content","full_access"],"property":"preset"},{"flag":"scopes","name":"scopes","type":"string[]","required":false,"description":"What the key may do, when a preset is not enough. Naming a write scope includes its read scope, and a list that holds every scope is a key with full access. Send either `scopes` or `preset`, not both","enum":["content:read","content:write","generate","partner:read","partner:write","billing:read","billing:write","account:read","account:write"],"property":"scopes"}]}},{"name":"delete-personal-workspace-domain","aliases":[],"operationId":"deletePersonalWorkspaceDomain","method":"DELETE","path":"/users/{id}/workspace/domains/{domainId}","server":"api","summary":"Stop allowing a website to show your widgets","description":"Removes one entry from an account's own list. When it was the last one, nothing is restricted any more and a widget can be shown anywhere again. A change can take about 15 seconds to reach every server.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the list belongs to. For your own, your own user id","format":"uuid","in":"path"},{"flag":"domain-id","name":"domainId","type":"string","required":true,"description":"The `id` of the entry, as the list shows it","format":"uuid","in":"path"}]},{"name":"delete-user-api-key","aliases":[],"operationId":"deleteUserApiKey","method":"DELETE","path":"/users/{id}/keys/{keyId}","server":"api","summary":"Delete an API key","description":"Deletes a key and the record of what it was used for. The key stops working at once on the server that took the request and within a minute on the others, and cannot be brought back: make a new one instead. To stop a key without losing it, switch it off with `updateUserApiKey`.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the key belongs to","format":"uuid","in":"path"},{"flag":"key-id","name":"keyId","type":"string","required":true,"description":"The `keyId` of the key, as the list shows it","in":"path"}]},{"name":"get-personal-workspace-domains","aliases":[],"operationId":"getPersonalWorkspaceDomains","method":"GET","path":"/users/{id}/workspace/domains","server":"api","summary":"List the websites allowed to show your widgets","description":"Lists the websites on an account's own list, newest first. It is the list Widgetic checks when a widget of the account is embedded: when it holds at least one website, the widget is shown on those websites (and on Widgetic's own) and nowhere else; when it is empty, the widget can be shown anywhere. If the account administers a partner, the websites set on the partner record by Widgetic are added to them. Widgets that belong to an organization use the organization's list instead.\nThe answer holds the whole list, however long it is: a partner's list has no limit.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the list belongs to. For your own, your own user id","format":"uuid","in":"path"}]},{"name":"get-user-adapter-connections","aliases":[],"operationId":"getUserAdapterConnections","method":"GET","path":"/users/{userId}/adapter-connections","server":"api","summary":"See which adapter keys an account has connected","description":"Widget adapters are capabilities a widget can use, and some of them act through a service the owner has an\naccount with: an adapter that sends email needs the owner's email provider key, one that sends texts needs the\nowner's SMS provider key. This lists every key an adapter takes from an owner and whether this account has\nconnected it, so a settings page can show what is missing.\n\nAn empty list means no adapter takes a key from an owner at the moment. The list never contains a key: only\nwhether one is connected and when it was last set.\n\nConnect a key with `setUserAdapterConnection`; take it away with `removeUserAdapterConnection`. The widgets of\ncustomers a partner manages use the partner's keys instead (`getPartnerAdapterConnections`).\n\nThe caller must be the account itself (an API key of that account works, with the `account:read` scope), or a\nWidgetic administrator.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"user-id","name":"userId","type":"string","required":true,"description":"The account whose adapter keys to list. For your own, your own user id","format":"uuid","in":"path"}]},{"name":"get-user-api-key-usage-logs","aliases":[],"operationId":"getUserApiKeyUsageLogs","method":"GET","path":"/users/{id}/api-keys/{keyId}/logs","server":"api","summary":"Read what an API key was used for","description":"Lists the calls made with one key, newest first: when, which path and method, and the status each ended with. Use it to see whether a key is still in use before deleting it, or to see what a key that should not be in use has been doing.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the key belongs to","format":"uuid","in":"path"},{"flag":"key-id","name":"keyId","type":"string","required":true,"description":"The `keyId` of the key, as the list shows it","in":"path"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"How many calls to return, newest first","default":500,"minimum":1,"maximum":500,"in":"query"}]},{"name":"get-user-api-keys","aliases":[],"operationId":"getUserApiKeys","method":"GET","path":"/users/{id}/keys","server":"api","summary":"List the API keys of an account","description":"Lists the API keys of an account, in the order they were made: name, the first characters of the key, when it was made, when it expires, when it was last used, whether it is switched on and what it is limited to (`scopes` and `preset`). The secret of a key is never part of the list. A caller can list its own account's keys; Widgetic staff can list any account's.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the keys belong to. For your own keys, your own user id","format":"uuid","in":"path"}]},{"name":"remove-user-adapter-connection","aliases":[],"operationId":"removeUserAdapterConnection","method":"DELETE","path":"/users/{userId}/adapter-connections/{adapterId}/{credentialKey}","server":"api","summary":"Remove an adapter key from an account","description":"Removes a key an adapter was given. The adapter stops working for the widgets that need it: their visitors are\ntold the feature is unavailable. Nothing else of the widgets changes, and connecting a key again brings them\nback.\n\nRemoving a key that is not connected answers 200 with `removed: false`: the state asked for is the state it is\nin. A key of an adapter that has since been retired from the catalog can still be removed.\n\nThis needs an API key with full access. The caller must be the account itself or a Widgetic administrator.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"user-id","name":"userId","type":"string","required":true,"description":"The account that keeps the key. For your own, your own user id","format":"uuid","in":"path"},{"flag":"adapter-id","name":"adapterId","type":"string","required":true,"description":"The adapter the key is for","maxLength":40,"in":"path"},{"flag":"credential-key","name":"credentialKey","type":"string","required":true,"description":"The key's name within the adapter","maxLength":40,"in":"path"}]},{"name":"set-user-adapter-connection","aliases":[],"operationId":"setUserAdapterConnection","method":"PUT","path":"/users/{userId}/adapter-connections/{adapterId}/{credentialKey}","server":"api","summary":"Connect an adapter key to an account","description":"Gives an adapter the key it needs to act through a service you have an account with, for the widgets you made.\nTake the `adapterId` and `credentialKey` from `getUserAdapterConnections`. Saving a key that is already\nconnected replaces it.\n\n**What Widgetic does with it.** The key is stored encrypted, bound to this account, this adapter and this\ncredential, so that a stored value cannot be moved to another. It is never returned, never logged and never\nshown to a visitor of a widget. A server of Widgetic opens it for the one call a widget makes through the\nadapter, uses it to call the provider on your behalf, and discards it. To cut that off, remove the key here, or\nrevoke it with the provider.\n\n**What is checked.** That the adapter exists and takes this credential, that an owner may keep it (some are\nfor a partner only) and that the text is one line of at most 2048 characters. Whether the provider accepts\nthe key is not checked here: it shows the first time a widget uses it, and the widget tells its owner.\n\n**An address that has to be confirmed.** Some credentials are not a key but the email address Widgetic sends a\nwidget's messages to (`confirmation: email_code` in `getUserAdapterConnections`). Saving one checks that it is one\nemail address and emails a six-digit code to it; the answer says `awaitingConfirmation: true`, and nothing is sent\nto the address until the code is given back with `confirmUserAdapterConnection`. Saving the same address again\nbefore it is confirmed emails a new code (not sooner than a minute after the last); saving the address that is\nalready confirmed changes nothing. If the code cannot be emailed, nothing is saved.\n\nA widget whose adapter has no key connected, or whose address is not confirmed yet, is not charged and does not\nrun that adapter: its visitors are told the feature is unavailable, and the owner is told once a day what to do.\n\nThis needs an API key with full access. The caller must be the account itself or a Widgetic administrator. A\nretry is harmless: saving the same key twice only refreshes when it was set.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"user-id","name":"userId","type":"string","required":true,"description":"The account that keeps the key. For your own, your own user id","format":"uuid","in":"path"},{"flag":"adapter-id","name":"adapterId","type":"string","required":true,"description":"The adapter the key is for","maxLength":40,"in":"path"},{"flag":"credential-key","name":"credentialKey","type":"string","required":true,"description":"The key's name within the adapter","maxLength":40,"in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["credential"],"additionalProperties":false,"properties":{"credential":{"type":"string","format":"password","writeOnly":true,"maxLength":2048,"description":"The key exactly as its provider gave it: one line of text, at most 2048 characters. It is stored encrypted and is never returned or logged. For a credential that is an email address (`confirmation: email_code`), write that one address, with nothing else","example":"YOUR_PROVIDER_KEY"}},"example":{"credential":"YOUR_PROVIDER_KEY"}},"flags":[{"flag":"credential","name":"credential","type":"string","required":true,"description":"The key exactly as its provider gave it: one line of text, at most 2048 characters. It is stored encrypted and is never returned or logged. For a credential that is an email address (`confirmation: email_code`), write that one address, with nothing else","maxLength":2048,"format":"password","secret":true,"property":"credential"}]}},{"name":"update-personal-workspace-domain","aliases":[],"operationId":"updatePersonalWorkspaceDomain","method":"PUT","path":"/users/{id}/workspace/domains/{domainId}","server":"api","summary":"Change a website on your list","description":"Replaces the website of one entry on an account's own list. The same rules apply as when adding one (`addPersonalWorkspaceDomain`): a website name such as `example.com` or `*.example.com`, kept in its cleaned form, and refused when it is not a website name or is already on the list.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the list belongs to. For your own, your own user id","format":"uuid","in":"path"},{"flag":"domain-id","name":"domainId","type":"string","required":true,"description":"The `id` of the entry, as the list shows it","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","description":"The body of a request that changes an entry of a list of allowed websites. The property is `domain`, as when an entry is added: the routes read nothing called `name`.","required":["domain"],"properties":{"domain":{"type":"string","description":"The website name to keep instead, such as `example.com`, or `*.example.com` for a website and all of its subdomains","example":"example.com"}},"example":{"domain":"example.com"}},"flags":[{"flag":"domain","name":"domain","type":"string","required":true,"description":"The website name to keep instead, such as `example.com`, or `*.example.com` for a website and all of its subdomains","property":"domain"}]}},{"name":"update-user-api-key","aliases":[],"operationId":"updateUserApiKey","method":"PUT","path":"/users/{id}/keys/{keyId}","server":"api","summary":"Rename an API key, or switch it off or on","description":"Renames a key, or switches it off (`isActive: false`) or back on. A key that is switched off is refused at once by the server that took the request and within a minute by the others, and can be switched on again; deleting is for a key that is not coming back. The secret, the expiry and the scopes of a key cannot be changed. Only a key with full access can make these requests: a key limited to some scopes is refused with 403 and the code `API_KEY_SCOPE_MISSING`.\nRenaming and switching off can be done with an API key. Switching a key on needs a person signed in on widgetic.com: a request that asks for it with an API key or an SDK token is refused with 403, so a leaked key cannot bring a dormant key back to life.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the key belongs to","format":"uuid","in":"path"},{"flag":"key-id","name":"keyId","type":"string","required":true,"description":"The `keyId` of the key, as the list shows it","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","description":"At least one of the two is required","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"A new name. It cannot be one that another key of the account already has"},"isActive":{"type":"boolean","description":"false switches the key off, true switches it on again. A key that is switched off stops working within a minute at the latest, and at once on the server that took the request"}},"example":{"name":"Example name","isActive":true}},"flags":[{"flag":"name","name":"name","type":"string","required":false,"description":"A new name. It cannot be one that another key of the account already has","minLength":1,"maxLength":100,"property":"name"},{"flag":"is-active","name":"isActive","type":"boolean","required":false,"description":"false switches the key off, true switches it on again. A key that is switched off stops working within a minute at the latest, and at once on the server that took the request","property":"isActive"}]}}]},{"name":"widgets","title":"Widgets","description":"The Widgets operations of the Widgetic API.","hidden":false,"commands":[{"name":"create-composition-from-widget","aliases":[],"operationId":"createCompositionFromWidget","method":"POST","path":"/widgets/{id}/composition","server":"api","summary":"Create a composition for a widget in one call","description":"After a managed user publishes a widget from the creator, call this with the widget id, the customer's\nmanaged user (when the widget is not already filed under one), and optional content, design, embed settings\nand locale. Every input is validated before a single insert; a failure never leaves a half-made composition.\n\nWhen the widget is already tagged with a managed user, you may omit `managedUserId`. When you send it, it\nmust match that tag.\n\nA widget that keeps what visitors send (a form, a vote, a rating) declares its tables in its own source\n(`src/lib/meta/database.json`); they are made for the composition here, in a schema of its own, as the widget\nwas when it was last published. A widget that keeps nothing leaves the composition without tables. If the\ntables cannot be made just now, or the widget's file cannot be used, the composition is still created (`201`)\nand `meta.warnings` says why; the tables are made the next time the widget is generated.\n\nThe widget must be published. The `Idempotency-Key` header is required.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The widget id","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"content":{"type":"object"},"design":{"type":["object","null"]},"embedSettings":{"type":"object"},"locale":{"type":"string"},"managedUserId":{"type":"string","format":"uuid"}}},"flags":[{"flag":"name","name":"name","type":"string","required":true,"description":"","property":"name"},{"flag":"locale","name":"locale","type":"string","required":false,"description":"","property":"locale"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"","format":"uuid","property":"managedUserId"}]}},{"name":"create-widget","aliases":["create"],"operationId":"createWidget","method":"POST","path":"/widgets","server":"api","summary":"Create Widget","description":"Creates a widget for your account: a named record with a type and some starting content, on a canvas of\nits own (or one of yours that you name). It costs no credits and has no code yet; the code is generated for\nit afterwards. The new widget is yours, and you can list it with `getWidgets`. A widget is a draft until\nyou say otherwise (`isDraft: false`).\n\nIf a step after the widget was saved does not go through, the widget is still created, and\n`data.warnings` says what did not. Read it before relying on the widget.\n\nA widget can be placed on one of your canvases (`canvasId`), in a project you can edit (`projectId`) or in an\norganization where you may create widgets (`organizationId`). Naming anything you cannot use is refused\nwith 403, and the answer is the same whether it exists or not.\n\nTo make the widget for one of your managed users, send `managedUserId` or `managedUserExternalId`. The widget\nstays in your account and is yours to edit; the tag lets you list one user's widgets with `getWidgets`. The\nmanaged user must be yours and active: one that is not yours, or does not exist, is answered with 404 and\n`MANAGED_USER_NOT_FOUND`, and one that is switched off with 409 and `MANAGED_USER_INACTIVE`. The tag is set\nhere and does not change afterwards.\n\nThis call has a limit of its own: 120 per minute for each account (ask Widgetic if you need more). Past\nthat the call is answered with 429 and a `Retry-After`. A widget has no database of its own: what the\nvisitors of a composition leave is kept in the tables of that composition, made when the composition is.\nThe `Idempotency-Key` header is required; send the same key when you retry, so one widget is created, not\ntwo.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"A unique value (a UUID is fine) that makes a retry of this request safe.","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","description":"What a new widget starts from. Only the fields listed here are used. The widget is created without\ncode: it is a record with a name, a type and some starting content, on a canvas (a new one, unless you\nname one of yours). It has no database: what the visitors of a composition leave is kept in the tables\nof that composition.\n","required":["name","type","content"],"properties":{"name":{"type":"string","minLength":1,"description":"Name of the widget.","example":"New Chart Widget"},"type":{"type":"string","minLength":1,"description":"What kind of widget this is, as a label of your choosing (for example chart, form, gallery).","example":"chart"},"content":{"type":"object","description":"Starting content for the widget. An empty object is fine.","additionalProperties":true,"example":{"chartType":"bar","dataSource":"api"}},"isPublic":{"type":"boolean","default":false,"description":"Whether the widget is public.","example":false},"isDraft":{"type":"boolean","default":true,"description":"Whether the widget starts as a draft. A widget created without this is a draft.","example":false},"canvasId":{"type":"string","format":"uuid","description":"Put the widget on this existing canvas instead of a new one. You must own the canvas.","example":"abc12345-def6-7890-ab12-cd3456ef7890"},"projectId":{"type":"string","format":"uuid","description":"Project to file the widget in. You must be able to edit the project.","example":"7c1e2f30-4a5b-4c6d-8e9f-0a1b2c3d4e5f"},"organizationId":{"type":"string","format":"uuid","description":"Organization the widget belongs to. You must be a member who may create widgets there.","example":"5e2d3f4a-1b6c-4a1f-8b9c-3c2d4e5f6a7b"},"workspaceType":{"type":"string","enum":["personal","organization"],"description":"Whether the widget lives in your personal workspace or in an organization's.","example":"personal"},"managedUserId":{"type":"string","format":"uuid","description":"Make the widget for one of your managed users, by Widgetic's id for the user. Send this or `managedUserExternalId`, not both. The managed user must be yours and active. The widget stays in your account; the tag lets you list one user's widgets and keeps it out of the others' lists. It is set here and does not change afterwards.","example":"b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e"},"managedUserExternalId":{"type":"string","maxLength":255,"description":"The same, by your own identifier for the user, the `externalId` you gave when you created the managed user (answers show it as `externalId`). Send this or `managedUserId`, not both.","example":"user-8841"}},"example":{"name":"New Chart Widget","type":"chart","content":{"chartType":"bar","dataSource":"api"},"isPublic":false,"isDraft":false,"canvasId":"abc12345-def6-7890-ab12-cd3456ef7890","projectId":"7c1e2f30-4a5b-4c6d-8e9f-0a1b2c3d4e5f","organizationId":"5e2d3f4a-1b6c-4a1f-8b9c-3c2d4e5f6a7b","workspaceType":"personal","managedUserId":"b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e","managedUserExternalId":"user-8841"}},"flags":[{"flag":"name","name":"name","type":"string","required":true,"description":"Name of the widget.","minLength":1,"property":"name"},{"flag":"type","name":"type","type":"string","required":true,"description":"What kind of widget this is, as a label of your choosing (for example chart, form, gallery).","minLength":1,"property":"type"},{"flag":"is-public","name":"isPublic","type":"boolean","required":false,"description":"Whether the widget is public.","default":false,"property":"isPublic"},{"flag":"is-draft","name":"isDraft","type":"boolean","required":false,"description":"Whether the widget starts as a draft. A widget created without this is a draft.","default":true,"property":"isDraft"},{"flag":"canvas-id","name":"canvasId","type":"string","required":false,"description":"Put the widget on this existing canvas instead of a new one. You must own the canvas.","format":"uuid","property":"canvasId"},{"flag":"project-id","name":"projectId","type":"string","required":false,"description":"Project to file the widget in. You must be able to edit the project.","format":"uuid","property":"projectId"},{"flag":"organization-id","name":"organizationId","type":"string","required":false,"description":"Organization the widget belongs to. You must be a member who may create widgets there.","format":"uuid","property":"organizationId"},{"flag":"workspace-type","name":"workspaceType","type":"string","required":false,"description":"Whether the widget lives in your personal workspace or in an organization's.","enum":["personal","organization"],"property":"workspaceType"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"Make the widget for one of your managed users, by Widgetic's id for the user. Send this or `managedUserExternalId`, not both. The managed user must be yours and active. The widget stays in your account; the tag lets you list one user's widgets and keeps it out of the others' lists. It is set here and does not change afterwards.","format":"uuid","property":"managedUserId"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"The same, by your own identifier for the user, the `externalId` you gave when you created the managed user (answers show it as `externalId`). Send this or `managedUserId`, not both.","maxLength":255,"property":"managedUserExternalId"}]}},{"name":"get-widgets","aliases":["list"],"operationId":"getWidgets","method":"GET","path":"/widgets","server":"api","summary":"List Widgets","description":"Lists the widgets your account owns, newest first. A widget is the reusable piece a composition is made\nfrom; this is your own library, not a catalogue of Widgetic's widgets.\n\nPass `page` (counting from 1) and `size` (1 to 100, 20 when left out) to read the list one page at a time.\n`meta` then carries `total`, `hasNextPage` and `hasPreviousPage`, so a client can walk the list to its end.\nA page past the end is an empty list. Without `page` and `size` the call returns the whole list in one\nresponse, which only suits an account with a few widgets.\n\nPass `canvasId` to list only the widgets on one of your canvases. That list is always returned whole,\nwhatever `page` and `size` say.\n\nPass `managedUserId` (Widgetic's id for one of your managed users) or `managedUserExternalId` (your own\nidentifier for the user) to list only the widgets made for that managed user. The widgets of your other\nmanaged users, and your own, are left out. It combines with `page`, `size` and `canvasId`. A managed user\nthat is not yours, or does not exist, is answered with 404 and `MANAGED_USER_NOT_FOUND`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"page","name":"page","type":"integer","required":false,"description":"Page to return, counting from 1. Giving `page` or `size` turns paging on.","minimum":1,"in":"query"},{"flag":"size","name":"size","type":"integer","required":false,"description":"How many widgets a page holds (20 when `page` is given without it). Giving `page` or `size` turns paging on.","minimum":1,"maximum":100,"in":"query"},{"flag":"canvas-id","name":"canvasId","type":"string","required":false,"description":"List only the widgets on this canvas. The list is returned whole.","format":"uuid","in":"query"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"List only the widgets made for this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both.","format":"uuid","in":"query"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"List only the widgets made for the managed user you registered with this `externalId` (your own identifier for the user). Send this or `managedUserId`, not both.","maxLength":255,"in":"query"}]},{"name":"get-widgets-batch","aliases":[],"operationId":"getWidgetsBatch","method":"POST","path":"/widgets/batch","server":"api","summary":"Get Widgets Batch","description":"Retrieves multiple widgets by their IDs in a single request. An id that is not found, or that is not yours, is reported in `errors` exactly like one that does not exist. Called with an editor session, only the widgets made for the session's managed user are returned.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["widgetIds"],"properties":{"widgetIds":{"type":"array","items":{"type":"string"}}}},"flags":[{"flag":"widget-ids","name":"widgetIds","type":"string[]","required":true,"description":"","property":"widgetIds"}]}}]},{"name":"workflows","title":"Workflows","description":"The Workflows operations of the Widgetic API.","hidden":false,"commands":[{"name":"archive-workspace-workflow","aliases":[],"operationId":"archiveWorkspaceWorkflow","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/archive","server":"api","summary":"Archive a Workflow","description":"Takes a workflow out of the default list and stops it serving: its embeds show only their first composition,\nas when paused. It is kept, with its versions and counts, until it is deleted.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can archive.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"create-workspace-workflow","aliases":[],"operationId":"createWorkspaceWorkflow","method":"POST","path":"/workspaces/{workspaceId}/workflows","server":"api","summary":"Create a Workflow","description":"Makes a draft workflow. Give it a composition and the workflow is born with one block, which starts it; without\none it starts empty. A workspace holds up to 500 workflows.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can create one.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":120,"example":"Welcome journey"},"compositionId":{"type":["string","null"],"format":"uuid","description":"A composition of this workspace to start with."},"timezone":{"type":["string","null"],"description":"An IANA time zone name; UTC when not given.","example":"Europe/Bucharest"}}},"flags":[{"flag":"name","name":"name","type":"string","required":true,"description":"","minLength":1,"maxLength":120,"property":"name"},{"flag":"composition-id","name":"compositionId","type":"string","required":false,"description":"A composition of this workspace to start with.","format":"uuid","property":"compositionId"},{"flag":"timezone","name":"timezone","type":"string","required":false,"description":"An IANA time zone name; UTC when not given.","property":"timezone"}]}},{"name":"create-workspace-workflow-from-preset","aliases":[],"operationId":"createWorkspaceWorkflowFromPreset","method":"POST","path":"/workspaces/{workspaceId}/workflows/from-preset","server":"api","summary":"Start a Workflow from a starting point","description":"Makes your own copy of a starting point (`listWorkspaceWorkflowPresets`): a copy of every widget it chains (an\nordinary private widget that serves the starting point's published code), a composition of your own on each copy\n(a draft, with the content, design, embed settings and tables of the original), and a draft workflow wired like the\nstarting point's live version. The workflow is named as you ask, or after the starting point. Nothing you do to the\ncopy reaches the starting point, and nothing its offerer changes afterwards reaches your copy. The workflow stays a\ndraft: publish it when it is ready.\n\nA start takes a few seconds, because each widget is copied. Send an `Idempotency-Key` so that asking again after\na timeout makes one copy. When a start fails (503 with `details.reason` `preset_copy_failed`) the compositions it\nmade are removed again, so asking again is safe; the widget copies it made stay in the workspace as ordinary\nwidgets. The workspace's limit of workflows is checked before anything is copied.\n\nA starting point that is not offered to you, or that can no longer be started, is 404.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can start one.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","additionalProperties":false,"required":["key"],"properties":{"key":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","description":"The starting point, as `listWorkspaceWorkflowPresets` gives it.","example":"welcome-then-offer"},"name":{"type":["string","null"],"minLength":1,"maxLength":120,"description":"The new workflow's name. The starting point's title when left out."}}},"flags":[{"flag":"key","name":"key","type":"string","required":true,"description":"The starting point, as `listWorkspaceWorkflowPresets` gives it.","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","property":"key"},{"flag":"name","name":"name","type":"string","required":false,"description":"The new workflow's name. The starting point's title when left out.","minLength":1,"maxLength":120,"property":"name"}]}},{"name":"delete-workspace-workflow","aliases":[],"operationId":"deleteWorkspaceWorkflow","method":"DELETE","path":"/workspaces/{workspaceId}/workflows/{workflowId}","server":"api","summary":"Delete a Workflow","description":"Deletes a workflow, in whatever state it is in (draft, live, paused or archived), with all its versions and\ncounts. It cannot be undone: archive the workflow instead to keep it. The compositions it chained are not\ntouched, and a page whose embed still names the workflow shows the embed's first composition, as for a paused\none. Asking for a workflow that is already gone is \"not found\".\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can delete.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"discard-workspace-workflow-changes","aliases":[],"operationId":"discardWorkspaceWorkflowChanges","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/discard","server":"api","summary":"Discard the Unpublished Changes of a Workflow","description":"Puts the draft back to the live version (its graph and its time zone). Only for a workflow that has been\npublished; one that never was has no live version to go back to. Names the `revision` the editor read.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can discard.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["revision"],"properties":{"revision":{"type":"integer","minimum":1}}},"flags":[{"flag":"revision","name":"revision","type":"integer","required":true,"description":"","minimum":1,"property":"revision"}]}},{"name":"duplicate-workspace-workflow","aliases":[],"operationId":"duplicateWorkspaceWorkflow","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/duplicate","server":"api","summary":"Duplicate a Workflow","description":"Makes a draft copy of a workflow (its draft graph, not its versions or counts), named \"<name> (copy)\".\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can duplicate.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-workspace-workflow","aliases":[],"operationId":"getWorkspaceWorkflow","method":"GET","path":"/workspaces/{workspaceId}/workflows/{workflowId}","server":"api","summary":"Get a Workflow","description":"Reads a workflow with its draft graph and the version that is live.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can read it.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-workspace-workflow-stats","aliases":[],"operationId":"getWorkspaceWorkflowStats","method":"GET","path":"/workspaces/{workspaceId}/workflows/{workflowId}/stats","server":"api","summary":"Get what Visitors Did in a Workflow","description":"Counts per block and per connection over a range of UTC days (the last 30 days when not given, up to 366), for one\nversion or for all of them. Only counts: how many visitors reached a block, took a connection, or could not be\nmoved on. No visitor and no value a visitor typed is ever stored.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can read.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"version-id","name":"versionId","type":"string","required":false,"description":"Count one version only","format":"uuid","in":"query"},{"flag":"from","name":"from","type":"string","required":false,"description":"First UTC day (YYYY-MM-DD)","in":"query"},{"flag":"to","name":"to","type":"string","required":false,"description":"Last UTC day (YYYY-MM-DD)","in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-workspace-workflow-version","aliases":[],"operationId":"getWorkspaceWorkflowVersion","method":"GET","path":"/workspaces/{workspaceId}/workflows/{workflowId}/versions/{versionId}","server":"api","summary":"Get a Published Version of a Workflow","description":"One published version with its graph, for the read-only preview and for the Runs view.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can read.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"version-id","name":"versionId","type":"string","required":true,"description":"The id of a published version of the workflow","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"list-workspace-workflow-composition-options","aliases":[],"operationId":"listWorkspaceWorkflowCompositionOptions","method":"GET","path":"/workspaces/{workspaceId}/workflows/composition-options","server":"api","summary":"List the compositions a workflow can show, and what each can do","description":"The workspace's compositions as the workflow editor offers them, newest first. For each one: whether it can be\nshown (its widget has a published version), the websites it may be embedded on, and which triggers it supports.\n\n- **Input added** (`inputs`) is enabled only for a composition that declares at least one table a visitor's\n entries can be written to. Its `tables` are all of them; its `fields` are the columns a rule may read (not\n `id` and `created_at`, not a column that is not text, a number or yes/no, and not one whose name says it holds\n a person's private details, such as an email or a phone number).\n- **Action in the widget** (`actions`) is enabled only when the published widget version declares at least one\n goal. Its `items` are the goals a trigger can wait for.\n\nWhen one of them is not enabled, `reason` says why (`widget_keeps_no_data`, `widget_reports_no_actions` or\n`widget_not_published`), so the editor can show the trigger disabled with an explanation. Publishing checks every\nreference again against what the composition can do at that moment.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can read it.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"search","name":"search","type":"string","required":false,"description":"Text the composition's name must contain","maxLength":120,"in":"query"},{"flag":"ids","name":"ids","type":"string","required":false,"description":"Only these compositions (comma-separated ids, at most 50), those of them you may see. Ask for the\ncompositions a workflow shows with this, so each is found whatever page it would be on. An id you may not\nsee is left out, as one that does not exist is.","in":"query"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"Page size (1-100, 50 when not given)","in":"query"},{"flag":"offset","name":"offset","type":"integer","required":false,"description":"How many to skip (0 when not given)","in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"list-workspace-workflow-presets","aliases":[],"operationId":"listWorkspaceWorkflowPresets","method":"GET","path":"/workspaces/{workspaceId}/workflows/presets","server":"api","summary":"List the starting points for a new Workflow","description":"The workflows you can start from in this workspace: published workflows that Widgetic offers or, for one of a\npartner's managed users, that the partner offers (a managed user is never shown Widgetic's, and a person is never\nshown a partner's). Only what can be started now is listed, in the order the offerer set (smallest `position`\nfirst, then by `key`). Each shows what you would get, and none shows who made it or any id of its own.\nStart from one with `createWorkspaceWorkflowFromPreset`.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can read the list.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"list-workspace-workflow-versions","aliases":[],"operationId":"listWorkspaceWorkflowVersions","method":"GET","path":"/workspaces/{workspaceId}/workflows/{workflowId}/versions","server":"api","summary":"List the Published Versions of a Workflow","description":"Every version the workflow has had, newest first, with who published it and when. The one visitors get is marked\n`isLive`.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can read.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"list-workspace-workflows","aliases":[],"operationId":"listWorkspaceWorkflows","method":"GET","path":"/workspaces/{workspaceId}/workflows","server":"api","summary":"List the Workflows of a Workspace","description":"Lists the workspace's workflows, newest first unless `sort` says otherwise. Archived workflows are left out\nunless `status` asks for them (`archived`, or `all`).\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can read the list.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"search","name":"search","type":"string","required":false,"description":"Text the workflow's name must contain","maxLength":120,"in":"query"},{"flag":"status","name":"status","type":"string","required":false,"description":"One state to list (`draft`, `live`, `paused`, `archived`), or `all` for every state. Without it, the ones in use (draft, live and paused).","enum":["draft","live","paused","archived","all"],"in":"query"},{"flag":"sort","name":"sort","type":"string","required":false,"description":"`<field>:<asc|desc>` where field is createdAt, lastPublishedAt, runsCount or name (or just asc / desc for createdAt). Newest first when not given.","in":"query"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"Page size (1-100, 50 when not given)","in":"query"},{"flag":"offset","name":"offset","type":"integer","required":false,"description":"How many to skip (0 when not given)","in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"pause-workspace-workflow","aliases":[],"operationId":"pauseWorkspaceWorkflow","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/pause","server":"api","summary":"Pause a Workflow","description":"While a workflow is paused, its embeds show only their first composition (the `composition-id` of the embed) and no\nvisitor moves on. The live version is kept, and visitors partway through carry on from where they were when it is\nresumed. Only a live workflow can be paused.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can pause.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"publish-workspace-workflow","aliases":[],"operationId":"publishWorkspaceWorkflow","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/publish","server":"api","summary":"Publish the Draft of a Workflow","description":"Checks the draft and, when nothing stops it, makes it the next version and the one new visitors get. Versions never\nchange once published. A visitor already partway through keeps the version they started on (for up to 30 days).\nThe check reads what each composition can do right now (a table the visitor adds to, a goal the widget reports,\nthe websites it may be embedded on), so a workflow never goes live pointing at something that has gone.\nIt is refused with 422 (`BUSINESS_OPERATION_INVALID`) when the draft has errors; the `issues` in the error's\n`details` name the blocks to fix: an unconnected trigger, a condition without rules, a composition that is not\npublished, a field or action that is gone, a composition whose allowed websites exclude the site the workflow\nstarts on. Nothing is published when the draft has no changes since the live version.\nThe `revision` is the one the editor read, and a publish of a draft that has moved on is refused with 409.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can publish.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["revision"],"properties":{"revision":{"type":"integer","minimum":1}}},"flags":[{"flag":"revision","name":"revision","type":"integer","required":true,"description":"","minimum":1,"property":"revision"}]}},{"name":"restore-workspace-workflow-version","aliases":[],"operationId":"restoreWorkspaceWorkflowVersion","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/versions/{versionId}/restore","server":"api","summary":"Restore a Version of a Workflow into the Draft","description":"Copies a published version into the draft, so it can be changed and published again. Nothing is published and\nnothing visitors see changes: the version stays in the history as it was. Names the `revision` the editor read.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can restore.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"version-id","name":"versionId","type":"string","required":true,"description":"The id of a published version of the workflow","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["revision"],"properties":{"revision":{"type":"integer","minimum":1}}},"flags":[{"flag":"revision","name":"revision","type":"integer","required":true,"description":"","minimum":1,"property":"revision"}]}},{"name":"resume-workspace-workflow","aliases":[],"operationId":"resumeWorkspaceWorkflow","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/resume","server":"api","summary":"Resume a Paused Workflow","description":"Makes a paused workflow live again, on the version that was live when it was paused.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can resume.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"save-workspace-workflow","aliases":[],"operationId":"saveWorkspaceWorkflow","method":"PUT","path":"/workspaces/{workspaceId}/workflows/{workflowId}","server":"api","summary":"Save the Draft of a Workflow","description":"Saves the name, the time zone and/or the graph of the draft. Send at least one. The save names the `revision` the\neditor started from and is refused with 409 (`BUSINESS_STATE_CONFLICT`) when the draft has moved on, so two\npeople editing do not overwrite each other without knowing; read the workflow again and apply the change to it.\nA save is never refused for what is wrong with the graph (a block with no connection yet is normal in a draft):\nonly the shape is judged here, and what stops a workflow going live is told when it is published. A graph larger\nthan 200,000 bytes, or with more than 100 blocks or 300 connections, is refused.\nAn archived workflow cannot be saved. Saving a live workflow changes the draft only: visitors keep getting the\npublished version until the changes are published.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can save.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["revision"],"properties":{"revision":{"type":"integer","minimum":1,"description":"The revision the editor read."},"name":{"type":"string","minLength":1,"maxLength":120},"timezone":{"type":"string","example":"Europe/Bucharest"},"graph":{"$ref":"#/$defs/WorkflowGraph"}},"$defs":{"WorkflowConditionCase":{"type":"object","description":"One rule of a condition block, and one path out of it. Rules are read in order and the first that matches wins;\nthe block's Default output is taken when none does.\n","required":["id","match","tests"],"properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$","description":"The output port of the rule. `default` is reserved for the Default output."},"label":{"type":"string"},"match":{"type":"string","enum":["all","any"],"description":"`all` - every comparison must hold; `any` - one is enough."},"tests":{"type":"array","items":{"$ref":"#/$defs/WorkflowConditionTest"}}},"example":{"id":"Example id","label":"Example label","match":"Example match","tests":["Example WorkflowConditionTest"]}},"WorkflowConditionTest":{"type":"object","description":"One comparison of a rule - a field, an operator and (for most operators) a value to compare with.","required":["field","operator"],"properties":{"field":{"$ref":"#/$defs/WorkflowFieldReference"},"operator":{"type":"string","enum":["is","isNot","contains","doesNotContain","startsWith","endsWith","greaterThan","atLeast","lessThan","atMost","isEmpty","isNotEmpty"],"description":"Text fields take is, isNot, contains, doesNotContain, startsWith, endsWith, isEmpty and isNotEmpty.\nNumber fields take is, isNot, greaterThan, atLeast, lessThan, atMost, isEmpty and isNotEmpty.\nYes/no fields take is and isNot.\n"},"value":{"description":"What the field is compared with; not used by isEmpty and isNotEmpty. A text for a text field, a number for\na number field, true or false for a yes/no field.\n"}},"example":{"field":null,"operator":"Example operator","value":null}},"WorkflowEdge":{"type":"object","description":"A connection from an output port of one block to the input of another. Connections run forward only.","required":["id","source","sourcePort","target"],"properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"source":{"type":"string"},"sourcePort":{"type":"string","description":"`out` for compositions and triggers; a rule's id, or `default`, for conditions."},"target":{"type":"string"}},"example":{"id":"Example id","source":"Example source","sourcePort":"Example sourcePort","target":"Example target"}},"WorkflowFieldReference":{"type":"object","description":"A value a rule reads: a column the visitor filled in (`input`) or a value that came with a page event\n(`event`). The type is saved with the reference so the graph can be evaluated without the widget's schema.\n","required":["kind","name","type"],"properties":{"kind":{"type":"string","enum":["input","event"]},"name":{"type":"string"},"type":{"type":"string","enum":["text","number","boolean"]}},"example":{"kind":"Example kind","name":"Example name","type":"Example type"}},"WorkflowGraph":{"type":"object","description":"A whole workflow, as the editor saves it and the gateway publishes it.","required":["schemaVersion","nodes","edges"],"properties":{"schemaVersion":{"type":"integer","enum":[1]},"nodes":{"type":"array","maxItems":100,"items":{"$ref":"#/$defs/WorkflowNode"}},"edges":{"type":"array","maxItems":300,"items":{"$ref":"#/$defs/WorkflowEdge"}}},"example":{"schemaVersion":42,"nodes":["Example WorkflowNode"],"edges":["Example WorkflowEdge"]}},"WorkflowNode":{"type":"object","description":"A block. A `composition` block shows one composition (exactly one block of a workflow has `startsHere`);\na `trigger` block waits for one thing to happen while the composition before it is shown; a `condition`\nblock chooses the next step from what the trigger before it carried. At most 25 blocks.\n","required":["id","type"],"properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"type":{"type":"string","enum":["composition","trigger","condition"]},"position":{"$ref":"#/$defs/WorkflowNodePosition"},"compositionId":{"type":"string","format":"uuid","description":"The composition a `composition` block shows."},"startsHere":{"type":"boolean","description":"Set on the one composition block a visitor starts at."},"trigger":{"$ref":"#/$defs/WorkflowTrigger"},"cases":{"type":"array","description":"The rules of a `condition` block, in the order they are read.","items":{"$ref":"#/$defs/WorkflowConditionCase"}}},"example":{"id":"Example id","type":"Example type","position":null,"compositionId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","startsHere":true,"trigger":null,"cases":["Example WorkflowConditionCase"]}},"WorkflowNodePosition":{"type":"object","description":"Where a block sits on the editor's canvas. Embeds never read it.","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"example":{"x":42,"y":42}},"WorkflowSchedule":{"type":"object","description":"When a Scheduled trigger fires - a wait counted from reaching the step, or a fixed moment.","required":["mode"],"properties":{"mode":{"type":"string","enum":["after","at"]},"amount":{"type":"number","description":"How long to wait (mode `after`)."},"unit":{"type":"string","enum":["seconds","minutes","hours","days","weeks"],"description":"The unit of `amount` (mode `after`)."},"at":{"type":"string","format":"date-time","description":"The moment to fire at, as an ISO time in UTC (mode `at`)."}},"example":{"mode":"Example mode","amount":42,"unit":"Example unit","at":"2026-01-15T09:30:00.000Z"}},"WorkflowTrigger":{"type":"object","description":"What a trigger block waits for. `input`: a row the visitor adds to a table of the composition (`table`).\n`action`: an action the widget reports (`actionId`). `pageEvent`: an event the page sends (`name`, and the\n`values` it promises to carry). `scheduled`: a wait or a date (`schedule`).\n","required":["kind"],"properties":{"kind":{"type":"string","enum":["input","action","pageEvent","scheduled"]},"table":{"type":"string"},"actionId":{"type":"string"},"name":{"type":"string"},"values":{"type":"array","items":{"type":"object","required":["name","type"],"properties":{"name":{"type":"string"},"type":{"type":"string","enum":["text","number","boolean"]}}}},"schedule":{"$ref":"#/$defs/WorkflowSchedule"}},"example":{"kind":"Example kind","table":"Example table","actionId":"Example actionId","name":"Example name","values":[],"schedule":null}}}},"flags":[{"flag":"revision","name":"revision","type":"integer","required":true,"description":"The revision the editor read.","minimum":1,"property":"revision"},{"flag":"name","name":"name","type":"string","required":false,"description":"","minLength":1,"maxLength":120,"property":"name"},{"flag":"timezone","name":"timezone","type":"string","required":false,"description":"","property":"timezone"}]}},{"name":"unarchive-workspace-workflow","aliases":[],"operationId":"unarchiveWorkspaceWorkflow","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/unarchive","server":"api","summary":"Unarchive a Workflow","description":"Brings an archived workflow back into the list: as paused when it was ever published (resume it to serve visitors\nagain), as a draft when it never was.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can unarchive.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]}]}]}
|
|
1
|
+
{"format":1,"apiUrl":"https://api.widgetic.com/v1","groups":[{"name":"brand-kit","title":"Brand Kit","description":"The Brand Kit operations of the Widgetic API.","hidden":false,"commands":[{"name":"delete-brand-kit","aliases":[],"operationId":"deleteBrandKit","method":"DELETE","path":"/brand-kit","server":"api","summary":"Reset the Brand Kit to the default","description":"","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"organization-id","name":"organizationId","type":"string","required":false,"description":"","format":"uuid","in":"query"}]},{"name":"delete-brand-kit-asset","aliases":[],"operationId":"deleteBrandKitAsset","method":"DELETE","path":"/brand-kit/assets/{assetId}","server":"api","summary":"Remove a font or icon from the Brand Kit","description":"","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"asset-id","name":"assetId","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"organization-id","name":"organizationId","type":"string","required":false,"description":"","format":"uuid","in":"query"}]},{"name":"get-brand-kit","aliases":["list"],"operationId":"getBrandKit","method":"GET","path":"/brand-kit","server":"api","summary":"The Brand Kit of the current workspace","description":"The colors, fonts, icons, corner radius and style notes every widget made by this account is generated with. An account that never saved a kit gets Widgetic's default one (`isDefault` is true). A partner account's kit is the default for everything its keys create, and a key reads and changes the kit of the account it belongs to; there is no kit per managed user.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"organization-id","name":"organizationId","type":"string","required":false,"description":"An organisation's kit; omit for the caller's current workspace.","format":"uuid","in":"query"}]},{"name":"patch-brand-kit","aliases":[],"operationId":"patchBrandKit","method":"PATCH","path":"/brand-kit","server":"api","summary":"Change part of the Brand Kit","description":"Changes only what is sent: the colors, a font role, a spacing step, one corner radius (0 for square corners) or the style notes, and keeps the rest of the saved kit. An account that never saved a kit starts from Widgetic's default one. The response lists what is worth fixing in `warnings`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"organization-id","name":"organizationId","type":"string","required":false,"description":"","format":"uuid","in":"query"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","minProperties":1,"description":"Changes part of the kit and leaves the rest as it is. Only the keys that are sent change.","properties":{"colors":{"type":"array","maxItems":24,"description":"The whole list of colors (it replaces the saved list).","items":{"$ref":"#/$defs/BrandColor"}},"fonts":{"type":"object","description":"The text roles to change; the roles that are left out keep their font.","properties":{"title":{"$ref":"#/$defs/BrandFont"},"subtitle":{"$ref":"#/$defs/BrandFont"},"body":{"$ref":"#/$defs/BrandFont"},"caption":{"$ref":"#/$defs/BrandFont"}}},"spacing":{"type":"object","description":"The spacing and radius steps to change; the steps that are left out keep their value.","properties":{"xs":{"type":"integer","minimum":0,"maximum":128},"s":{"type":"integer","minimum":0,"maximum":128},"m":{"type":"integer","minimum":0,"maximum":128},"l":{"type":"integer","minimum":0,"maximum":128},"xl":{"type":"integer","minimum":0,"maximum":128},"radiusS":{"type":"integer","minimum":0,"maximum":64},"radiusM":{"type":"integer","minimum":0,"maximum":64},"radiusL":{"type":"integer","minimum":0,"maximum":64}}},"cornerRadius":{"type":"integer","minimum":0,"maximum":64,"description":"One corner radius in pixels for the whole kit; the small, medium and large radii are made from it. 0 gives square corners. A radius step sent in `spacing` wins over the one made from this value."},"instructions":{"type":"string","maxLength":500,"description":"The kit owner's style notes (see `tokens.instructions`). An empty string removes them."},"expectedVersion":{"type":"integer","description":"Refuse the change with a 409 when the kit was saved again since this version was read."}},"example":{"cornerRadius":0,"instructions":"Use the dark color as the background always and the light color for titles."},"$defs":{"BrandColor":{"type":"object","required":["name","hex"],"properties":{"name":{"type":"string","minLength":1,"maxLength":40},"hex":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"},"role":{"type":"string","enum":["primary","secondary","accent","background","surface","text","title","sub","border","error"]}},"example":{"name":"Example name","hex":"Example hex","role":"Example role"}},"BrandFont":{"type":"object","required":["family","source"],"properties":{"family":{"type":"string","minLength":1,"maxLength":80},"source":{"type":"string","enum":["google","upload"]},"weight":{"type":"integer","enum":[100,200,300,400,500,600,700,800,900]},"sizeMax":{"type":"integer","minimum":8,"maximum":200}},"example":{"family":"Example family","source":"Example source","weight":42,"sizeMax":42}}}},"flags":[{"flag":"corner-radius","name":"cornerRadius","type":"integer","required":false,"description":"One corner radius in pixels for the whole kit; the small, medium and large radii are made from it. 0 gives square corners. A radius step sent in `spacing` wins over the one made from this value.","minimum":0,"maximum":64,"property":"cornerRadius"},{"flag":"instructions","name":"instructions","type":"string","required":false,"description":"The kit owner's style notes (see `tokens.instructions`). An empty string removes them.","maxLength":500,"property":"instructions"},{"flag":"expected-version","name":"expectedVersion","type":"integer","required":false,"description":"Refuse the change with a 409 when the kit was saved again since this version was read.","property":"expectedVersion"}]}},{"name":"patch-brand-kit-asset","aliases":[],"operationId":"patchBrandKitAsset","method":"PATCH","path":"/brand-kit/assets/{assetId}","server":"api","summary":"Rename an icon or relabel a font variant","description":"","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"asset-id","name":"assetId","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"organization-id","name":"organizationId","type":"string","required":false,"description":"","format":"uuid","in":"query"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","minProperties":1,"properties":{"name":{"type":"string","description":"Icon rename"},"family":{"type":"string","description":"Font family relabel"},"weight":{"type":"integer","enum":[100,200,300,400,500,600,700,800,900]},"style":{"type":"string","enum":["normal","italic"]}},"example":{"name":"Example name","family":"Example family","weight":42,"style":"Example style"}},"flags":[{"flag":"name","name":"name","type":"string","required":false,"description":"Icon rename","property":"name"},{"flag":"family","name":"family","type":"string","required":false,"description":"Font family relabel","property":"family"},{"flag":"weight","name":"weight","type":"integer","required":false,"description":"","property":"weight"},{"flag":"style","name":"style","type":"string","required":false,"description":"","enum":["normal","italic"],"property":"style"}]}},{"name":"post-brand-kit-font","aliases":[],"operationId":"postBrandKitFont","method":"POST","path":"/brand-kit/fonts","server":"api","summary":"Upload a font file to the Brand Kit","description":"","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"organization-id","name":"organizationId","type":"string","required":false,"description":"","format":"uuid","in":"query"}],"body":{"required":true,"kind":"multipart","contentType":"multipart/form-data","schema":{"type":"object","required":["file","family","weight"],"properties":{"file":{"type":"string","format":"binary"},"family":{"type":"string"},"weight":{"type":"integer"},"style":{"type":"string","enum":["normal","italic"]}}},"flags":[{"flag":"file","name":"file","type":"file","required":true,"description":"","property":"file"},{"flag":"family","name":"family","type":"string","required":true,"description":"","property":"family"},{"flag":"weight","name":"weight","type":"integer","required":true,"description":"","property":"weight"},{"flag":"style","name":"style","type":"string","required":false,"description":"","enum":["normal","italic"],"property":"style"}]}},{"name":"post-brand-kit-icon","aliases":[],"operationId":"postBrandKitIcon","method":"POST","path":"/brand-kit/icons","server":"api","summary":"Add a named SVG icon to the Brand Kit","description":"","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"organization-id","name":"organizationId","type":"string","required":false,"description":"","format":"uuid","in":"query"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["name","svg"],"properties":{"name":{"type":"string"},"svg":{"type":"string"}},"example":{"name":"Example name","svg":"Example svg"}},"flags":[{"flag":"name","name":"name","type":"string","required":true,"description":"","property":"name"},{"flag":"svg","name":"svg","type":"string","required":true,"description":"","property":"svg"}]}},{"name":"put-brand-kit","aliases":[],"operationId":"putBrandKit","method":"PUT","path":"/brand-kit","server":"api","summary":"Save Brand Kit tokens","description":"Replaces the colors, fonts, spacing and notes of the kit with the ones sent. To change one thing, use PATCH.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"organization-id","name":"organizationId","type":"string","required":false,"description":"","format":"uuid","in":"query"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["tokens"],"properties":{"tokens":{"$ref":"#/$defs/BrandKitTokens"},"figmaSource":{"anyOf":[{"$ref":"#/$defs/BrandKitFigmaSource"},{"type":"null"}]},"expectedVersion":{"type":"integer"}},"example":{"tokens":null,"figmaSource":null,"expectedVersion":42},"$defs":{"BrandColor":{"type":"object","required":["name","hex"],"properties":{"name":{"type":"string","minLength":1,"maxLength":40},"hex":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"},"role":{"type":"string","enum":["primary","secondary","accent","background","surface","text","title","sub","border","error"]}},"example":{"name":"Example name","hex":"Example hex","role":"Example role"}},"BrandFont":{"type":"object","required":["family","source"],"properties":{"family":{"type":"string","minLength":1,"maxLength":80},"source":{"type":"string","enum":["google","upload"]},"weight":{"type":"integer","enum":[100,200,300,400,500,600,700,800,900]},"sizeMax":{"type":"integer","minimum":8,"maximum":200}},"example":{"family":"Example family","source":"Example source","weight":42,"sizeMax":42}},"BrandKitFigmaSource":{"type":"object","required":["url","fileKey","nodeId","fileName","importedAt"],"properties":{"url":{"type":"string"},"fileKey":{"type":"string"},"nodeId":{"type":["string","null"]},"fileName":{"type":["string","null"]},"importedAt":{"type":"string","format":"date-time"}},"example":{"url":"Example url","fileKey":"Example fileKey","nodeId":"Example nodeId","fileName":"Example fileName","importedAt":"2026-01-15T09:30:00.000Z"}},"BrandKitTokens":{"type":"object","required":["colors","fonts","spacing"],"description":"Colors, fonts by role, spacing and corner radii of a Brand Kit.","properties":{"colors":{"type":"array","maxItems":24,"items":{"$ref":"#/$defs/BrandColor"}},"fonts":{"type":"object","properties":{"title":{"$ref":"#/$defs/BrandFont"},"subtitle":{"$ref":"#/$defs/BrandFont"},"body":{"$ref":"#/$defs/BrandFont"},"caption":{"$ref":"#/$defs/BrandFont"}}},"spacing":{"$ref":"#/$defs/BrandSpacing"},"instructions":{"type":"string","maxLength":500,"description":"The kit owner's own style notes, taken into account in every widget made with the kit, for example \"Use the dark color as the background always and the light color for titles\". They are about how a widget looks and never about what it holds, and what a request names itself wins over them. Left out, Widgetic's default note applies (\"Widget backgrounds to be white whenever possible.\"); an empty string means none."}},"example":{"colors":["Example BrandColor"],"fonts":{},"spacing":null,"instructions":"Example instructions"}},"BrandSpacing":{"type":"object","required":["xs","s","m","l","xl","radiusS","radiusM","radiusL"],"properties":{"xs":{"type":"integer","minimum":0,"maximum":128},"s":{"type":"integer","minimum":0,"maximum":128},"m":{"type":"integer","minimum":0,"maximum":128},"l":{"type":"integer","minimum":0,"maximum":128},"xl":{"type":"integer","minimum":0,"maximum":128},"radiusS":{"type":"integer","minimum":0,"maximum":64},"radiusM":{"type":"integer","minimum":0,"maximum":64},"radiusL":{"type":"integer","minimum":0,"maximum":64}},"example":{"xs":42,"s":42,"m":42,"l":42,"xl":42,"radiusS":42,"radiusM":42,"radiusL":42}}}},"flags":[{"flag":"expected-version","name":"expectedVersion","type":"integer","required":false,"description":"","property":"expectedVersion"}]}}]},{"name":"canvases","title":"Canvases","description":"The Canvases operations of the Widgetic API.","hidden":false,"commands":[{"name":"create-canvas","aliases":["create"],"operationId":"createCanvas","method":"POST","path":"/canvases","server":"api","summary":"Create Canvas","description":"Creates a new canvas with the specified properties.\n\nTo make the canvas for one of your managed users, send `managedUserId` or `managedUserExternalId`. The canvas\nstays in your account and is yours to edit; the tag lets you list one user's canvases with `getAllCanvases`.\nThe managed user must be yours and active: one that is not yours, or does not exist, is answered with 404 and\n`MANAGED_USER_NOT_FOUND`, and one that is switched off with 409 and `MANAGED_USER_INACTIVE`. The tag is set\nhere and does not change afterwards. Called with an editor session, the canvas is always made for the\nsession's managed user.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["persistenceKey"],"properties":{"persistenceKey":{"type":"string","description":"Key used for persistent storage and reference","example":"canvas_xyz123"},"canvasContent":{"type":"object","description":"JSON content of the canvas, containing layout and configuration","example":{"version":1,"elements":[],"settings":{}}},"primaryWidgetId":{"type":["string","null"],"format":"uuid","description":"Optional primary widget id (usually set after widget create)"},"managedUserId":{"type":["string","null"],"format":"uuid","description":"Make the canvas for this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both. The tag is set here and does not change afterwards."},"managedUserExternalId":{"type":["string","null"],"maxLength":255,"description":"Make the canvas for the managed user you registered with this `externalId` (your own identifier for the user). Send this or `managedUserId`, not both."},"projectId":{"type":["string","null"],"format":"uuid","description":"Project this canvas belongs to. Empty canvases show on that project's list."}},"example":{"persistenceKey":"canvas_xyz123","canvasContent":{"version":1,"elements":[],"settings":{}},"primaryWidgetId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","managedUserId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","managedUserExternalId":"Example managedUserExternalId","projectId":"3fa85f64-5717-4562-b3fc-2c963f66afa6"}},"flags":[{"flag":"persistence-key","name":"persistenceKey","type":"string","required":true,"description":"Key used for persistent storage and reference","property":"persistenceKey"},{"flag":"primary-widget-id","name":"primaryWidgetId","type":"string","required":false,"description":"Optional primary widget id (usually set after widget create)","format":"uuid","property":"primaryWidgetId"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"Make the canvas for this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both. The tag is set here and does not change afterwards.","format":"uuid","property":"managedUserId"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"Make the canvas for the managed user you registered with this `externalId` (your own identifier for the user). Send this or `managedUserId`, not both.","maxLength":255,"property":"managedUserExternalId"},{"flag":"project-id","name":"projectId","type":"string","required":false,"description":"Project this canvas belongs to. Empty canvases show on that project's list.","format":"uuid","property":"projectId"}]}},{"name":"delete-canvas","aliases":["delete"],"operationId":"deleteCanvas","method":"DELETE","path":"/canvases/{id}","server":"api","summary":"Delete Canvas","description":"Deletes a canvas together with every widget on it.\n\nCalled with an editor session, only a canvas whose widgets were all made for the session's managed user can\nbe deleted; one that also holds somebody else's widgets is refused with 409 and `CANVAS_HAS_OTHER_WIDGETS`,\nand nothing is deleted. Delete such a canvas with your API key.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the canvas","format":"uuid","in":"path"}]},{"name":"get-all-canvases","aliases":["list"],"operationId":"getAllCanvases","method":"GET","path":"/canvases","server":"api","summary":"Get All Canvases","description":"Retrieves all canvases with pagination support and filtering options.\n\nPass `managedUserId` (Widgetic's id for one of your managed users) or `managedUserExternalId` (your own\nidentifier for the user) to list only the canvases made for that managed user. The canvases of your other\nmanaged users, and your own, are left out. A managed user that is not yours, or does not exist, is answered\nwith 404 and `MANAGED_USER_NOT_FOUND`. Called with an editor session, the list is always the session's\nmanaged user's.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"page","name":"page","type":"integer","required":false,"description":"Page number for pagination","default":1,"minimum":1,"in":"query"},{"flag":"size","name":"size","type":"integer","required":false,"description":"Number of items per page","default":20,"minimum":1,"maximum":100,"in":"query"},{"flag":"sort","name":"sort","type":"string","required":false,"description":"Sort field and direction (e.g., \"created_at:desc\")","default":"created_at:desc","in":"query"},{"flag":"created-after","name":"createdAfter","type":"string","required":false,"description":"Filter canvases created after the specified date (ISO 8601 format)","format":"date-time","in":"query"},{"flag":"created-before","name":"createdBefore","type":"string","required":false,"description":"Filter canvases created before the specified date (ISO 8601 format)","format":"date-time","in":"query"},{"flag":"updated-after","name":"updatedAfter","type":"string","required":false,"description":"Filter canvases updated after the specified date (ISO 8601 format)","format":"date-time","in":"query"},{"flag":"updated-before","name":"updatedBefore","type":"string","required":false,"description":"Filter canvases updated before the specified date (ISO 8601 format)","format":"date-time","in":"query"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"List only the canvases made for this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both.","format":"uuid","in":"query"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"List only the canvases made for the managed user you registered with this `externalId` (your own identifier for the user). Send this or `managedUserId`, not both.","maxLength":255,"in":"query"}]},{"name":"get-canvas","aliases":["get"],"operationId":"getCanvas","method":"GET","path":"/canvases/{id}","server":"api","summary":"Get Canvas","description":"Retrieves a specific canvas by ID.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the canvas","format":"uuid","in":"path"}]},{"name":"update-canvas","aliases":["update"],"operationId":"updateCanvas","method":"PUT","path":"/canvases/{id}","server":"api","summary":"Update Canvas","description":"Updates an existing canvas with the specified properties.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the canvas to update","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","properties":{"persistenceKey":{"type":"string","description":"Updated key used for persistent storage and reference","example":"updated_canvas_xyz123"},"canvasContent":{"type":"object","description":"Updated JSON content of the canvas","example":{"version":2,"elements":[{"id":"elem1"}],"settings":{"theme":"dark"}}},"primaryWidgetId":{"type":["string","null"],"format":"uuid","description":"Primary widget for list/deep-link; omit to leave unchanged"}},"example":{"persistenceKey":"updated_canvas_xyz123","canvasContent":{"version":2,"elements":[{"id":"elem1"}],"settings":{"theme":"dark"}},"primaryWidgetId":"3fa85f64-5717-4562-b3fc-2c963f66afa6"}},"flags":[{"flag":"persistence-key","name":"persistenceKey","type":"string","required":false,"description":"Updated key used for persistent storage and reference","property":"persistenceKey"},{"flag":"primary-widget-id","name":"primaryWidgetId","type":"string","required":false,"description":"Primary widget for list/deep-link; omit to leave unchanged","format":"uuid","property":"primaryWidgetId"}]}}]},{"name":"compositions","title":"Compositions","description":"The Compositions operations of the Widgetic API.","hidden":false,"commands":[{"name":"add-ai-answers-knowledge-document","aliases":[],"operationId":"addAiAnswersKnowledgeDocument","method":"POST","path":"/compositions/{id}/adapters/ai-answers/knowledge/documents","server":"api","summary":"Add a document for a widget's AI to answer from","description":"Gives the widget's AI a document to answer visitors' questions from. Send the title and the plain text. The call\nreturns once the document is ready to be used, which takes a few seconds, longer for a long text.\n\n**How it is used.** The text is cleaned, cut into short passages and each passage is turned into numbers (an\nembedding) so the widget can find the passages nearest to a question. When a visitor asks, the nearest passages and\nthe question are shown to an AI model, which writes the answer from them and from nothing else; if the documents\nsay nothing about the question the visitor is told so, and the owner is not charged for that question. The titles of\nthe documents an answer used are shown to the visitor as its sources.\n\n**What leaves Widgetic.** The text of a document is sent to OpenAI to be embedded when it is added, and the\npassages used for a question are sent to the AI model that writes the answer. Say so in your privacy policy.\n\n**Treat every document as public.** An answer can quote or paraphrase anything a document says, to any visitor of\nthe widget. Do not add passwords, keys, personal data or anything that must stay private.\n\n**Limits.** A widget holds up to 20 documents, each up to 100000 characters, and up to 400000 characters in all\n(`getAiAnswersKnowledge` reports the current numbers). This call takes text. To add a text file or a picture of a\ndocument (a PDF too, once PDFs are read) without turning it into text yourself, use `addAiAnswersKnowledgeFile`. Adding a document costs no\ncredits. The same text cannot be added twice to one widget (409).\n\nThe caller must be allowed to change the widget. An API key needs the `content:write` scope. Adds are limited to ten\nin ten minutes per person. A partner's customer in an editor session is one person of its own: its adds are not\ncounted with the partner's other customers'.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The widget (composition) to add the document to","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["title","text"],"additionalProperties":false,"properties":{"title":{"type":"string","maxLength":120,"description":"What the document is called, on one line. Visitors see it as the source of an answer. Line breaks and invisible characters are removed","example":"Shipping and returns"},"text":{"type":"string","maxLength":100000,"description":"The text of the document, as plain text. It is cleaned (line endings are made the same, invisible characters and surplus blank lines are removed) and then counted: up to 100000 characters. Markup is not interpreted, so paste the words, not a web page","example":"Orders ship within 2 business days. Returns are accepted for 30 days after delivery, in the original packaging."}},"example":{"title":"Shipping and returns","text":"Orders ship within 2 business days. Returns are accepted for 30 days after delivery, in the original packaging."}},"flags":[{"flag":"title","name":"title","type":"string","required":true,"description":"What the document is called, on one line. Visitors see it as the source of an answer. Line breaks and invisible characters are removed","maxLength":120,"property":"title"},{"flag":"text","name":"text","type":"string","required":true,"description":"The text of the document, as plain text. It is cleaned (line endings are made the same, invisible characters and surplus blank lines are removed) and then counted: up to 100000 characters. Markup is not interpreted, so paste the words, not a web page","maxLength":100000,"property":"text"}]}},{"name":"add-ai-answers-knowledge-file","aliases":[],"operationId":"addAiAnswersKnowledgeFile","method":"POST","path":"/compositions/{id}/adapters/ai-answers/knowledge/documents/file","server":"api","summary":"Add a text file or a picture of a document (a PDF once PDFs are read) for a widget's AI to answer from","description":"Gives the widget's AI a document made from a file. Send a `multipart/form-data` request with the file in the field\nnamed `file` and, if you like, a `title`. The file is turned into text and then added exactly as\n`addAiAnswersKnowledgeDocument` adds text: the same cleaning, the same limits, the same refusal of a copy of a\ndocument the widget already has. The call returns once the document is ready to be used, which takes a few seconds\n(a PDF is read first, so it takes longer).\n\n**PDFs are not read yet.** For now a PDF is refused with 415, in a sentence that says what to do instead: paste the\ntext, upload it as a text file, or upload its pages as pictures. What this description says about a PDF is what will\nhold from the day PDFs are read.\n\n**Which files.**\n- A **PDF** (not read yet, see above), up to 10 MB and 300 pages. Its text is read and its paragraphs are put back\n together. A PDF that is only pictures of pages (a scan) has no text to read: upload the pages as pictures instead.\n A PDF locked with a password is refused.\n- A **text file**: `.txt`, `.text`, `.md`, `.markdown` or `.csv`, in UTF-8, UTF-16 or Windows-1252.\n- A **picture of a document**: PNG, JPEG or WebP, up to 4 MB. The text in it is read with the same text recognition\n as the `ocr` adapter.\n- Word and other office files are not read yet: save the file as a PDF and upload that.\n\nWhat a file is comes from what is in it, not from its name or the type it is sent as. A file that is none of these is\nrefused with 415.\n\n**The title.** Left out, the name of the file (without its extension) is the title. Visitors see the title as the\nsource of an answer, so send one that means something to them.\n\n**The file is not kept.** Only its text, cut into passages, is kept, and `getAiAnswersKnowledge` never returns it.\n\n**What leaves Widgetic.** As for text, the words of the document are sent to OpenAI to be embedded when it is added,\nand the passages used for a question are sent to the AI model that writes the answer. A picture is also sent to the\ntext recognition service. A PDF is read on Widgetic's own servers and is not sent anywhere to be read. Say so in your\nprivacy policy.\n\n**Treat every document as public.** An answer can quote or paraphrase anything a document says, to any visitor of\nthe widget. Do not add passwords, keys, personal data or anything that must stay private.\n\n**Limits.** The widget's limits are those of `addAiAnswersKnowledgeDocument` (up to 20 documents, each up to 100000\ncharacters of text, and up to 400000 characters in all; `getAiAnswersKnowledge` reports the current numbers), and\nthe file is at most 10 MB. Adding a document costs no credits. The same text cannot be added twice to one widget\n(409).\n\nThe caller must be allowed to change the widget. An API key needs the `content:write` scope. Adds, from text or from\na file, share one limit of ten in ten minutes per person. A partner's customer in an editor session is one person\nof its own: its adds are not counted with the partner's other customers'.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The widget (composition) to add the document to","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"multipart","contentType":"multipart/form-data","schema":{"type":"object","required":["file"],"additionalProperties":false,"properties":{"file":{"type":"string","format":"binary","description":"The file, which is a text file or a picture of a document (a PDF is not read yet). One file only"},"title":{"type":"string","maxLength":120,"description":"What the document is called, on one line. Visitors see it as the source of an answer. Left out, the name of the file stands in for it","example":"Shipping and returns"}}},"flags":[{"flag":"file","name":"file","type":"file","required":true,"description":"The file, which is a text file or a picture of a document (a PDF is not read yet). One file only","property":"file"},{"flag":"title","name":"title","type":"string","required":false,"description":"What the document is called, on one line. Visitors see it as the source of an answer. Left out, the name of the file stands in for it","maxLength":120,"property":"title"}]}},{"name":"check-composition-access","aliases":[],"operationId":"checkCompositionAccess","method":"GET","path":"/compositions/{id}/access","server":"api","summary":"Check Composition Access","description":"Checks if the authenticated user can access a specific composition based on workspace context and permissions.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"Composition ID","format":"uuid","in":"path"}]},{"name":"create-composition","aliases":["create"],"operationId":"createComposition","method":"POST","path":"/compositions","server":"api","summary":"Create Composition","description":"Creates a new composition.\n\nTo make the composition for one of your managed users, send `managedUserId` or `managedUserExternalId`.\nThe composition stays in your account and is yours to edit; the tag lets you list one user's compositions\nwith `getAllCompositions`. The managed user must be yours and active: one that is not yours, or does not\nexist, is answered with 404 and `MANAGED_USER_NOT_FOUND`, and one that is switched off with 409 and\n`MANAGED_USER_INACTIVE`. The tag is set here and does not change afterwards. A copy made with\n`duplicateComposition` keeps the tag of the original.\n\nAn editor session with the `creator` surface may call this to start a composition of a widget it built:\nit sends only `name`, `widgetId` (a widget made for the session's managed user) and `content`, and the\ncomposition is always made for that managed user.\n\nSend `databaseSchema` to give the composition tables of its own for what visitors send it (the shape, the\nallowed types and the limits are described on the field). A `databaseSchema` that cannot be used is refused\nbefore anything is created, with `400` and `INVALID_DATABASE_SCHEMA`. If the composition is created but its\ntables cannot be made just now, the answer is still `201` and `meta.warnings` says so.\n\nLeave `databaseSchema` out and the composition gets the tables its widget declares in its own source\n(`src/lib/meta/database.json`), if it declares any: they are made the same way, in a schema of the\ncomposition's own. A widget that keeps nothing leaves the composition without tables. If the widget's file\ncannot be used, or its tables cannot be made just now, the answer is still `201` and `meta.warnings` says so.\nThe widget is read as it was when it was last published (as it is now, when it never was).\n\nThe `Idempotency-Key` header is required (`400` without it). Send the same key when you retry, so one\ncomposition is created, not two.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","properties":{"name":{"type":"string","description":"Name of the composition","example":"My New Composition"},"widgetId":{"type":"string","format":"uuid","description":"ID of the widget this composition is based on","example":"123e4567-e89b-12d3-a456-426614174002"},"creatorId":{"type":"string","format":"uuid","description":"ID of the user creating the composition","example":"123e4567-e89b-12d3-a456-426614174001"},"content":{"type":"object","description":"Content data for the composition","example":{"key":"value"}},"status":{"type":"string","description":"Initial status of the composition","default":"draft","example":"draft"},"embedSettings":{"type":"object","description":"Settings for embedding the composition","example":{"width":"100%","height":"500px"}},"design":{"type":"object","description":"Design settings for the composition","example":{"theme":"light","colors":{"primary":"#0066ff"}}},"databaseSchema":{"type":"object","description":"The tables this composition keeps what visitors send it in (votes, ratings, answers). They are made when\nthe composition is created, in a schema of their own that no other composition can reach: in Widgetic's\ndatabase, or in your own Neon database when you have set one with `setPartnerDataDatabase`. They are\ndeleted with the composition.\n\nThe shape is `{ \"tables\": [ ... ] }`. A table has a `name`, a list of `columns` and, optionally, a list of\n`indexes`, and optionally `countable`. A column has a `name` and a `type`, and optionally `primaryKey`,\n`notNull`, `unique`, `default` and `readable`. An index has a `name` and the `columns` it covers (1 to 8),\nand optionally `unique`. Any other key is refused, except `source` (see below).\n\n`countable` and `readable` are the read policy of the composition's own page: nothing the visitors sent\ncan be read back by the page until you say so. `countable: true` on a table lets the widget show how many\nrows it has; `readable: true` on a column lets it show or total that column's values (a tally of votes,\nan average rating). Mark only what is safe to show to anyone who opens the page. A column whose name\nholds `email`, `phone`, `address`, `token`, `password`, `secret`, `ip` or the like, and a `json`, `jsonb`\nor `uuid` column, cannot be `readable`.\n\n- Names (tables, columns, indexes) use lower-case letters, digits and underscores, start with a letter and\n are at most 63 characters long. A name that starts with `_` or `pg_` is reserved.\n- `type` is one of `text`, `smallint`, `integer` (or `int`), `bigint`, `numeric`, `real`,\n `double precision`, `boolean` (or `bool`), `date`, `timestamp`, `timestamptz` (or\n `timestamp with time zone`), `uuid`, `json`, `jsonb`, `serial`, `bigserial`.\n- `default` is a number, `true` or `false`, a text of at most 500 characters (no backslash), a JSON value\n for a `json` or `jsonb` column, or one of `now()`, `current_timestamp` and `current_date` (date and\n timestamp columns) and `gen_random_uuid()` (a `uuid` column). A `serial` column numbers itself and has\n no default.\n- A composition declares at most 20 tables, with at most 64 columns and 10 indexes each.\n\nA declaration that cannot be used is refused with `400` and `INVALID_DATABASE_SCHEMA`;\n`error.details.problems` lists what to change. Tables that already exist are never changed or dropped:\nsending more tables later, when you update the composition, adds the ones that are missing. When the\ncomposition is saved but its tables cannot be made just now, the answer says so in `meta.warnings`: send\nthe same `databaseSchema` again to retry.\n\nLeave it out and the composition gets the tables its widget declares in its own source\n(`src/lib/meta/database.json`), if any. Send `{ \"tables\": [] }` to give the composition no tables and\nkeep it from getting its widget's.\n\nA composition whose tables were made from its widget's own file reads back with `\"source\": \"widget\"` next\nto `tables`. When the widget is published again, a table, a field or an index its file gained is added to\nthem; nothing that exists is changed or dropped, and what a page may read does not change. Send `source`\nback as it was and it stays. Leave it out and the tables are yours: no widget's file adds anything to\nthem. It is the only value `source` can have.\n","example":{"tables":[{"name":"votes","columns":[{"name":"id","type":"serial","primaryKey":true},{"name":"choice","type":"text","notNull":true},{"name":"created_at","type":"timestamptz","default":"now()"}],"indexes":[{"name":"votes_choice_idx","columns":["choice"]}]}]}},"projectId":{"type":"string","format":"uuid","description":"ID of the project this composition belongs to","example":"123e4567-e89b-12d3-a456-426614174004"},"managedUserId":{"type":"string","format":"uuid","description":"Make the composition for one of your managed users, by Widgetic's id for the user. Send this or `managedUserExternalId`, not both. The managed user must be yours and active. The composition stays in your account; the tag lets you list one user's compositions and keeps it out of the others' lists. It is set here and does not change afterwards.","example":"b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e"},"managedUserExternalId":{"type":"string","maxLength":255,"description":"The same, by your own identifier for the user, the `externalId` you gave when you created the managed user (answers show it as `externalId`). Send this or `managedUserId`, not both.","example":"user-8841"}},"required":["name","widgetId"],"example":{"name":"My New Composition","widgetId":"123e4567-e89b-12d3-a456-426614174002","creatorId":"123e4567-e89b-12d3-a456-426614174001","content":{"key":"value"},"status":"draft","embedSettings":{"width":"100%","height":"500px"},"design":{"theme":"light","colors":{"primary":"#0066ff"}},"databaseSchema":{"tables":[{"name":"votes","columns":[{"name":"id","type":"serial","primaryKey":true},{"name":"choice","type":"text","notNull":true},{"name":"created_at","type":"timestamptz","default":"now()"}],"indexes":[{"name":"votes_choice_idx","columns":["choice"]}]}]},"projectId":"123e4567-e89b-12d3-a456-426614174004","managedUserId":"b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e","managedUserExternalId":"user-8841"}},"flags":[{"flag":"name","name":"name","type":"string","required":true,"description":"Name of the composition","property":"name"},{"flag":"widget-id","name":"widgetId","type":"string","required":true,"description":"ID of the widget this composition is based on","format":"uuid","property":"widgetId"},{"flag":"creator-id","name":"creatorId","type":"string","required":false,"description":"ID of the user creating the composition","format":"uuid","property":"creatorId"},{"flag":"status","name":"status","type":"string","required":false,"description":"Initial status of the composition","default":"draft","property":"status"},{"flag":"project-id","name":"projectId","type":"string","required":false,"description":"ID of the project this composition belongs to","format":"uuid","property":"projectId"},{"flag":"body-managed-user-id","name":"managedUserId","type":"string","required":false,"description":"Make the composition for one of your managed users, by Widgetic's id for the user. Send this or `managedUserExternalId`, not both. The managed user must be yours and active. The composition stays in your account; the tag lets you list one user's compositions and keeps it out of the others' lists. It is set here and does not change afterwards.","format":"uuid","property":"managedUserId"},{"flag":"body-managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"The same, by your own identifier for the user, the `externalId` you gave when you created the managed user (answers show it as `externalId`). Send this or `managedUserId`, not both.","maxLength":255,"property":"managedUserExternalId"}]}},{"name":"create-composition-content-item","aliases":[],"operationId":"createCompositionContentItem","method":"POST","path":"/compositions/{id}/content/{list}/items","server":"api","summary":"Add a content list item","description":"Creates one item in a widget content list without replacing the whole list. Item field keys are widget data (camelCase or snake_case are kept as sent). Partners use this to keep galleries in sync.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"list","name":"list","type":"string","required":true,"description":"List name from the content schema (`contentItems`) or `items`","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"},{"flag":"if-match","name":"If-Match","type":"string","required":false,"description":"Optional content version from ETag","in":"header"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","additionalProperties":true,"properties":{"position":{"type":"integer","minimum":0}}},"flags":[{"flag":"position","name":"position","type":"integer","required":false,"description":"","minimum":0,"property":"position"}]}},{"name":"create-composition-database-record","aliases":[],"operationId":"createCompositionDatabaseRecord","method":"POST","path":"/compositions/{id}/database/{table}","server":"api","summary":"Create Record In Composition Database","description":"Creates a new record in one of the composition's tables. Only the composition's own tables (made from its `databaseSchema` or from the tables its widget declares) can be reached; any other table name answers `404`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"Name of the database table","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object"},"flags":[]}},{"name":"delete-composition","aliases":["delete"],"operationId":"deleteComposition","method":"DELETE","path":"/compositions/{id}","server":"api","summary":"Delete Composition","description":"Deletes a specific composition, and the data it keeps (the tables it declared and what visitors left in them).","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The ID of the composition to delete","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"delete-composition-content-item","aliases":[],"operationId":"deleteCompositionContentItem","method":"DELETE","path":"/compositions/{id}/content/{list}/items/{itemId}","server":"api","summary":"Delete a content list item","description":"","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"list","name":"list","type":"string","required":true,"description":"","in":"path"},{"flag":"item-id","name":"itemId","type":"string","required":true,"description":"","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"},{"flag":"if-match","name":"If-Match","type":"string","required":false,"description":"","in":"header"}]},{"name":"delete-composition-data-source","aliases":[],"operationId":"deleteCompositionDataSource","method":"DELETE","path":"/compositions/{id}/data-source","server":"api","summary":"Remove the composition data source binding","description":"Removes the binding. Asking again, or asking for a composition that had none, is not an error: the composition has no data source afterwards, and that is all `removed: true` says.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"delete-composition-database-record","aliases":[],"operationId":"deleteCompositionDatabaseRecord","method":"DELETE","path":"/compositions/{id}/database/{table}/{recordId}","server":"api","summary":"Delete Record From Composition Database","description":"Deletes a specific record from one of the composition's tables. Only the composition's own tables (made from its `databaseSchema` or from the tables its widget declares) can be reached; any other table name answers `404`.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"Name of the database table","in":"path"},{"flag":"record-id","name":"recordId","type":"string","required":true,"description":"ID of the record to delete","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"delete-composition-entry-items","aliases":[],"operationId":"deleteCompositionEntryItems","method":"DELETE","path":"/compositions/{id}/entries/{table}/items","server":"api","summary":"Delete composition entry items by id or host context","description":"Delete by comma-separated ids (1–100) or by context filters (not both). Rate limit 30 requests per minute.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"","maxLength":63,"in":"path"},{"flag":"ids","name":"ids","type":"string","required":false,"description":"Comma-separated entry ids (1–100)","in":"query"},{"flag":"context","name":"context","type":"pairs","required":false,"description":"Host context filters as `context[key]=value` (max 5 keys, counting dotted `context.key` too). Use `column.<name>=value` for columns whose names collide with reserved parameters.","in":"query"},{"flag":"filter","name":"filter","type":"pairs","required":false,"description":"Column filters as `filter[columnName]=value` (same meaning as bare `columnName=value` or `column.columnName=value`; max 10 column filters counting all spellings). A plain `filter=value` still filters a column named `filter`.","in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"describe-composition-entries","aliases":[],"operationId":"describeCompositionEntries","method":"GET","path":"/compositions/{id}/entries","server":"api","summary":"Describe What a Composition Keeps","description":"Names the tables a composition's widget keeps entries in (the votes, ratings, answers or sign-ups its\nvisitors left), their columns, and how many entries each holds. Call it first to learn the table and\ncolumn names that `readCompositionEntries` takes.\n\nA widget that only shows content keeps nothing of its own: the answer is `hasData: false` with no tables,\nand that is not an error. A table that could not be counted has `rows: null`, which is not zero.\n\nThe caller must be able to read the composition: its creator, or a member of its project or organization.\nAn editor session cannot call this route.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"duplicate-composition","aliases":[],"operationId":"duplicateComposition","method":"POST","path":"/compositions/{id}/duplicate","server":"api","summary":"Duplicate Composition","description":"Creates a duplicate of an existing composition for the authenticated user.\n\nThe duplicate is made after the answer: the call returns `202` with an `operationId`. Ask\n`GET /operations/{id}` for that operation until its `status` is `completed`; the new composition is\nnamed in `metadata.newCompositionId`. A problem with the duplicate's database (for example, tables that\ncould not be made just now) is listed in `metadata.warnings`, which is absent when there is none.\n\nThe `Idempotency-Key` header is required (`400` without it). Send the same key when you retry, so one\nduplicate is made, not two.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition to duplicate","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":false,"kind":"json","contentType":"application/json","schema":{"type":"object","properties":{"name":{"type":"string","description":"Optional new name for the duplicate (defaults to \"Copy of [original name]\")","example":"My Duplicated Composition"},"includeDatabase":{"type":"boolean","description":"Whether the duplicate gets the tables the original declares (`databaseSchema`), empty and in a schema\nof their own (default false). What visitors left in the original is never copied, and the duplicate\nnever shares the original's data.\n","default":false}}},"flags":[{"flag":"name","name":"name","type":"string","required":false,"description":"Optional new name for the duplicate (defaults to \"Copy of [original name]\")","property":"name"},{"flag":"include-database","name":"includeDatabase","type":"boolean","required":false,"description":"Whether the duplicate gets the tables the original declares (`databaseSchema`), empty and in a schema\nof their own (default false). What visitors left in the original is never copied, and the duplicate\nnever shares the original's data.","default":false,"property":"includeDatabase"}]}},{"name":"export-composition-entry-items","aliases":[],"operationId":"exportCompositionEntryItems","method":"GET","path":"/compositions/{id}/entries/{table}/export","server":"api","summary":"Export composition entry items","description":"Streams up to 50,000 matching rows in batches of 1,000. Tables without cursor paging export at most one batch; when more rows exist the response sets `X-Widgetic-Export-Truncated: true`. Rate limit 20 requests per minute.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"","maxLength":63,"in":"path"},{"flag":"format","name":"format","type":"string","required":false,"description":"","enum":["csv","json"],"default":"csv","in":"query"},{"flag":"order","name":"order","type":"string","required":false,"description":"`desc` (the default) lists the newest entry first, `asc` the oldest. Entries are sorted by their time, then\nby their id (compared as text, byte by byte) in the same direction, so the order of two entries made at the\nsame instant is always the same; an entry with no time counts as the oldest. A table with no time column is\nsorted by its id. The managed-user feed sorts by time, then by composition id, table and id.","enum":["asc","desc"],"default":"desc","in":"query"},{"flag":"since","name":"since","type":"string","required":false,"description":"Only entries made at this time or later: `since` is INCLUSIVE (an entry made at exactly `since` is\nreturned). A date and time with an offset, such as `2026-10-01T00:00:00Z`; anything else answers 400\n`INVALID_SINCE`. An entry with no time never matches, and a table with no time column answers 400\n`ENTRY_TABLE_HAS_NO_TIME`.","format":"date-time","in":"query"},{"flag":"until","name":"until","type":"string","required":false,"description":"Only entries made before this time: `until` is EXCLUSIVE (an entry made at exactly `until` is NOT\nreturned), so `since=A&until=B` followed by `since=B&until=C` counts every entry once. Same form and same\nrefusals as `since` (400 `INVALID_UNTIL`).","format":"date-time","in":"query"},{"flag":"context","name":"context","type":"pairs","required":false,"description":"Host context filters as `context[key]=value` (max 5 keys, counting dotted `context.key` too). Use `column.<name>=value` for columns whose names collide with reserved parameters.","in":"query"},{"flag":"filter","name":"filter","type":"pairs","required":false,"description":"Column filters as `filter[columnName]=value` (same meaning as bare `columnName=value` or `column.columnName=value`; max 10 column filters counting all spellings). A plain `filter=value` still filters a column named `filter`.","in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-ai-answers-knowledge","aliases":[],"operationId":"getAiAnswersKnowledge","method":"GET","path":"/compositions/{id}/adapters/ai-answers/knowledge","server":"api","summary":"See the documents a widget's AI answers from","description":"The `ai-answers` adapter lets a widget answer a visitor's question with text an AI model writes from documents the\nwidget's owner provides. This lists those documents, and how much room is left for more: `totals` carries the widget's\nlimits next to what it uses.\n\nThe list never contains what a document says, only which documents there are: their title, size and state. A\n`processing` document is not used to answer questions yet (or an add of it was interrupted: remove it and add it\nagain).\n\nAdd a document with `addAiAnswersKnowledgeDocument`; remove one with `removeAiAnswersKnowledgeDocument`.\n\nThe caller must be allowed to see the widget: its owner, a partner for a customer it manages, or a Widgetic\nadministrator. An API key needs the `content:read` scope.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The widget (composition) whose documents to list","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-all-compositions","aliases":["list"],"operationId":"getAllCompositions","method":"GET","path":"/compositions","server":"api","summary":"Get All Compositions","description":"Retrieves all compositions with pagination support.\n\nPass `managedUserId` (Widgetic's id for one of your managed users) or `managedUserExternalId` (your own\nidentifier for the user) to list only the compositions made for that managed user. The compositions of\nyour other managed users, and your own, are left out. It combines with the other filters and with paging.\nA managed user that is not yours, or does not exist, is answered with 404 and `MANAGED_USER_NOT_FOUND`.\n\nAn editor session with the `creator` surface may call this to list the compositions of ONE widget it\nbuilt: it must send `widgetId`, and the answer holds only the compositions made for the session's\nmanaged user.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"page","name":"page","type":"integer","required":false,"description":"Page number for pagination","default":1,"minimum":1,"in":"query"},{"flag":"size","name":"size","type":"integer","required":false,"description":"Number of items per page","default":20,"minimum":1,"maximum":100,"in":"query"},{"flag":"sort","name":"sort","type":"string","required":false,"description":"The order of the list, as `<field>:<asc|desc>`. The field is `createdAt`, `updatedAt`, `name` or `status`\n(the snake_case spelling, such as `created_at`, is read too). `asc` or `desc` alone orders by `createdAt`.\nDefault: `createdAt:desc`. Any other field is a 400.","in":"query"},{"flag":"widget-id","name":"widgetId","type":"string","required":false,"description":"Filter compositions by widget ID","format":"uuid","in":"query"},{"flag":"workspace","name":"workspace","type":"string","required":false,"description":"Filter by workspace","enum":["true","false"],"in":"query"},{"flag":"project-folder","name":"projectFolder","type":"string","required":false,"description":"Only the compositions in this project (the project's id).","format":"uuid","in":"query"},{"flag":"status","name":"status","type":"string","required":false,"description":"Only the compositions in this status. Without it, every status is listed.","enum":["draft","published","archived"],"in":"query"},{"flag":"query-managed-user-id","name":"managedUserId","type":"string","required":false,"description":"List only the compositions made for this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both. Send it as `managedUserId`; the snake_case spelling `managed_user_id` is refused with a 400, not ignored.","format":"uuid","in":"query"},{"flag":"query-managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"List only the compositions made for the managed user you registered with this `externalId` (your own identifier for the user). Send this or `managedUserId`, not both.","maxLength":255,"in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-by-id","aliases":["get"],"operationId":"getCompositionById","method":"GET","path":"/compositions/{id}","server":"api","summary":"Get Composition","description":"Retrieves a specific composition by its ID.\n\nThe answer carries `direct`, which says whether the widget the composition is made from is drawn straight\ninto your page (`true`) or inside a frame (`false`), or `null` while the widget has no published version.\nRead it before you go live: a widget drawn without a frame writes its entries from your own page, so the\npublic entry route looks at the website that page is on. While the composition's allowed list is empty (you\nhave not restricted the allowed websites), every website may write; once you list websites, only those may,\nbesides Widgetic's own sites, the visitor's own machine and your own platform. A widget in a frame writes from\nWidgetic's own frame and is accepted whatever the list says. The public JSON of the composition stays open to\nevery website.\n\nFor a composition a team shares (one filed under a project of an organization, or made in a Shopify store's\nworkspace) the answer also carries `lastEditedBy` and `lastEditedAt`: who made the latest change and when, so\nthat a teammate who opens it can be told \"Anna changed this at 14:32\". A composition in a personal project\nleaves both out, and so does an answer given to an editor session or to a call made for one managed user: only\na person is told who a teammate is.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"Composition ID","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-content","aliases":[],"operationId":"getCompositionContent","method":"GET","path":"/compositions/{id}/content","server":"api","summary":"Get Current Content","description":"Retrieves the current content of a composition","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-content-schema","aliases":[],"operationId":"getCompositionContentSchema","method":"GET","path":"/compositions/{id}/schema/content","server":"api","summary":"Get Content Schema","description":"Retrieves the content schema for a composition based on its widget type","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-data-source","aliases":[],"operationId":"getCompositionDataSource","method":"GET","path":"/compositions/{id}/data-source","server":"api","summary":"Read the composition data source binding","description":"The data source that is bound to the composition: `url` and `refreshSeconds`. The answer's `data` is `null` when none is bound, and also when what is stored does not pass the rule for a public https address (a binding written by another path, such as the general embed-settings update, is never shown to a page unless it passes the same checks as this route).","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-database-record","aliases":[],"operationId":"getCompositionDatabaseRecord","method":"GET","path":"/compositions/{id}/database/{table}/{recordId}","server":"api","summary":"Get Record From Composition Database","description":"Retrieves a specific record from one of the composition's tables. Only the composition's own tables (made from its `databaseSchema` or from the tables its widget declares) can be reached; any other table name answers `404`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"Name of the database table","in":"path"},{"flag":"record-id","name":"recordId","type":"string","required":true,"description":"ID of the record to retrieve","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-database-records","aliases":[],"operationId":"getCompositionDatabaseRecords","method":"GET","path":"/compositions/{id}/database/{table}","server":"api","summary":"Get All Records From Composition Database Table","description":"Retrieves all records from one of the composition's tables. Only the composition's own tables (made from its `databaseSchema` or from the tables its widget declares) can be reached; any other table name answers `404`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"Name of the database table","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-design","aliases":[],"operationId":"getCompositionDesign","method":"GET","path":"/compositions/{id}/design","server":"api","summary":"Get Composition Design","description":"Retrieves only the design field from a composition.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-design-schema","aliases":[],"operationId":"getCompositionDesignSchema","method":"GET","path":"/compositions/{id}/schema/design","server":"api","summary":"Get Design Schema","description":"Retrieves the design schema for a composition based on its widget type","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-embed-schema","aliases":[],"operationId":"getCompositionEmbedSchema","method":"GET","path":"/compositions/{id}/schema/embed","server":"api","summary":"Get Embed Schema","description":"Retrieves the definition of the composition's Embed tab: which settings exist, what each one holds, what it\ndefaults to, and when its control is shown. It is the third tab beside `schema/content` and `schema/design`,\nin the same format (`componentGroups` → `components` → `propertyGroups` → `properties`, each property with a\n`type`, a `defaultValue` and an `inputController`), so a client that draws those two tabs draws this one with\nthe same code.\n\nThe schema is the same for every composition (it is the one Widgetic's own Embed Editor draws). The\ncomposition id decides who may ask: the caller must be able to read the composition.\n\nReading and saving the values:\n- `GET /compositions/{id}/embed-settings` returns what the composition saved. A composition whose Embed tab\n was never saved has nothing to return; seed the form from `defaults`, which holds the `defaultValue` of\n every setting that declares one.\n- `PUT /compositions/{id}` with `embedSettings` saves them. The gateway stores `embedSettings` as given and\n does not check it against this schema; the schema is how a client knows what to put there.\n\nA property's `visibilityCondition` says when its control is shown: `condition` is a comparison of the\ncurrent values of the properties named in `dependsOn` (`useIframe === false`, `embedTarget === 'mixed' &&\nkeepDimensions === false`). While it is false the Embed Editor does not show the control.\n\nA colour picker with `showOpacityControls` saves its opacity next to the colour, under the key\n`<name>_opacity` as a number from 0 to 1 (`overlayBackgroundColor` → `overlayBackgroundColor_opacity`).\nIts `defaultTransparency` is a percentage and the saved value a fraction (50 → 0.5).","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-embed-settings","aliases":[],"operationId":"getCompositionEmbedSettings","method":"GET","path":"/compositions/{id}/embed-settings","server":"api","summary":"Get Composition Embed Settings","description":"Retrieves only the embedSettings field from a composition.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-preview","aliases":[],"operationId":"getCompositionPreview","method":"GET","path":"/compositions/{id}/preview","server":"api","summary":"Get Composition Preview","description":"Returns the address of the page an editor shows the composition in: the page of the widget's published\nversion on the CDN, with the widget and version it is. Load it in a frame, then send the frame what to show\n(the content, design and embed settings you read from the composition, and every change the user makes) with\n`postMessage`: `{ type: 'widgetic:update', design }`, `{ type: 'widgetic:update', content }` or\n`{ type: 'widgetic:update', contentItems }`.\n\nThe version is the one embeds show, so the editor previews what a visitor gets. The address does not carry\nthe composition id: an embed uses the id to fetch the saved composition itself, while an editor sends the\ncurrent state (unsaved changes included).\n\nA widget that has never been published has no page to show: the answer is 409 `WIDGET_NOT_PUBLISHED`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-composition-versions","aliases":[],"operationId":"getCompositionVersions","method":"GET","path":"/compositions/{id}/versions","server":"api","summary":"Get Composition Versions","description":"Retrieves version history for a specific composition.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"Composition ID","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-compositions-batch","aliases":[],"operationId":"getCompositionsBatch","method":"POST","path":"/compositions/batch","server":"api","summary":"Get Compositions Batch","description":"Retrieves multiple compositions by their IDs in a single request.\nThis endpoint includes domain validation for embed contexts and is used by the embed SDK.\n\nEach composition is checked on its own. If its owner listed the websites it may appear on (the\nworkspace's domain list, or the `allowedDomains` of a partner the owner administers) and the request\ncomes from another website (the `Origin` header, or the host of `Referer` when there is no `Origin`),\nthat composition is left out of `compositions` and reported in `errors` as\n`Domain access denied: <reason>`; the others are still returned. A request that names no website, and\nan owner who listed nothing, are not restricted, and a page on the visitor's own machine (`localhost`,\n`127.0.0.1` or `[::1]`, on any port) is always allowed. So is the website where a partner integrates\nWidgetic, with every subdomain of it (`platformDomain` in the answer of `GET /partners/mine`), for the\ncompositions of that partner: a partner's list never has to name it.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["compositionIds"],"properties":{"compositionIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Array of composition IDs to retrieve","maxItems":50}}},"flags":[{"flag":"composition-ids","name":"compositionIds","type":"string[]","required":true,"description":"Array of composition IDs to retrieve","property":"compositionIds"}]}},{"name":"list-composition-entry-items","aliases":[],"operationId":"listCompositionEntryItems","method":"GET","path":"/compositions/{id}/entries/{table}/items","server":"api","summary":"List composition entry items with paging and filters","description":"Reads one widget table with cursor paging (time then id in byte order), optional since/until on the time column, and up to five context filters plus ten column filters. Rate limit 120 requests per minute. Each read batch uses a 5 s statement timeout.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"","maxLength":63,"in":"path"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"","default":25,"minimum":1,"maximum":100,"in":"query"},{"flag":"order","name":"order","type":"string","required":false,"description":"`desc` (the default) lists the newest entry first, `asc` the oldest. Entries are sorted by their time, then\nby their id (compared as text, byte by byte) in the same direction, so the order of two entries made at the\nsame instant is always the same; an entry with no time counts as the oldest. A table with no time column is\nsorted by its id. The managed-user feed sorts by time, then by composition id, table and id.","enum":["asc","desc"],"default":"desc","in":"query"},{"flag":"since","name":"since","type":"string","required":false,"description":"Only entries made at this time or later: `since` is INCLUSIVE (an entry made at exactly `since` is\nreturned). A date and time with an offset, such as `2026-10-01T00:00:00Z`; anything else answers 400\n`INVALID_SINCE`. An entry with no time never matches, and a table with no time column answers 400\n`ENTRY_TABLE_HAS_NO_TIME`.","format":"date-time","in":"query"},{"flag":"until","name":"until","type":"string","required":false,"description":"Only entries made before this time: `until` is EXCLUSIVE (an entry made at exactly `until` is NOT\nreturned), so `since=A&until=B` followed by `since=B&until=C` counts every entry once. Same form and same\nrefusals as `since` (400 `INVALID_UNTIL`).","format":"date-time","in":"query"},{"flag":"cursor","name":"cursor","type":"string","required":false,"description":"","in":"query"},{"flag":"context","name":"context","type":"pairs","required":false,"description":"Host context filters as `context[key]=value` (max 5 keys, counting dotted `context.key` too). Use `column.<name>=value` for columns whose names collide with reserved parameters.","in":"query"},{"flag":"filter","name":"filter","type":"pairs","required":false,"description":"Column filters as `filter[columnName]=value` (same meaning as bare `columnName=value` or `column.columnName=value`; max 10 column filters counting all spellings). A plain `filter=value` still filters a column named `filter`.","in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"patch-composition-content-item","aliases":[],"operationId":"patchCompositionContentItem","method":"PATCH","path":"/compositions/{id}/content/{list}/items/{itemId}","server":"api","summary":"Update a content list item","description":"","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"list","name":"list","type":"string","required":true,"description":"","in":"path"},{"flag":"item-id","name":"itemId","type":"string","required":true,"description":"","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"},{"flag":"if-match","name":"If-Match","type":"string","required":false,"description":"","in":"header"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","additionalProperties":true},"flags":[]}},{"name":"read-composition-entries","aliases":[],"operationId":"readCompositionEntries","method":"GET","path":"/compositions/{id}/entries/{table}","server":"api","summary":"Read the Entries of a Composition's Table","description":"Counts the entries visitors left in one table, tallies them per value of a column on request, and returns\nthe newest entries only when asked to. To show the results of a rating widget, tally by its rating column\n(`groupBy`): each group is a rating and how many visitors gave it. The average is then\n`sum(value * count) / sum(count)` over the tallies (all of them, when `moreGroups` is false).\n\n- **`totalRows`** is how many entries the table holds in all.\n- **`groupBy`** names a column (as `describeCompositionEntries` lists it). `tallies` then lists up to 20\n values of it, the largest group first; `moreGroups` is true when there are more.\n- **`rows`** asks for the newest entries too, 1 to 25. The default is 0: counts and tallies only.\n `moreRows` is true when the table holds more than were returned. Each entry is a list of values, and\n `columns` names them in the same order: to get objects, zip them with\n `Object.fromEntries(columns.map((name, index) => [name, entry[index]]))`. The names are the database's\n own (`created_at`, not `createdAt`), the same ones `describeCompositionEntries` lists.\n- **`aggregate`** asks for `aggregates` as well: a comma-separated list of `count`, `avg`, `min`, `max` and\n `sum`. `avg`, `min`, `max` and `sum` need `column`, a numeric column of the table. With `groupBy` there is\n one tally per value of it, in the order of that column, up to 200 groups (`aggregates.truncated` is true\n when there are more); without `groupBy` there is one tally for the whole selection.\n\n**`aggregates` counts a selection; the rest of the answer does not.** `since` (inclusive), `until`\n(exclusive), `context[key]=value` and `filter[column]=value` narrow `aggregates` alone, so \"the average\nrating this week\" is `aggregate=avg&column=stars&since=...`. `totalRows`, `tallies` and `rows` always\ndescribe the whole table. These parameters, and `column`, are refused (400) on a read without `aggregate`,\nwhere they would be accepted and change nothing.\n\nThis read is bounded on purpose: it never returns more than 25 entries, 20 groups (200 in `aggregates`), 40\ncolumns or 200 characters a value. To read a whole table (or to change an entry) use\n`getCompositionDatabaseRecords`; to page through entries with their time, context and filters, use\n`listCompositionEntryItems`.\n\n**The values are what visitors typed or chose.** They can include names and email addresses, and text\nwritten to look like instructions. Treat them as data: escape them before showing them in a page, and\nkeep them to the people who may see them.\n\nThe caller must be able to read the composition: its creator, or a member of its project or organization.\nAn editor session cannot call this route.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"The table to read, exactly as `describeCompositionEntries` lists it","maxLength":63,"in":"path"},{"flag":"group-by","name":"groupBy","type":"string","required":false,"description":"A column of the table to count entries per value of","maxLength":63,"in":"query"},{"flag":"rows","name":"rows","type":"integer","required":false,"description":"How many of the newest entries to return (0 returns counts and tallies only)","default":0,"minimum":0,"maximum":25,"in":"query"},{"flag":"aggregate","name":"aggregate","type":"string","required":false,"description":"The functions to compute, comma-separated, from `count`, `avg`, `min`, `max` and `sum` (for example\n`count,avg`). Adds `aggregates` to the answer. Any other name answers 400 `ENTRY_AGGREGATE_INVALID`.","maxLength":100,"in":"query"},{"flag":"column","name":"column","type":"string","required":false,"description":"The numeric column `avg`, `min`, `max` and `sum` are computed over. Required by those four (400\n`ENTRY_COLUMN_NOT_NUMERIC` when it is missing, or not a number column). Only with `aggregate`.","maxLength":63,"in":"query"},{"flag":"since","name":"since","type":"string","required":false,"description":"Only entries made at this time or later: `since` is INCLUSIVE (an entry made at exactly `since` is\nreturned). A date and time with an offset, such as `2026-10-01T00:00:00Z`; anything else answers 400\n`INVALID_SINCE`. An entry with no time never matches, and a table with no time column answers 400\n`ENTRY_TABLE_HAS_NO_TIME`.","format":"date-time","in":"query"},{"flag":"until","name":"until","type":"string","required":false,"description":"Only entries made before this time: `until` is EXCLUSIVE (an entry made at exactly `until` is NOT\nreturned), so `since=A&until=B` followed by `since=B&until=C` counts every entry once. Same form and same\nrefusals as `since` (400 `INVALID_UNTIL`).","format":"date-time","in":"query"},{"flag":"context","name":"context","type":"pairs","required":false,"description":"Host context filters as `context[key]=value` (max 5 keys, counting dotted `context.key` too). Use `column.<name>=value` for columns whose names collide with reserved parameters.","in":"query"},{"flag":"filter","name":"filter","type":"pairs","required":false,"description":"Column filters as `filter[columnName]=value` (same meaning as bare `columnName=value` or `column.columnName=value`; max 10 column filters counting all spellings). A plain `filter=value` still filters a column named `filter`.","in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"remove-ai-answers-knowledge-document","aliases":[],"operationId":"removeAiAnswersKnowledgeDocument","method":"DELETE","path":"/compositions/{id}/adapters/ai-answers/knowledge/documents/{documentId}","server":"api","summary":"Remove a document from a widget's knowledge base","description":"Removes one document and its passages: the widget's AI stops answering from it at once. Take the `documentId` from\n`getAiAnswersKnowledge`.\n\nThe caller must be allowed to change the widget. An API key needs the `content:write` scope.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The widget (composition) the document is in","format":"uuid","in":"path"},{"flag":"document-id","name":"documentId","type":"string","required":true,"description":"The document to remove","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"set-composition-data-source","aliases":[],"operationId":"setCompositionDataSource","method":"PUT","path":"/compositions/{id}/data-source","server":"api","summary":"Bind a public JSON data source for embed refresh","description":"Stores an https URL the widget runtime may read on load and every `refreshSeconds`. The URL must name a public host (no loopback, link-local or metadata addresses). A composition has one binding: saving another replaces it. The answer is the binding as it was saved (`url`, `refreshSeconds`).","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["url","refreshSeconds"],"properties":{"url":{"type":"string","maxLength":2048},"refreshSeconds":{"type":"integer","minimum":10,"maximum":86400}}},"flags":[{"flag":"url","name":"url","type":"string","required":true,"description":"","maxLength":2048,"property":"url"},{"flag":"refresh-seconds","name":"refreshSeconds","type":"integer","required":true,"description":"","minimum":10,"maximum":86400,"property":"refreshSeconds"}]}},{"name":"set-dynamic-content","aliases":[],"operationId":"setDynamicContent","method":"PUT","path":"/compositions/{id}/content","server":"api","summary":"Set Dynamic Content","description":"Updates the content of a composition dynamically with optional schema validation.\n\nAn editor session (see `createManagedUserEditorSession`) can send only `content`, `validate` and\n`partial`, and only for a composition made for its managed user. A body that names `version`, `source`\nor any other field is refused with `403` and `EDITOR_SESSION_FIELD_NOT_ALLOWED`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"},{"flag":"if-match","name":"If-Match","type":"string","required":false,"description":"Optional content version from ETag (`\"3\"`, `3`, or `*`)","in":"header"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","properties":{"content":{"type":"object","description":"Content data to set"},"validate":{"type":"boolean","description":"Whether to validate content against widget schema","default":true},"partial":{"type":"boolean","description":"Whether to merge with existing content (true) or replace entirely (false)","default":false},"version":{"type":"string","description":"Optional version identifier for the content"},"source":{"type":"string","description":"Source of the update for tracking purposes"}},"required":["content"],"example":{"content":{"title":"Dynamic Title","items":[{"image":"https://example.com/image1.jpg","title":"Product 1"}]},"validate":true,"partial":true,"source":"ecommerce-api"}},"flags":[{"flag":"validate","name":"validate","type":"boolean","required":false,"description":"Whether to validate content against widget schema","default":true,"property":"validate"},{"flag":"partial","name":"partial","type":"boolean","required":false,"description":"Whether to merge with existing content (true) or replace entirely (false)","default":false,"property":"partial"},{"flag":"version","name":"version","type":"string","required":false,"description":"Optional version identifier for the content","property":"version"},{"flag":"source","name":"source","type":"string","required":false,"description":"Source of the update for tracking purposes","property":"source"}]}},{"name":"update-composition","aliases":["update"],"operationId":"updateComposition","method":"PUT","path":"/compositions/{id}","server":"api","summary":"Update Composition","description":"Updates a specific composition.\n\nThe `Idempotency-Key` header is required (`400` without it). Send the same key when you retry.\n\nAn editor session (see `createManagedUserEditorSession`) can send only `name`, `content`, `design` and\n`embedSettings`, and only for a composition made for its managed user. A body that names any other field\nis refused with `403` and `EDITOR_SESSION_FIELD_NOT_ALLOWED`.\n\nSend `databaseSchema` to add tables to the composition: the tables that are missing are made, and\nthe ones that exist are left as they are. A `databaseSchema` that cannot be used is refused before anything\nis saved, with `400` and `INVALID_DATABASE_SCHEMA`. If the composition is saved but its tables cannot be made\njust now, the answer is still `200` and `meta.warnings` says so.\n\n**When several people edit the same composition.** A save that carries `content` can name the content version\nit started from in the `If-Match` header (the `contentVersion` of the composition you read, or the `ETag` of an\nearlier answer). If somebody saved the content since, nothing is overwritten: the save is refused with `412` and\n`CONTENT_VERSION_MISMATCH`, and `error.details.currentVersion` is the version to start again from. For a\ncomposition a team shares, `error.details` also says who got there first: `lastEditedBy`\n(`{ id, name }`, or `null` when nobody can be named) and `lastEditedAt`. An editor session, or a call made for\none managed user, is told the version only. A save without `If-Match` still goes through, and the other\nperson's change is replaced, as it always was.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"Composition ID","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"},{"flag":"if-match","name":"If-Match","type":"string","required":false,"description":"Optional content version from `contentVersion` or an `ETag` (`\"3\"`, `3`, or `*`). Read only when the body has `content`.","in":"header"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","properties":{"name":{"type":"string","description":"Updated name of the composition","example":"Updated Composition Name"},"widgetId":{"type":"string","format":"uuid","description":"Updated widget ID","example":"123e4567-e89b-12d3-a456-426614174002"},"content":{"type":"object","description":"Updated content data","example":{"key":"new value"}},"status":{"type":"string","description":"Updated status","example":"published"},"embedSettings":{"type":"object","description":"Updated embed settings","example":{"width":"100%","height":"600px"}},"design":{"type":"object","description":"Updated design settings","example":{"theme":"dark","colors":{"primary":"#0044cc"}}},"databaseSchema":{"type":"object","description":"Tables to make for the composition, in the shape `createComposition` takes\n(`{ \"tables\": [ ... ] }`, with the same names, types, defaults and limits). Only the tables that are\nmissing are made. A table that already exists is never changed or dropped: it keeps its columns, so a\ncolumn added to the declaration is not added to it. (Tables made from the widget's own file are the one\nexception, brought up to date when the widget is published; see `source` on `createComposition`.) A\ndeclaration that cannot be used is refused with\n`400` and `INVALID_DATABASE_SCHEMA`. When the composition is saved but its tables cannot be made just\nnow, `meta.warnings` says so: send the same `databaseSchema` again to retry.\n"},"projectId":{"type":"string","format":"uuid","description":"Updated project ID","example":"123e4567-e89b-12d3-a456-426614174004"}},"example":{"name":"Updated Composition Name","widgetId":"123e4567-e89b-12d3-a456-426614174002","content":{"key":"new value"},"status":"published","embedSettings":{"width":"100%","height":"600px"},"design":{"theme":"dark","colors":{"primary":"#0044cc"}},"databaseSchema":{},"projectId":"123e4567-e89b-12d3-a456-426614174004"}},"flags":[{"flag":"name","name":"name","type":"string","required":false,"description":"Updated name of the composition","property":"name"},{"flag":"widget-id","name":"widgetId","type":"string","required":false,"description":"Updated widget ID","format":"uuid","property":"widgetId"},{"flag":"status","name":"status","type":"string","required":false,"description":"Updated status","property":"status"},{"flag":"project-id","name":"projectId","type":"string","required":false,"description":"Updated project ID","format":"uuid","property":"projectId"}]}},{"name":"update-composition-database-record","aliases":[],"operationId":"updateCompositionDatabaseRecord","method":"PUT","path":"/compositions/{id}/database/{table}/{recordId}","server":"api","summary":"Update Record In Composition Database","description":"Updates a specific record in one of the composition's tables. Only the composition's own tables (made from its `databaseSchema` or from the tables its widget declares) can be reached; any other table name answers `404`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"Name of the database table","in":"path"},{"flag":"record-id","name":"recordId","type":"string","required":true,"description":"ID of the record to update","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object"},"flags":[]}},{"name":"update-composition-status","aliases":[],"operationId":"updateCompositionStatus","method":"PUT","path":"/compositions/{id}/status","server":"api","summary":"Update Composition Status","description":"Updates the status of a specific composition.\n\nThe `Idempotency-Key` header is required (`400` without it). Send the same key when you retry.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"Composition ID","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["draft","published","archived"],"description":"New status to set"}}},"flags":[{"flag":"status","name":"status","type":"string","required":true,"description":"New status to set","enum":["draft","published","archived"],"property":"status"}]}},{"name":"validate-composition-content","aliases":[],"operationId":"validateCompositionContent","method":"POST","path":"/compositions/{id}/content/validate","server":"api","summary":"Validate Content","description":"Checks content against the composition's widget schema without saving it. Send what a content update (`PUT /compositions/{id}/content`) would store, `{ \"design\": {...}, \"content\": {...} }`, or the widget's values alone. Each value is checked by the kind of field the widget declares for it: text, a number within the limits of its slider, true or false, one of the choices of a dropdown, a color, a font, the address of an image, a date. The items of the widget's list are checked the same way, and their number against the limits of the list. A value that cannot be what the field holds, or a number outside its range, is an error and `valid` is false. Text longer than the field takes, a number or true/false sent as text, and a name the widget has no field for are warnings, and `valid` stays true. The design is not checked here. Nothing is saved and nothing in the body is changed.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the composition","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","additionalProperties":true,"description":"The content to check, as a content update stores it: `{ \"design\": {...}, \"content\": {...} }`, or the widget's values alone."},"flags":[]}}]},{"name":"credits","title":"Credits","description":"The Credits operations of the Widgetic API.","hidden":false,"commands":[{"name":"get-user-credit-balance","aliases":[],"operationId":"getUserCreditBalance","method":"GET","path":"/credits/user/{userId}/balance","server":"api","summary":"Get an account's credit balance","description":"Returns the credits an account holds: the balance, and how much of it is reserved by operations that\nare still running.\n\nAn account reads its own balance; with a partner's API key, that is the account of the person who\nadministers the partner. Widgetic administrators can read any account's. An account that has never held\ncredits reads a balance of 0. `getPartnerCreditBalance` reads this same balance by partner id.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"user-id","name":"userId","type":"string","required":true,"description":"The account whose balance to read","format":"uuid","in":"path"}]},{"name":"get-user-credit-transactions","aliases":[],"operationId":"getUserCreditTransactions","method":"GET","path":"/credits/user/{userId}/transactions","server":"api","summary":"List an account's credit ledger","description":"Lists the entries of an account's credit ledger, newest first. This is also how an account's usage is\nread: every operation that spends credits is a `debit` entry, so summing the `debit` entries over a\nperiod gives the credits consumed in it, and the other types give what was added.\n\nAn account reads its own ledger; with a partner's API key, that is the account of the person who\nadministers the partner. Widgetic administrators can read any account's. `getPartnerCreditTransactions`\nreads this same ledger by partner id.\n\nThe page is always bounded: `limit` defaults to 100 and is capped at 500, `offset` defaults to 0.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"user-id","name":"userId","type":"string","required":true,"description":"The account whose ledger to read","format":"uuid","in":"path"},{"flag":"transaction-type","name":"transactionType","type":"string","required":false,"description":"Only entries of this type, for instance `debit` for the credits spent. The types are listed on `AccountCreditTransaction`.","in":"query"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"Maximum number of entries to return","default":100,"minimum":1,"maximum":500,"in":"query"},{"flag":"offset","name":"offset","type":"integer","required":false,"description":"Number of entries to skip","default":0,"minimum":0,"in":"query"}]}]},{"name":"entitlements","title":"Entitlements","description":"The Entitlements operations of the Widgetic API.","hidden":false,"commands":[{"name":"get-managed-user-entitlements","aliases":[],"operationId":"getManagedUserEntitlements","method":"GET","path":"/entitlements/managed-users/{managedUserId}","server":"api","summary":"Get managed user entitlements","description":"Retrieves all entitlements for a managed user (admin or partner access).","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"managed-user-id","name":"managedUserId","type":"string","required":true,"description":"Managed user ID","format":"uuid","in":"path"},{"flag":"include-expired","name":"includeExpired","type":"boolean","required":false,"description":"Include expired entitlements in results","in":"query"},{"flag":"feature","name":"feature","type":"string","required":false,"description":"Filter by specific feature","in":"query"},{"flag":"source","name":"source","type":"string","required":false,"description":"Filter by entitlement source","in":"query"}]},{"name":"get-partner-entitlements","aliases":[],"operationId":"getPartnerEntitlements","method":"GET","path":"/entitlements/partners/{partnerId}","server":"api","summary":"Get partner entitlements","description":"Retrieves all entitlements for a partner (admin or partner access).","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"Partner ID","format":"uuid","in":"path"},{"flag":"include-expired","name":"includeExpired","type":"boolean","required":false,"description":"Include expired entitlements in results","in":"query"},{"flag":"feature","name":"feature","type":"string","required":false,"description":"Filter by specific feature","in":"query"},{"flag":"source","name":"source","type":"string","required":false,"description":"Filter by entitlement source","in":"query"}]}]},{"name":"invoices","title":"Invoices","description":"The Invoices operations of the Widgetic API.","hidden":false,"commands":[{"name":"get-invoices-by-partner-id","aliases":[],"operationId":"getInvoicesByPartnerId","method":"GET","path":"/invoices/partner/{partnerId}","server":"api","summary":"List a partner's invoices","description":"Lists the invoices issued to a partner, newest first. Each carries the price (`subtotal`), the tax on\nit (`taxAmount`) and what is paid (`total`), its status as the payment provider reports it, why the\nlast payment failed (`failureReason`), and the page where it is paid (`hostedInvoiceUrl`).\n\nA partner reads its own: the caller must be the person who administers the partner (an API key of\nthat account works), and anyone else is refused with 403 whether or not the partner exists.\nWidgetic administrators can read any partner's and see exactly what the partner sees; the full\nrecords, with the internal fields, are `getAllInvoices` filtered by partner.\n\nThe page is always bounded: `limit` defaults to 50 and is capped at 100, `offset` defaults to 0.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose invoices to list","format":"uuid","in":"path"},{"flag":"status","name":"status","type":"string","required":false,"description":"Only invoices with this status","enum":["draft","open","pending","paid","failed","void","uncollectible","refunded","canceled"],"in":"query"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"Maximum number of invoices to return","default":50,"minimum":1,"maximum":100,"in":"query"},{"flag":"offset","name":"offset","type":"integer","required":false,"description":"Number of invoices to skip","default":0,"minimum":0,"in":"query"}]}]},{"name":"managed-users","title":"Managed Users","description":"The Managed Users operations of the Widgetic API.","hidden":false,"commands":[{"name":"copy-preset-for-managed-user","aliases":[],"operationId":"copyPresetForManagedUser","method":"POST","path":"/managed-users/{id}/presets/{key}/copy","server":"api","summary":"Give a managed user a copy of one of your presets","description":"Copies the preset `key` (see `setPartnerPreset`) onto a new canvas for one of your managed users, so their\nwidget creator opens on a widget of their own that starts from yours. The copy is a draft widget in your\naccount, filed under the managed user: `getAllWidgets` with `managedUserId` lists it, and no other managed\nuser's editor can reach it.\n\nThe copy carries the preset's code, design and content schemas, and thumbnail, and starts from a fork of its\ncode repository, so the user's chat edits begin where yours left off. It does not carry the preset's\ndatabase or its compositions. It is not a preset itself, and changing or removing the preset afterwards\nnever touches it.\n\nTo start a composition (the content and design editor) instead, you do not need a copy: call\n`createComposition` with the preset's `widgetId` (from `listPartnerPresets`) and the managed user.\n\nThe `Idempotency-Key` header is required (`400` without it). Send the same key when you retry, so one copy\nis made, not two. A copy that could not be made leaves nothing behind (503 `PRESET_COPY_FAILED`).\n\nThe managed user must be yours and active.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The managed user to give the copy to (Widgetic's id for the user)","format":"uuid","in":"path"},{"flag":"key","name":"key","type":"string","required":true,"description":"The key of the preset to copy","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":false,"kind":"json","contentType":"application/json","schema":{"type":"object","additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":120,"description":"Names the copy. Left out, the copy is named after the preset's title, or the widget's own name."}},"example":{"name":"Example name"}},"flags":[{"flag":"name","name":"name","type":"string","required":false,"description":"Names the copy. Left out, the copy is named after the preset's title, or the widget's own name.","minLength":1,"maxLength":120,"property":"name"}]}},{"name":"create-composition-from-preset","aliases":[],"operationId":"createCompositionFromPreset","method":"POST","path":"/managed-users/{id}/presets/{key}/composition","server":"api","summary":"Create a composition from one of your presets in one call","description":"Validates the name, optional content, design, embed settings and locale, then creates a draft composition\nfiled under the managed user, built on the preset's widget, in a single insert. A failure never leaves a\nhalf-made composition.\n\nThe preset's widget must be published. The managed user must be yours and active.\n\nA widget that keeps what visitors send (a form, a vote, a rating) declares its tables in its own source\n(`src/lib/meta/database.json`); they are made for the composition here, in a schema of its own, as the widget\nwas when it was last published. A widget that keeps nothing leaves the composition without tables. If the\ntables cannot be made just now, or the widget's file cannot be used, the composition is still created (`201`)\nand `meta.warnings` says why; the tables are made the next time the widget is generated.\n\nThe `Idempotency-Key` header is required (`400` without it). Send the same key when you retry, so one\ncomposition is created, not two.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"key","name":"key","type":"string","required":true,"description":"","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"content":{"type":"object"},"design":{"type":["object","null"]},"embedSettings":{"type":"object"},"locale":{"type":"string","description":"BCP-47 language tag stored on embed settings for the widget runtime"}}},"flags":[{"flag":"name","name":"name","type":"string","required":true,"description":"","property":"name"},{"flag":"locale","name":"locale","type":"string","required":false,"description":"BCP-47 language tag stored on embed settings for the widget runtime","property":"locale"}]}},{"name":"create-managed-user","aliases":["create"],"operationId":"createManagedUser","method":"POST","path":"/managed-users","server":"api","summary":"Create Managed User","description":"Creates a new managed user for a partner account.\n\nThe `Idempotency-Key` header is required (`400` without it). Send the same key when you retry, so one\nmanaged user is created, not two.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["externalId"],"properties":{"externalId":{"type":"string","description":"Your own identifier for the managed user. Must be unique among the managed users you manage (409 otherwise); another partner using the same value does not matter.","example":"external-user-123"},"managerId":{"type":"string","format":"uuid","description":"ID of the user who will manage this managed user. Defaults to your own user ID; a partner can only use their own ID (403 otherwise), only an admin can name another manager.","example":"123e4567-e89b-12d3-a456-426614174001"},"metadata":{"type":"object","description":"Additional metadata for the managed user","example":{"firstName":"John","lastName":"Doe","role":"editor"}}},"example":{"externalId":"external-user-123","managerId":"123e4567-e89b-12d3-a456-426614174001","metadata":{"firstName":"John","lastName":"Doe","role":"editor"}}},"flags":[{"flag":"external-id","name":"externalId","type":"string","required":true,"description":"Your own identifier for the managed user. Must be unique among the managed users you manage (409 otherwise); another partner using the same value does not matter.","property":"externalId"},{"flag":"manager-id","name":"managerId","type":"string","required":false,"description":"ID of the user who will manage this managed user. Defaults to your own user ID; a partner can only use their own ID (403 otherwise), only an admin can name another manager.","format":"uuid","property":"managerId"}]}},{"name":"create-managed-user-editor-session","aliases":[],"operationId":"createManagedUserEditorSession","method":"POST","path":"/managed-users/{id}/editor-sessions","server":"api","summary":"Create an editor session for a managed user","description":"Makes a short-lived credential that lets the browser of ONE of your managed users use Widgetic's editor\npieces, such as the File Browser and the composition editor, without your API key ever reaching that\nbrowser. Call this from your server when the page that hosts the editor opens, and hand the `token` in\nthe answer to that page.\n\nThe token is shown once. It starts with `wgt_ses_` and the browser sends it as\n`Authorization: Bearer <token>`. It can do only what the `surfaces` you name allow, and only for this\nmanaged user:\n\n- `files`: upload a file (`uploadsPost`) and list the files uploaded for this user (`uploadsGet`). Every\n file is filed for this user and stored in your own storage destination, never in Widgetic's: a file a\n customer uploads is yours to keep, and takes none of your storage allowance. Partner storage is not\n optional, so no session, whatever its surfaces, can be made until you have set a destination\n (`setPartnerStorageDestination`); until then it is refused with 409 and `PARTNER_STORAGE_REQUIRED`, and\n when the destination cannot be used with 503 and `STORAGE_DESTINATION_UNAVAILABLE`. A destination you\n remove after the session was made stops its uploads too (the same 409; the browser is only told that\n the feature is not available right now). To keep a customer from uploading, leave `files` out of the\n session and hand the editors your own media library instead (`mediaLibrary`).\n- `editor`: edit one of this user's compositions: read it (`getCompositionById`, its content, design,\n embed settings and schemas), check a content change (`validateCompositionContent`), and change its\n name, content, design and embed settings (`updateComposition`, `setDynamicContent`). For a widget that\n answers visitors' questions from documents (the `ai-answers` adapter), it also lists those documents,\n adds one as text or as a file (a text file or a picture; a PDF once PDFs are read), and removes one\n (`getAiAnswersKnowledge`, `addAiAnswersKnowledgeDocument`, `addAiAnswersKnowledgeFile`,\n `removeAiAnswersKnowledgeDocument`); adding costs no credits, and is limited for each of your users\n separately. It can reach\n only compositions you created for this managed user (tag them with `managedUserId` when you create\n them); any other composition, including another of your users', answers 404.\n- `creator`: use Widgetic's widget creator for this user, including its AI. Draw on canvases and save\n them (`getAllCanvases`, `getCanvas`, `createCanvas`, `updateCanvas`, `previewDeleteCanvas`,\n `deleteCanvas`); make, change, copy and delete the widgets on them (`createWidget`, `getWidgets`,\n `getWidgetsBatch`, `updateWidget`, `uploadWidgetThumbnail`, `duplicateWidget`, `deleteWidget`);\n list and make the compositions of a widget it built (`getAllCompositions`, `createComposition`), which\n is how it shows the widget with sample values; keep the\n conversation of each widget (`getConversationsByWidgetId`, `createConversationForWidget`,\n `updateConversation`, `getMessages`, `createMessage`, `updateMessage`, `restoreCheckpoint`); and read a\n widget's schemas and give it its repository (`getWidgetContentSchema`, `getWidgetDesignSchema`,\n `updateWidgetRepository`). The AI that builds a widget is part of it: list the models\n (`agentModelsGet`), open the widget's repository (`agentRepositoriesPost`,\n `agentRepositoriesRepoIdGet`), start a generation and follow or cancel it (`agentCodeGeneratePost`,\n `operationsStreamGet`, `operationsIdGet`, `operationsIdCancelPost`), build and preview the result\n (`agentWidgetBuilderRepoHeadWidgetIdGet`, `buildWidgetFromRepo`, `previewWidgetVersion`), publish a\n version of the widget and move between its versions (`agentWidgetBuilderPublishWidgetIdPost`,\n `getWidgetPublishHistory`, `setWidgetPublishHistoryLive`, `renameWidgetPublishHistoryTitle`), and\n turn a stroke or a sentence into a decision (`agentShapeRecognizePost`, `agentDesignResolvePost`). It can\n also offer the customer your presets as starting points: read them and ask for a copy for this customer\n (`listMyPresets`, `copyMyPreset`; you choose the presets with `setPartnerPreset`).\n What the session makes is filed for this managed user, and the lists it reads hold only this user's\n canvases, widgets and compositions. It can reach only canvases, widgets, compositions, repositories,\n uploaded files and operations made for this managed user (tag the canvases and widgets you create\n with `managedUserId`); any other canvas, widget, composition, conversation, message, repository, file\n or operation, including another of your users', answers 404. The creator also saves the composition\n it is editing (`updateComposition`, from the `editor` surface) and attaches pictures to its chat\n (`uploadsPost`, from the `files` surface), and the surfaces do not imply one another: to open the\n whole creator for a user, ask for `creator`, `editor` and `files` together. A generation or a build\n one user starts can be followed and cancelled by that user and by you, never by another of your\n users. The AI is paid for by your account, as your own calls are: its credits and allowances are\n yours. A session cannot make a widget public (publishing a version does not), file one under a\n project or an organization, or set anything the creator does not set: a body with another field is\n refused with 403 and `EDITOR_SESSION_FIELD_NOT_ALLOWED`.\n- `workflows`: use Widgetic's Workflows editor for this user. List their workflows and the compositions\n a workflow of theirs can chain (`listWorkspaceWorkflows`, `listWorkspaceWorkflowCompositionOptions`);\n list the starting points you offer them and make a workflow from one, which gives the user their own copy\n of every widget it chains, a composition of their own on each copy, and a draft\n (`listWorkspaceWorkflowPresets`, `createWorkspaceWorkflowFromPreset`; you offer them with\n `setPartnerWorkflowPreset`);\n make, open, change and delete one (`createWorkspaceWorkflow`, `getWorkspaceWorkflow`,\n `saveWorkspaceWorkflow`, `deleteWorkspaceWorkflow`) and copy it (`duplicateWorkspaceWorkflow`); publish\n it or discard the changes of its draft (`publishWorkspaceWorkflow`,\n `discardWorkspaceWorkflowChanges`); read and restore its versions (`listWorkspaceWorkflowVersions`,\n `getWorkspaceWorkflowVersion`, `restoreWorkspaceWorkflowVersion`); pause, resume, archive and unarchive it\n (`pauseWorkspaceWorkflow`, `resumeWorkspaceWorkflow`, `archiveWorkspaceWorkflow`,\n `unarchiveWorkspaceWorkflow`); and read its counts (`getWorkspaceWorkflowStats`). The workspace in each\n path is `personal`: a session has no other, and a path that names an organization is refused. What the\n session makes is filed for this managed user (`managedUserId`), the lists hold only this user's workflows,\n and it can reach only workflows made for this managed user; any other workflow, including one of yours\n or of another of your users, answers 404. A workflow can chain only the compositions you made for this\n managed user (tag them with `managedUserId`): one that belongs to anybody else is not available, and\n publishing a workflow that uses it is refused. A customer can have at most 50 workflows, and what a\n customer's session reads never says who published a version. A session cannot set who a workflow belongs\n to or which workspace it is in: a body with another field is refused with 403 and\n `EDITOR_SESSION_FIELD_NOT_ALLOWED`.\n\nEverything else is refused with 403 and `EDITOR_SESSION_ROUTE_NOT_ALLOWED`: a session cannot read your\naccount, make more sessions, change your presets or delete a composition, and a session without the `creator` surface cannot\ncreate or list compositions. Create the composition yourself with your API key, then open the editor on it.\n\nA session ends at `expiresAt` (at most four hours), when you end it (`revokeManagedUserEditorSession`),\nor when the managed user is switched off or deleted. A managed user can have at most 100 live sessions at\nonce (`429 EDITOR_SESSION_LIMIT_REACHED`). A session that is bound to a web page (`allowedOrigin`) is\nrefused from any other origin.\n\nThe AI the session runs is paid for with your credits. Set `maxCredits` to hold the session to a number\nof them: once its generations have taken that many (a generation that fails is refunded and does not\ncount), the next one is refused with 402 and `EDITOR_SESSION_CREDIT_LIMIT_REACHED`, and the browser is told\nonly that the feature is not available right now. Without it, the limit Widgetic has set for your account\napplies when it has set one (the same limit for every session you make, and `maxCredits: null` asks for\nnone), and otherwise the credits of your account are the only ceiling. What each of your users' sessions\nhas spent is in `getManagedUserUsage`.\n\nEach call makes a new session. It does not take an `Idempotency-Key`: the answer holds the token, and\nthe token is never stored where a retry could read it back. The answer is sent with\n`Cache-Control: no-store`.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The managed user the session is for (Widgetic's id for the user)","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["surfaces"],"properties":{"surfaces":{"type":"array","minItems":1,"items":{"type":"string","enum":["files","editor","creator","workflows"]},"description":"The pieces of Widgetic's editor the session may drive. `files` is the media library (the File Browser: upload a file, list the files uploaded for this user); what a user uploads is kept in your own storage destination. Partner storage is not optional: a session of any surface can be made only once you have set a destination up (`setPartnerStorageDestination`), and is refused with `409 PARTNER_STORAGE_REQUIRED` until then. `editor` is editing one of this user's compositions: read it, change its name, content, design and embed settings, read its schemas, and ask where to show it (`getCompositionPreview`: the widget's published page). `creator` is Widgetic's widget creator, with its drawing canvas and its AI: this user's canvases, the widgets on them and the conversation of each widget, the generation, build and publish calls that make a widget, and the compositions of those widgets (list, make). The surfaces do not imply one another: the whole creator also saves a composition (`editor`) and attaches pictures to its chat (`files`), so ask for `[\"creator\", \"editor\", \"files\"]`. `workflows` is the Workflows editor: this user's own workflows (list, make, make from one of your starting points, draw, publish, pause, resume, archive, delete, the version history and the counts) under `/workspaces/personal/workflows`, and the compositions made for this user, which are the only ones a workflow of theirs can chain. A workflow is filed under the user it was made for (`managedUserId`), and the session reaches nothing of another user's, nor of yours.\n","example":["files","editor"]},"expiresInSeconds":{"type":"integer","minimum":60,"maximum":14400,"default":3600,"description":"How long the session lasts, in seconds. It cannot be extended; make a new session when it ends.","example":3600},"allowedOrigin":{"type":["string","null"],"description":"Bind the session to one web page origin, such as `https://app.example.com` (no path; `http` is accepted only for `localhost`). The session is then refused from any other origin, and from a request that carries no `Origin` header. Leave it out for a session that can be used from anywhere, which is what a server-side caller needs.\n","example":"https://app.example.com"},"maxCredits":{"type":"integer","minimum":1,"maximum":1000000,"description":"The most credits this session may spend on Widgetic's AI, one credit being one generation. Once the operations it ran have taken that many, the next one is refused (402, `EDITOR_SESSION_CREDIT_LIMIT_REACHED`) and the browser is only told that the feature is not available right now. A generation that fails is refunded and does not count. Leave it out to get the limit Widgetic has set for your account, if it has set one (it sets none unless you ask), and otherwise no limit of its own, which leaves the credits of your account as the only ceiling. Send `null` to ask for no limit of its own even when a default is set.\n","example":20}},"example":{"surfaces":["files","editor"],"expiresInSeconds":3600,"allowedOrigin":"https://app.example.com","maxCredits":20}},"flags":[{"flag":"surfaces","name":"surfaces","type":"string[]","required":true,"description":"The pieces of Widgetic's editor the session may drive. `files` is the media library (the File Browser: upload a file, list the files uploaded for this user); what a user uploads is kept in your own storage destination. Partner storage is not optional: a session of any surface can be made only once you have set a destination up (`setPartnerStorageDestination`), and is refused with `409 PARTNER_STORAGE_REQUIRED` until then. `editor` is editing one of this user's compositions: read it, change its name, content, design and embed settings, read its schemas, and ask where to show it (`getCompositionPreview`: the widget's published page). `creator` is Widgetic's widget creator, with its drawing canvas and its AI: this user's canvases, the widgets on them and the conversation of each widget, the generation, build and publish calls that make a widget, and the compositions of those widgets (list, make). The surfaces do not imply one another: the whole creator also saves a composition (`editor`) and attaches pictures to its chat (`files`), so ask for `[\"creator\", \"editor\", \"files\"]`. `workflows` is the Workflows editor: this user's own workflows (list, make, make from one of your starting points, draw, publish, pause, resume, archive, delete, the version history and the counts) under `/workspaces/personal/workflows`, and the compositions made for this user, which are the only ones a workflow of theirs can chain. A workflow is filed under the user it was made for (`managedUserId`), and the session reaches nothing of another user's, nor of yours.","enum":["files","editor","creator","workflows"],"property":"surfaces"},{"flag":"expires-in-seconds","name":"expiresInSeconds","type":"integer","required":false,"description":"How long the session lasts, in seconds. It cannot be extended; make a new session when it ends.","default":3600,"minimum":60,"maximum":14400,"property":"expiresInSeconds"},{"flag":"allowed-origin","name":"allowedOrigin","type":"string","required":false,"description":"Bind the session to one web page origin, such as `https://app.example.com` (no path; `http` is accepted only for `localhost`). The session is then refused from any other origin, and from a request that carries no `Origin` header. Leave it out for a session that can be used from anywhere, which is what a server-side caller needs.","property":"allowedOrigin"},{"flag":"max-credits","name":"maxCredits","type":"integer","required":false,"description":"The most credits this session may spend on Widgetic's AI, one credit being one generation. Once the operations it ran have taken that many, the next one is refused (402, `EDITOR_SESSION_CREDIT_LIMIT_REACHED`) and the browser is only told that the feature is not available right now. A generation that fails is refunded and does not count. Leave it out to get the limit Widgetic has set for your account, if it has set one (it sets none unless you ask), and otherwise no limit of its own, which leaves the credits of your account as the only ceiling. Send `null` to ask for no limit of its own even when a default is set.","minimum":1,"maximum":1000000,"property":"maxCredits"}]}},{"name":"delete-managed-user","aliases":["delete"],"operationId":"deleteManagedUser","method":"DELETE","path":"/managed-users/{id}","server":"api","summary":"Delete Managed User","description":"Deletes a managed user by ID. Its editor sessions end with it. What was filed under it (compositions, widgets, uploads) stays in your account, untagged.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the managed user to delete","format":"uuid","in":"path"}]},{"name":"get-all-managed-users","aliases":["list"],"operationId":"getAllManagedUsers","method":"GET","path":"/managed-users","server":"api","summary":"Get Managed Users","description":"Retrieve the managed users you manage, with pagination support. (Admins see every partner's managed users.)","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"page","name":"page","type":"integer","required":false,"description":"Page number for pagination","default":1,"minimum":1,"in":"query"},{"flag":"size","name":"size","type":"integer","required":false,"description":"Number of items per page","default":10,"minimum":1,"maximum":100,"in":"query"},{"flag":"sort","name":"sort","type":"string","required":false,"description":"Sort field and direction, `<field>:<asc|desc>`. The field is `createdAt`, `updatedAt` or `externalId` (the snake_case spellings `created_at`, `updated_at` and `external_id` work too); `asc` or `desc` alone sorts by `createdAt`. Any other field is a 400.","default":"createdAt:desc","in":"query"}]},{"name":"get-managed-user-by-id","aliases":["get"],"operationId":"getManagedUserById","method":"GET","path":"/managed-users/{id}","server":"api","summary":"Get Managed User By ID","description":"Retrieves a specific managed user by ID.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the managed user to retrieve","format":"uuid","in":"path"}]},{"name":"get-managed-user-design-defaults","aliases":[],"operationId":"getManagedUserDesignDefaults","method":"GET","path":"/managed-users/{id}/design-defaults","server":"api","summary":"Read design defaults for one managed user","description":"","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-managed-user-limits","aliases":[],"operationId":"getManagedUserLimits","method":"GET","path":"/managed-users/{id}/limits","server":"api","summary":"Read monthly credit cap and usage for one managed user","description":"","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-managed-user-storage-usage","aliases":[],"operationId":"getManagedUserStorageUsage","method":"GET","path":"/managed-users/{id}/storage-usage","server":"api","summary":"Storage used by one managed user's uploads","description":"","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-managed-user-usage","aliases":[],"operationId":"getManagedUserUsage","method":"GET","path":"/managed-users/{id}/usage","server":"api","summary":"What a managed user's editor sessions spent","description":"Says how many credits the editor sessions of one of your managed users took from your account over a\nperiod, in total and for each operation. A generation costs one credit, taken from your account when it\nstarts; one that fails is refunded and is counted as refunded, not as spent.\n\nOnly what was spent through editor sessions is here, and only since usage was first recorded: what you\nspend with your API key, and anything from before, is not. Use it to see which of your users spend the\nmost, or to charge them for it. To hold one session to a number of credits, set `max_credits` when you\nmake it (`createManagedUserEditorSession`).\n\nThe period runs from `startDate` (included) to `endDate` (not included): the 30 days before now when you\ngive neither, and at most 366 days. A date without a time is midnight UTC at the start of that day, so for\nall of October give `startDate=2026-10-01` and `endDate=2026-11-01`.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The managed user whose usage you want (Widgetic's id for the user)","format":"uuid","in":"path"},{"flag":"start-date","name":"startDate","type":"string","required":false,"description":"Start of the period, included: an ISO 8601 date (`2026-10-01`) or date-time with its offset (`2026-10-01T08:00:00Z`). Default: 30 days before `endDate`.","in":"query"},{"flag":"end-date","name":"endDate","type":"string","required":false,"description":"End of the period, not included, in the same forms. Default: now. It must be after `startDate`, by at most 366 days.","in":"query"}]},{"name":"get-managed-users-by-manager-id","aliases":[],"operationId":"getManagedUsersByManagerId","method":"GET","path":"/users/{managerId}/managed-users","server":"api","summary":"Get Managed Users By Manager","description":"Retrieve managed users for a specific manager with pagination support.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"manager-id","name":"managerId","type":"string","required":true,"description":"ID of the manager","format":"uuid","in":"path"},{"flag":"page","name":"page","type":"integer","required":false,"description":"Page number for pagination","default":1,"minimum":1,"in":"query"},{"flag":"size","name":"size","type":"integer","required":false,"description":"Number of items per page","default":10,"minimum":1,"maximum":100,"in":"query"},{"flag":"sort","name":"sort","type":"string","required":false,"description":"Sort field and direction, `<field>:<asc|desc>`. The field is `createdAt`, `updatedAt` or `externalId` (the snake_case spellings `created_at`, `updated_at` and `external_id` work too); `asc` or `desc` alone sorts by `createdAt`. Any other field is a 400.","default":"createdAt:desc","in":"query"}]},{"name":"list-managed-user-entries","aliases":[],"operationId":"listManagedUserEntries","method":"GET","path":"/managed-users/{id}/entries","server":"api","summary":"List entries across a managed user's compositions","description":"Merges eligible tables (time and id columns) across the managed user's compositions in global time order, then compositionId, table, and id (byte order). Only tables with both a time and an id column take part. Rate limit 30 requests per minute.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"composition-id","name":"compositionId","type":"string","required":false,"description":"","format":"uuid","in":"query"},{"flag":"table","name":"table","type":"string","required":false,"description":"","maxLength":63,"in":"query"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"","default":25,"minimum":1,"maximum":100,"in":"query"},{"flag":"order","name":"order","type":"string","required":false,"description":"`desc` (the default) lists the newest entry first, `asc` the oldest. Entries are sorted by their time, then\nby their id (compared as text, byte by byte) in the same direction, so the order of two entries made at the\nsame instant is always the same; an entry with no time counts as the oldest. A table with no time column is\nsorted by its id. The managed-user feed sorts by time, then by composition id, table and id.","enum":["asc","desc"],"default":"desc","in":"query"},{"flag":"since","name":"since","type":"string","required":false,"description":"Only entries made at this time or later: `since` is INCLUSIVE (an entry made at exactly `since` is\nreturned). A date and time with an offset, such as `2026-10-01T00:00:00Z`; anything else answers 400\n`INVALID_SINCE`. An entry with no time never matches, and a table with no time column answers 400\n`ENTRY_TABLE_HAS_NO_TIME`.","format":"date-time","in":"query"},{"flag":"until","name":"until","type":"string","required":false,"description":"Only entries made before this time: `until` is EXCLUSIVE (an entry made at exactly `until` is NOT\nreturned), so `since=A&until=B` followed by `since=B&until=C` counts every entry once. Same form and same\nrefusals as `since` (400 `INVALID_UNTIL`).","format":"date-time","in":"query"},{"flag":"cursor","name":"cursor","type":"string","required":false,"description":"","in":"query"},{"flag":"context","name":"context","type":"pairs","required":false,"description":"Host context filters as `context[key]=value` (max 5 keys, counting dotted `context.key` too). Use `column.<name>=value` for columns whose names collide with reserved parameters.","in":"query"},{"flag":"filter","name":"filter","type":"pairs","required":false,"description":"Column filters as `filter[columnName]=value` (same meaning as bare `columnName=value` or `column.columnName=value`; max 10 column filters counting all spellings). A plain `filter=value` still filters a column named `filter`.","in":"query"}]},{"name":"revoke-managed-user-editor-session","aliases":[],"operationId":"revokeManagedUserEditorSession","method":"DELETE","path":"/managed-users/{id}/editor-sessions/{sessionId}","server":"api","summary":"End one editor session of a managed user","description":"Ends the session at once: its token stops working on the next request (on another server of ours, within\na few seconds). Ending a session that has already ended is fine.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The managed user the session is for","format":"uuid","in":"path"},{"flag":"session-id","name":"sessionId","type":"string","required":true,"description":"The `sessionId` of the session, from `createManagedUserEditorSession`","format":"uuid","in":"path"}]},{"name":"revoke-managed-user-editor-sessions","aliases":[],"operationId":"revokeManagedUserEditorSessions","method":"DELETE","path":"/managed-users/{id}/editor-sessions","server":"api","summary":"End every editor session of a managed user","description":"Ends all of the managed user's live editor sessions at once, for example when the user signs out of your\nplatform. Their tokens stop working on the next request (on another server of ours, within a few seconds).\nIt is fine to call it for a user who has none.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The managed user whose sessions end","format":"uuid","in":"path"}]},{"name":"set-managed-user-design-defaults","aliases":[],"operationId":"setManagedUserDesignDefaults","method":"PUT","path":"/managed-users/{id}/design-defaults","server":"api","summary":"Set or clear design defaults for one managed user","description":"Send `{ \"design\": { ... } }` to store a JSON object (at most 64 KB, nesting depth 32), or `{ \"design\": null }`\nto remove the override. New compositions for this customer merge this layer over the partner default and\nunder the request body.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["design"],"properties":{"design":{"type":["object","null"]}}},"flags":[]}},{"name":"set-managed-user-limits","aliases":[],"operationId":"setManagedUserLimits","method":"PUT","path":"/managed-users/{id}/limits","server":"api","summary":"Set or clear the monthly credit cap for one managed user","description":"Send monthlyCredits as a whole number from 0 up to 2147483647 to cap what this customer's editor sessions may spend in a UTC calendar month, or null to remove the cap. The cap counts the credits the customer's sessions spent that month (refunded credits do not count) and applies to all of the customer's sessions together, beside any `max_credits` a single session was given. A cap of 0 stops all paid work by this customer until you raise it. The cap starts counting from the first of the month (UTC); a cap set below what was already spent this month stops further work at once.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["monthlyCredits"],"properties":{"monthlyCredits":{"type":["integer","null"],"minimum":0,"maximum":2147483647}}},"flags":[{"flag":"monthly-credits","name":"monthlyCredits","type":"integer","required":true,"description":"","minimum":0,"maximum":2147483647,"property":"monthlyCredits"}]}},{"name":"update-managed-user","aliases":["update"],"operationId":"updateManagedUser","method":"PUT","path":"/managed-users/{id}","server":"api","summary":"Update Managed User","description":"Updates an existing managed user. Only `externalId`, `metadata` and `isActive` can change, and `metadata`\nis replaced as a whole, so send all of it. `isActive: false` switches the managed user off: what it\nalready has stays, and nothing new can be filed under it until you send `isActive: true`. Switching it\noff also ends all of its editor sessions, and they do not come back when you switch it on again.\n\nThe `Idempotency-Key` header is required (`400` without it).","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"ID of the managed user to update","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","properties":{"externalId":{"type":"string","description":"Updated external identifier for the managed user. Must be unique among the managed users you manage (409 otherwise).","example":"updated-external-user-123"},"metadata":{"type":"object","description":"Updated additional metadata for the managed user","example":{"firstName":"John","lastName":"Smith","role":"admin"}},"isActive":{"type":"boolean","description":"Switch the managed user off (`false`) or back on (`true`). A managed user that is off keeps everything already filed under it, and that content can still be listed, but nothing new can be filed under it (`409 MANAGED_USER_INACTIVE`). Send a real boolean, not the text \"false\". Switching a managed user off also ends all of its editor sessions.\n","example":false}},"example":{"externalId":"updated-external-user-123","metadata":{"firstName":"John","lastName":"Smith","role":"admin"},"isActive":false}},"flags":[{"flag":"external-id","name":"externalId","type":"string","required":false,"description":"Updated external identifier for the managed user. Must be unique among the managed users you manage (409 otherwise).","property":"externalId"},{"flag":"is-active","name":"isActive","type":"boolean","required":false,"description":"Switch the managed user off (`false`) or back on (`true`). A managed user that is off keeps everything already filed under it, and that content can still be listed, but nothing new can be filed under it (`409 MANAGED_USER_INACTIVE`). Send a real boolean, not the text \"false\". Switching a managed user off also ends all of its editor sessions.","property":"isActive"}]}}]},{"name":"operations","title":"Operations","description":"The Operations operations of the Widgetic API.","hidden":false,"commands":[{"name":"operations-id-get","aliases":["get"],"operationId":"operationsIdGet","method":"GET","path":"/operations/{id}","server":"api","summary":"Get operation status","description":"Follows work that carries on after the response that started it. Duplicating a composition,\nfor one, answers 202 with an `operationId`: ask for that operation until its `status` is\n`completed` (or `failed`, with the reason in `error`). A finished duplication names the new\ncomposition in `metadata.newCompositionId`.\n\nAn operation belongs to the user who started it. Asking for somebody else's operation answers\n404, exactly as for one that does not exist. Administrators may read any operation.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"Operation identifier returned when the job was created.","format":"uuid","in":"path"}]}]},{"name":"partners","title":"Partners","description":"The Partners operations of the Widgetic API.","hidden":false,"commands":[{"name":"confirm-partner-adapter-connection","aliases":[],"operationId":"confirmPartnerAdapterConnection","method":"POST","path":"/partners/{partnerId}/adapter-connections/{adapterId}/{credentialKey}/confirm","server":"api","summary":"Confirm a partner's adapter address with the code that was emailed to it","description":"Some credentials are an email address Widgetic sends a widget's messages to (`confirmation: email_code` in\n`getPartnerAdapterConnections`). Saving one with `setPartnerAdapterConnection` emails a six-digit code to it; this\ngives the code back. Only then is the address used: until it is confirmed, the widgets of the partner's customers\nthat need it are not charged and their visitors are told the feature is unavailable.\n\nThe code works for 30 minutes and for five wrong tries. After that, save the address again and a new code is\nemailed (not sooner than a minute after the last one). A code that is not made of digits is refused without\ncounting as a try.\n\nConfirming an address that is already confirmed answers 200 with the same state, so a retry of a request that\nwent through is harmless. Saving a different address, or the same one before it was confirmed, starts again.\n\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner that keeps the address","format":"uuid","in":"path"},{"flag":"adapter-id","name":"adapterId","type":"string","required":true,"description":"The adapter the address is for","maxLength":40,"in":"path"},{"flag":"credential-key","name":"credentialKey","type":"string","required":true,"description":"The credential's name within the adapter","maxLength":40,"in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["code"],"additionalProperties":false,"properties":{"code":{"type":"string","maxLength":32,"description":"The digits of the code Widgetic emailed to the address, with nothing else. It works for 30 minutes and for five wrong tries; after that, save the address again and a new code is emailed","example":"123456"}},"example":{"code":"123456"}},"flags":[{"flag":"code","name":"code","type":"string","required":true,"description":"The digits of the code Widgetic emailed to the address, with nothing else. It works for 30 minutes and for five wrong tries; after that, save the address again and a new code is emailed","maxLength":32,"property":"code"}]}},{"name":"create-partner-extra-credits-invoice","aliases":[],"operationId":"createPartnerExtraCreditsInvoice","method":"POST","path":"/partners/{partnerId}/credits/extra-invoice","server":"api","summary":"Buy extra credits for a partner","description":"Asks Widgetic for an invoice for extra credits, on top of the credits of the contract. Name the dollars; the\ninvoice is for the most whole credits they buy at the **list price** of a credit (the price a contract may\nlower belongs to the contract), so it is never for more than you asked: 12.34 buys 123 credits and the invoice\nsays 12.30. The smallest purchase is 10 dollars and one request buys at most 100000 (ask Widgetic for an\ninvoice for more).\n\nPay the invoice with the link in the answer. When it is paid the credits are added **once, for good**, to the\naccount of the person who administers the partner, which is the account everything the partner builds is\ncharged to (`getPartnerCreditBalance` shows it). Extra credits **do not expire**, unlike the credits of a\ncontract period, which end with it. Nothing is added before the invoice is paid, and an unpaid invoice for\nextra credits never stops the partner: only an unpaid invoice of the contract does (`payment_arrears`).\n\nAvailable only to partners that have an active paid contract in force with Widgetic (`NO_PAID_CONTRACT` when\nnone is in force) and no overdue contract invoice (`CONTRACT_INVOICE_OVERDUE` while a contract invoice is past\nits due day). A partner that has run out of credits during its period can buy more at the list price.\n\nAn `Idempotency-Key` is required, and it is what makes a retry safe: the same key with the same body returns\nthe invoice already issued, the same key with a different body is refused with 422, and a duplicate that\narrives while the first is still running is refused with 409. Use one key per purchase you mean to make, and\na new key for the next one. At most 10 requests an hour.\n\nWho may ask: the person who administers the partner, or a Widgetic administrator. A key limited to scopes\nneeds `billing:write`.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"Partner ID","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"A key of your own for this purchase, so that a retry cannot make a second invoice","minLength":8,"maxLength":50,"pattern":"^[A-Za-z0-9_-]+$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["usd"],"properties":{"usd":{"type":"number","minimum":10,"maximum":100000,"description":"The dollars to spend on credits, to the cent: at least 10, at most 100000 in one request. The invoice is for the most whole credits that money buys at the list price of a credit, so it is never for more than this (12.34 buys 123 credits and the invoice says 12.30).\n","example":250}},"example":{"usd":250}},"flags":[{"flag":"usd","name":"usd","type":"number","required":true,"description":"The dollars to spend on credits, to the cent: at least 10, at most 100000 in one request. The invoice is for the most whole credits that money buys at the list price of a credit, so it is never for more than this (12.34 buys 123 credits and the invoice says 12.30).","minimum":10,"maximum":100000,"property":"usd"}]}},{"name":"get-my-partners","aliases":[],"operationId":"getMyPartners","method":"GET","path":"/partners/mine","server":"api","summary":"Find the partner you administer","description":"Returns the partner (nearly always one) that the signed-in account administers, with the id every other\npartner route asks for and the standing it is in: `active`, `inactive` (not activated yet), `suspended`\n(paused, with the reason Widgetic gave) or `payment_arrears` (a contract invoice is past its due date and\nunpaid, since `overdueSince`; the partner is stopped until it is paid). A partner API key is that account's\nkey, so it reads its own partner here.\n\nAn account that administers no partner is not refused: it gets `standing: not_a_partner` and an empty\nlist, so a dashboard can tell \"this is not a partner account\" from \"something failed\".\n\nThe call stays open while the partner is paused, not yet active or in payment arrears: that is when its\ndashboard has to say why.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[]},{"name":"get-partner-adapter-connections","aliases":[],"operationId":"getPartnerAdapterConnections","method":"GET","path":"/partners/{partnerId}/adapter-connections","server":"api","summary":"See which adapter keys a partner has connected","description":"Widget adapters are capabilities a widget can use, and some of them act through a service the account that\nmade the widget has with a provider: an adapter that sends email needs an email provider key, one that sends\ntexts needs an SMS provider key. For the widgets of the customers a partner manages, those keys are the\npartner's. This lists every key an adapter takes from a partner and whether the partner has connected it.\n\nAn empty list means no adapter takes a key from a partner at the moment. The list never contains a key: only\nwhether one is connected and when it was last set.\n\nConnect a key with `setPartnerAdapterConnection`; take it away with `removePartnerAdapterConnection`. A managed\ncustomer never holds a key: the widgets of a managed customer use the partner's, and never the personal key\nof the person who administers the partner.\n\nThe caller must be the person who administers the partner (an API key of that account works, with the\n`partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose adapter keys to list","format":"uuid","in":"path"}]},{"name":"get-partner-analytics-by-managed-user","aliases":[],"operationId":"getPartnerAnalyticsByManagedUser","method":"GET","path":"/partners/{partnerId}/analytics/managed-users","server":"api","summary":"What each managed user's widgets did","description":"Lists every one of your managed users with what their widgets and compositions did in a period: views,\ninteractions and visitors, and how many widgets and compositions they have made. A managed user whose widgets\nwere not viewed is listed too, with zeros. The most viewed come first, and `page.hasMore` says whether more\nfollow.\n\nOnly the compositions made for a managed user count for it: file them under the user when you create them\n(`managedUserId` on `createComposition`), or let the user's editor session do it, which files everything under\nits user. What a visitor did with a composition that is not filed under anyone is in the summary of the whole\naccount (`getPartnerAnalyticsSummary`) and in no managed user's row.\n\nThe period works as it does for `getPartnerAnalyticsSummary`. The caller must be the person who administers the\npartner (an API key of that account works, with the `partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose managed users to report on","format":"uuid","in":"path"},{"flag":"start-date","name":"startDate","type":"string","required":false,"description":"Start of the period, included. An ISO 8601 date (`2026-10-01`) or date-time with its offset. Default 30 days before `endDate`.","in":"query"},{"flag":"end-date","name":"endDate","type":"string","required":false,"description":"End of the period, not included, in the same forms. Default now. At most 366 days after `startDate`.","in":"query"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"How many managed users to list at most","default":50,"minimum":1,"maximum":100,"in":"query"},{"flag":"offset","name":"offset","type":"integer","required":false,"description":"How many managed users to skip","default":0,"minimum":0,"maximum":100000,"in":"query"}]},{"name":"get-partner-analytics-summary","aliases":[],"operationId":"getPartnerAnalyticsSummary","method":"GET","path":"/partners/{partnerId}/analytics/summary","server":"api","summary":"What was done with a partner's published widgets","description":"Counts what the visitors of the compositions made through your account did in a period: how many times a\ncomposition was **viewed**, how many times a visitor **interacted** with one, and how many different visitors\nthere were. It covers every composition of your account, your own and the ones made for your managed users;\nnarrow it to one managed user with `managedUserId` or `managedUserExternalId` (not both).\n\nThe numbers come from the events the embedded widgets send while they are shown (the Widgetic embed SDK sends\nthem by itself), counted when you ask. Only views and interactions are counted; the events that describe a\nvisit (its start, its end, the device and the referrer) are not.\n\nThe period runs from `startDate` (included) to `endDate` (not included): the 30 days before now when you give\nneither, and at most 366 days. A date without a time is midnight UTC at the start of that day, so for all of\nOctober give `startDate=2026-10-01` and `endDate=2026-11-01`.\n\nThe caller must be the person who administers the partner (an API key of that account works, with the\n`partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose widgets to report on","format":"uuid","in":"path"},{"flag":"start-date","name":"startDate","type":"string","required":false,"description":"Start of the period, included. An ISO 8601 date (`2026-10-01`) or date-time with its offset (`2026-10-01T08:00:00Z`). Default 30 days before `endDate`.","in":"query"},{"flag":"end-date","name":"endDate","type":"string","required":false,"description":"End of the period, not included, in the same forms. Default now. It must be after `startDate`, by at most 366 days.","in":"query"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"Report only on this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both.","format":"uuid","in":"query"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"Report only on the managed user you registered with this `externalId` (your own identifier for the user). Send this or `managedUserId`, not both.","maxLength":255,"in":"query"}]},{"name":"get-partner-analytics-timeline","aliases":[],"operationId":"getPartnerAnalyticsTimeline","method":"GET","path":"/partners/{partnerId}/analytics/timeline","server":"api","summary":"What was done with a partner's widgets, day by day","description":"Counts views, interactions and visitors for each day of a period (UTC days), oldest first, for a chart. Every\nday of the period is in the list, also one on which nothing happened (all zeros), so the list can be drawn as\nit is. Narrow it to one managed user with `managedUserId` or `managedUserExternalId` (not both).\n\nThe period works as it does for `getPartnerAnalyticsSummary`; `startDate` and `endDate` are best given as dates,\nso that the first and the last day are whole days. The caller must be the person who administers the partner\n(an API key of that account works, with the `partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose widgets to report on","format":"uuid","in":"path"},{"flag":"start-date","name":"startDate","type":"string","required":false,"description":"Start of the period, included. An ISO 8601 date (`2026-10-01`) or date-time with its offset. Default 30 days before `endDate`.","in":"query"},{"flag":"end-date","name":"endDate","type":"string","required":false,"description":"End of the period, not included, in the same forms. Default now. At most 366 days after `startDate`.","in":"query"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"Report only on this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both.","format":"uuid","in":"query"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"Report only on the managed user you registered with this `externalId`. Send this or `managedUserId`, not both.","maxLength":255,"in":"query"}]},{"name":"get-partner-analytics-top-widgets","aliases":[],"operationId":"getPartnerAnalyticsTopWidgets","method":"GET","path":"/partners/{partnerId}/analytics/top-widgets","server":"api","summary":"Which widgets were viewed the most","description":"Lists the widgets whose compositions were viewed the most in a period, with their views, interactions,\nvisitors and how many of their compositions were viewed. A widget that nobody viewed or used is not listed. The\nmost viewed come first (then the most used), and at most `limit` are listed. Narrow it to one managed user with\n`managedUserId` or `managedUserExternalId` (not both).\n\nThe period works as it does for `getPartnerAnalyticsSummary`. The caller must be the person who administers the\npartner (an API key of that account works, with the `partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose widgets to report on","format":"uuid","in":"path"},{"flag":"start-date","name":"startDate","type":"string","required":false,"description":"Start of the period, included. An ISO 8601 date (`2026-10-01`) or date-time with its offset. Default 30 days before `endDate`.","in":"query"},{"flag":"end-date","name":"endDate","type":"string","required":false,"description":"End of the period, not included, in the same forms. Default now. At most 366 days after `startDate`.","in":"query"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"Report only on this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both.","format":"uuid","in":"query"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"Report only on the managed user you registered with this `externalId`. Send this or `managedUserId`, not both.","maxLength":255,"in":"query"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"How many widgets to list at most","default":10,"minimum":1,"maximum":50,"in":"query"}]},{"name":"get-partner-billing-overview","aliases":[],"operationId":"getPartnerBillingOverview","method":"GET","path":"/partners/{partnerId}/billing-overview","server":"api","summary":"Get comprehensive partner billing overview","description":"Retrieves the partner's credits (`operationalCredits`), the partner's contract terms (`partnerCustomization`, the same fields as `getPartnerContract`) and billing integration status. Internal negotiation notes are not part of it.\n`operationalCredits` is the one account a partner has for its credits: the account of the person who administers it. It holds `balance` (the same balance `getPartnerCreditBalance` reads), `reservedBalance`, `availableBalance` (what a new operation can use) and `recentTransactions` (the ten newest entries of the ledger `getPartnerCreditTransactions` lists). A partner with no administrator has no account, and reads 0 and an empty list.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"Partner ID","format":"uuid","in":"path"}]},{"name":"get-partner-contract","aliases":[],"operationId":"getPartnerContract","method":"GET","path":"/partners/{partnerId}/contract","server":"api","summary":"Get a partner's contract","description":"Returns the commercial terms agreed with the partner: price, included credits and storage, billing\ncycle, commitment, support level, and the dates the contract runs between.\n\nA partner reads its own: the caller must be the person who administers the partner (an API key of\nthat account works), and anyone else is refused with 403 whether or not the partner exists. Widgetic\nadministrators can read any partner's. Responds 404 while no contract has been recorded for the\npartner, which is the case until Widgetic sets its terms.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose contract to read","format":"uuid","in":"path"}]},{"name":"get-partner-credit-balance","aliases":[],"operationId":"getPartnerCreditBalance","method":"GET","path":"/partners/{partnerId}/credits/balance","server":"api","summary":"Get partner credit balance","description":"Returns the credits the partner has, and how much of that is reserved by operations that are still running.\nA partner has ONE account for its credits: the account of the person who administers it, the same account\nits API key acts as. There is nothing else to create or to fund. What its customers' editor sessions and its\nown API calls spend comes out of that account, and the credits a paid invoice carries are added to it, so\nthis is the same balance `getUserCreditBalance` reads for that person.\n\nA partner with no administrator has no account to hold credits, and reads a balance of 0.\n\nAccess: the person who administers the partner, or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"Partner ID","format":"uuid","in":"path"}]},{"name":"get-partner-credit-transactions","aliases":[],"operationId":"getPartnerCreditTransactions","method":"GET","path":"/partners/{partnerId}/credits/transactions","server":"api","summary":"Get partner credit transaction history","description":"Lists what happened to the partner's credits, newest first: the entries of the credit ledger of the account of\nthe person who administers it, the one account a partner has for its credits. Every operation that spends\ncredits is a `debit` entry, and the credits an invoice carried arrive as `credit` entries when the invoice is\npaid, so this is also how the partner's usage is read. It is the same ledger `getUserCreditTransactions`\nreads for that person.\n\nA partner with no administrator has no account, and an empty ledger.\n\nThe page is always bounded: `limit` defaults to 20 and is capped at 100, `offset` defaults to 0.\n\nAccess: the person who administers the partner, or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"Partner ID","format":"uuid","in":"path"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"Number of transactions to return","default":20,"minimum":1,"maximum":100,"in":"query"},{"flag":"offset","name":"offset","type":"integer","required":false,"description":"Number of transactions to skip","default":0,"minimum":0,"in":"query"},{"flag":"type","name":"type","type":"string","required":false,"description":"Only entries of this type, for instance `debit` for the credits spent. The types are listed on `AccountCreditTransaction`.","in":"query"}]},{"name":"get-partner-data-database","aliases":[],"operationId":"getPartnerDataDatabase","method":"GET","path":"/partners/{partnerId}/data-database","server":"api","summary":"See the Neon database a partner keeps the data of its compositions in","description":"Returns the database set with `setPartnerDataDatabase`, without its password: the host, the database and the\nrole, when it last passed the check, and why the latest re-check failed if it did. Responds 404 while the\npartner has no database of its own, which means the data of its compositions is kept by Widgetic.\n\nThe caller must be the person who administers the partner (an API key of that account works, with the\n`partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose database to read","format":"uuid","in":"path"}]},{"name":"get-partner-design-defaults","aliases":[],"operationId":"getPartnerDesignDefaults","method":"GET","path":"/partners/{partnerId}/design-defaults","server":"api","summary":"Read partner-wide design defaults","description":"","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-partner-entry-context-signing","aliases":[],"operationId":"getPartnerEntryContextSigning","method":"GET","path":"/partners/{partnerId}/entry-context-signing","server":"api","summary":"See how the context of your embeds is signed","description":"Says whether you have a secret to sign the `context` of your embeds with, when it was made, whether the secret\nit replaced is still accepted, and whether an entry without a valid signature is refused (`required`). The\nsecret itself is never part of the answer: it is shown once, when it is made\n(`rotatePartnerEntryContextSecret`, which also explains what signing is for and how to sign).\n\nSigning covers the entries written through the gateway into the compositions of your managed users and into\nthose of your own account. The caller must be the person who administers the partner (an API key of that\naccount works, with the `partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose context signing to read","format":"uuid","in":"path"}]},{"name":"get-partner-notification-settings","aliases":[],"operationId":"getPartnerNotificationSettings","method":"GET","path":"/partners/{partnerId}/notifications/settings","server":"api","summary":"Where your notifications go","description":"Returns who gets the emails, which events you want by email and by webhook, the address of your webhook, and\nwhether it has a secret to sign with. The secret itself is never returned: it is shown once, when it is made.\n`events` lists every kind of event there is, for a settings screen.\n\nThe caller must be the person who administers the partner (an API key of that account works, with the\n`partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose notification settings to read","format":"uuid","in":"path"}]},{"name":"get-partner-storage-destination","aliases":[],"operationId":"getPartnerStorageDestination","method":"GET","path":"/partners/{partnerId}/storage","server":"api","summary":"Get where a partner's uploaded files are stored","description":"Returns the partner's storage destination without its secret: the endpoint, bucket, folder and public\naddress, the last four characters of the access key id, when the destination last passed the check, and\nwhy the latest re-check failed if it did. Responds 404 while the partner has no destination, which means\nthe partner cannot be activated, its uploads are refused, and no editor session is made for its customers\nuntil a destination is set.\n\nThe caller must be the person who administers the partner, or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose storage destination to read","format":"uuid","in":"path"}]},{"name":"get-partner-storage-usage","aliases":[],"operationId":"getPartnerStorageUsage","method":"GET","path":"/partners/{partnerId}/storage/usage","server":"api","summary":"What a partner's customers store","description":"Says how many files, and how many bytes, your account keeps, in total and for each of your managed users, and\nhow much of it is in Widgetic's storage and how much in your own storage destination\n(`setPartnerStorageDestination`). Use it to see which customers store the most, or to charge them for it.\n\nA file is counted for a managed user when it was filed under that user: by naming `managed_user_id` or\n`managed_user_external_id` when you upload it (`uploadsPost`, or the metadata of a resumable upload), or\nbecause it was uploaded through that user's editor session, which files everything under its user. The files\nthat are not filed under anyone (your own, and the ones uploaded without naming a managed user) are the row\nwhose `managedUserId` is null. Deleted files are not counted. To list the files of one managed user, use the\n`managed_user_id` filter of `uploadsGet`.\n\nThe answer is counted when you ask, so it is always current. The groups are ordered by size, the heaviest\nfirst, and `page.hasMore` says whether more follow.\n\nThe caller must be the person who administers the partner (an API key of that account works, with the\n`partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose storage to report on","format":"uuid","in":"path"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"How many managed users to list at most","default":50,"minimum":1,"maximum":100,"in":"query"},{"flag":"offset","name":"offset","type":"integer","required":false,"description":"How many managed users to skip","default":0,"minimum":0,"maximum":100000,"in":"query"}]},{"name":"list-partner-notification-deliveries","aliases":[],"operationId":"listPartnerNotificationDeliveries","method":"GET","path":"/partners/{partnerId}/notifications/deliveries","server":"api","summary":"How your notifications reached you","description":"Lists the sends of your notifications, the newest first: each email to one address and each call to your\nwebhook, with whether it was delivered, when it is tried again if not, how many attempts it had, and why the\nlast one failed. Use it to find out why a webhook of yours did not receive an event.\n\nAn email counts as delivered when it was handed to the mail service; a call to your webhook counts as\ndelivered when your endpoint answered with a 2xx status. Anything else is tried again, after 1 minute, then\n5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours and a day, eight attempts in all, because your address is\nread again at each attempt and you may have mended it. Two answers end the delivery at once: `410 Gone` and\n`413 Payload Too Large`.\n\nThe caller must be the person who administers the partner (an API key of that account works, with the\n`partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose deliveries to list","format":"uuid","in":"path"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"How many deliveries to list at most","default":100,"minimum":1,"maximum":200,"in":"query"}]},{"name":"list-partner-notifications","aliases":[],"operationId":"listPartnerNotifications","method":"GET","path":"/partners/{partnerId}/notifications","server":"api","summary":"What your partner account was told","description":"Lists the notifications of a partner, the newest first: the invoices Widgetic issued and their payments, the\nreminders to pay them, a warning when your credits run low, and the changes to the standing of the account\n(activated, paused, resumed). It is the same list your dashboard shows, and each entry is what your webhook\nreceives as `data` when you have one (`updatePartnerNotificationSettings`).\n\nA page holds `limit` notifications (20 by default, at most 100). When there are older ones `nextBefore` is\nset: ask again with it as `before` to get the next page.\n\nThe caller must be the person who administers the partner (an API key of that account works, with the\n`partner:read` scope), or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose notifications to list","format":"uuid","in":"path"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"How many notifications to list at most","default":20,"minimum":1,"maximum":100,"in":"query"},{"flag":"before","name":"before","type":"string","required":false,"description":"Only the notifications made before this moment. Give the `nextBefore` of the previous page.","format":"date-time","in":"query"}]},{"name":"list-partner-presets","aliases":[],"operationId":"listPartnerPresets","method":"GET","path":"/partners/{partnerId}/presets","server":"api","summary":"List the widgets a partner offers as presets","description":"Returns the partner's presets, ordered by `position` and then by `key`: the widgets it offers its managed\nusers as starting points. The list is the same for every managed user.\n\nUse a preset in one of two ways:\n- **Make a composition of it** (the content and design editor): call `createComposition` with the preset's\n `widgetId` and the managed user. The widget itself stays yours; a composition is the user's own content.\n- **Give the user a copy to build on** (the widget creator): call `copyPresetForManagedUser`. The user gets\n a widget and a canvas of their own.\n\nPresets are not the same as the widgets Widgetic puts on every new Widgetic account. Those are chosen by\nWidgetic and are copied at sign-up. A preset here is yours, and is copied only when you ask.\n\nThe caller must be the person who administers the partner (an API key of that account works), or a\nWidgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose presets to list","format":"uuid","in":"path"}]},{"name":"list-partner-workflow-presets","aliases":[],"operationId":"listPartnerWorkflowPresets","method":"GET","path":"/partners/{partnerId}/workflow-presets","server":"api","summary":"List the workflows a partner offers as places to start","description":"The partner's starting points, ordered by `position` and then by `key`: the published workflows it offers its managed\nusers when they make a new workflow. Each says whether people can start from it now and, when they cannot, why. The\nlist is the same for every managed user, and a managed user is shown only what is available.\n\nA starting point is one of the partner account's own published workflows, built from compositions the account made\nfor itself (not for a customer or a team) and from widgets the partner already offers as presets\n(`setPartnerPreset`), and it uses at most 10 widgets. A partner offers up to 50.\n\nThe caller must be the person who administers the partner (an API key of that account works), or a Widgetic\nadministrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose starting points to list","format":"uuid","in":"path"}]},{"name":"remove-partner-adapter-connection","aliases":[],"operationId":"removePartnerAdapterConnection","method":"DELETE","path":"/partners/{partnerId}/adapter-connections/{adapterId}/{credentialKey}","server":"api","summary":"Remove an adapter key from a partner","description":"Removes a key an adapter was given. The adapter stops working for the widgets of the partner's customers that\nneed it: their visitors are told the feature is unavailable. Nothing else of the widgets changes, and connecting\na key again brings them back.\n\nRemoving a key that is not connected answers 200 with `removed: false`: the state asked for is the state it is\nin. A key of an adapter that has since been retired from the catalog can still be removed.\n\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner that keeps the key","format":"uuid","in":"path"},{"flag":"adapter-id","name":"adapterId","type":"string","required":true,"description":"The adapter the key is for","maxLength":40,"in":"path"},{"flag":"credential-key","name":"credentialKey","type":"string","required":true,"description":"The key's name within the adapter","maxLength":40,"in":"path"}]},{"name":"remove-partner-data-database","aliases":[],"operationId":"removePartnerDataDatabase","method":"DELETE","path":"/partners/{partnerId}/data-database","server":"api","summary":"Stop keeping the data of a partner's compositions in its own database","description":"Removes the partner's database from Widgetic: Widgetic forgets the connection string, and from then on the data\nof the partner's compositions is kept in Widgetic's database again. Nothing in your database is deleted.\n\nRemoving is refused with 409 `PARTNER_DATABASE_HOLDS_DATA` while any composition of the partner keeps its data in\nyour database: it would read as empty from then on, and what it holds would stay where nothing reads it. Delete\nthe compositions you no longer need, or keep the database. If Widgetic cannot check what your database holds\n(it cannot be reached) the answer is 503 `PARTNER_DATABASE_UNAVAILABLE`; check it with\n`verifyPartnerDataDatabase`, or set the same database again to repair its connection.\n\nRemoving a database the partner does not have is not an error: the answer is `removed: false`.\n\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose database to remove","format":"uuid","in":"path"}]},{"name":"remove-partner-preset","aliases":[],"operationId":"removePartnerPreset","method":"DELETE","path":"/partners/{partnerId}/presets/{key}","server":"api","summary":"Take a widget off the partner's presets","description":"Removes the preset under `key`. The widget itself is not touched, and neither are the copies and the\ncompositions already made from it.\n\nRemoving a preset the partner does not have is not an error: the answer is `removed: false`.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose preset to remove","format":"uuid","in":"path"},{"flag":"key","name":"key","type":"string","required":true,"description":"The key of the preset to remove","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","in":"path"}]},{"name":"remove-partner-storage-destination","aliases":[],"operationId":"removePartnerStorageDestination","method":"DELETE","path":"/partners/{partnerId}/storage","server":"api","summary":"Stop storing a partner's uploaded files in its own bucket","description":"Removes the partner's storage destination. Partner storage is not optional, so after this every upload of\nthe account, on any route, and the uploads of your customers' editor sessions are refused (409\n`PARTNER_STORAGE_REQUIRED`) until a destination is set again, and no editor session can be made. Files\nalready in the partner's bucket are not touched and are not moved: they stay where they are, at the\naddresses they were given.\n\nRemoving a destination the partner does not have is not an error: the answer is `removed: false`.\n\nThe caller must be the person who administers the partner, or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose storage destination to remove","format":"uuid","in":"path"}]},{"name":"remove-partner-workflow-preset","aliases":[],"operationId":"removePartnerWorkflowPreset","method":"DELETE","path":"/partners/{partnerId}/workflow-presets/{key}","server":"api","summary":"Stop offering a workflow as a place to start","description":"Removes the starting point under `key`. The workflow itself is not touched, and neither are the workflows people\nalready started from it.\n\nRemoving a starting point the partner does not offer is not an error: the answer is `removed: false`.","accessLevel":"partner","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose starting point to remove","format":"uuid","in":"path"},{"flag":"key","name":"key","type":"string","required":true,"description":"The key of the starting point to remove","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","in":"path"}]},{"name":"replay-partner-notification-delivery","aliases":[],"operationId":"replayPartnerNotificationDelivery","method":"POST","path":"/partners/{partnerId}/notifications/deliveries/{deliveryId}/replay","server":"api","summary":"Send a webhook delivery again","description":"Sends one earlier **webhook** delivery again to your **current** webhook address, with the same event `id`\n(`webhook-id`) as the original notification so a receiver that remembers ids can ignore a duplicate, and a\nnew `createdAt` and signature made with your current secret. The attempt is recorded in the delivery log as a\nnew row that points at the delivery you replayed.\n\nSet a webhook address (and secret) under notification settings before you replay. Changing the settings needs\nan API key with full access. The caller must be the person who administers the partner, or a Widgetic\nadministrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose delivery to replay","format":"uuid","in":"path"},{"flag":"delivery-id","name":"deliveryId","type":"string","required":true,"description":"The delivery to send again (must be a webhook delivery of this partner)","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}]},{"name":"rotate-partner-entry-context-secret","aliases":[],"operationId":"rotatePartnerEntryContextSecret","method":"POST","path":"/partners/{partnerId}/entry-context-signing/secret","server":"api","summary":"Make a secret to sign the context of your embeds","description":"Makes a secret to sign the `context` of your embeds with, and returns it in `secret`: **keep it now, it is\nshown once** and cannot be read again. Keep it on your server only: whoever holds it can write contexts your\ncompositions accept. Call this again to make a new secret; the one it replaces is still accepted for a day, so\nthat your server can switch without an entry being refused (making another within that day ends the overlap\nwith the first).\n\n**What signing is for.** `<widgetic composition-id=\"...\" context='{\"customerId\":\"c_42\"}'>` hands attribution to\na widget, which sends it with every entry it saves. The page is yours but the browser is the visitor's:\nanybody can change that attribute and file an entry under another customer's id. When your server signs the\ncontext, the gateway refuses an entry whose context was changed, and the entries you keep hold attribution\nyour server wrote.\n\n**How to sign** (on your server, for each page you render):\n\n1. Take the context to give the widget. It may hold text and whole numbers only (within ±2^53, written without\n a fraction or an exponent).\n2. Choose when the signature stops being good: `widgetic_exp`, in seconds since 1970 (UTC), no more than\n seven days from now. A minute of difference between clocks is forgiven.\n3. Write the text to sign, with no spaces except those inside values:\n `widgetic-context.v1.<compositionId>.<widgetic_exp>.<json>`. `<compositionId>` is the id of the composition\n in lower case. `<json>` is the context as JSON with the keys in character-code order (so `Z` comes before\n `a`, and `10` before `9`), no spaces, strings escaped as JSON escapes them, and the two keys of the\n signature left out.\n4. Sign that text: HMAC-SHA256 with the 32 bytes the secret stands for (the base64 text after `ctxsec_`), and\n write the result as base64url without padding.\n5. Give the widget the context with two keys added: `widgetic_exp` and `widgetic_sig` (the signature). The whole\n attribute, the two keys included, stays within the limits of any context: at most 20 keys, 2048 bytes of\n JSON, strings up to 500 characters. The two keys take two of the 20 keys and 87 bytes of the 2048.\n\nA test vector, to check your code against before you rely on it. Secret\n`ctxsec_AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=` (the bytes 0 to 31), composition\n`7c9e6679-7425-40de-944b-e07fc1f90ae7`, `widgetic_exp` 1792000000 and context\n`{\"customerId\":\"c_42\",\"plan\":\"pro\",\"seats\":3}` sign the text\n`widgetic-context.v1.7c9e6679-7425-40de-944b-e07fc1f90ae7.1792000000.{\"customerId\":\"c_42\",\"plan\":\"pro\",\"seats\":3}`\nand give the signature `j6g7151F54tl9C8oEY9levaYSppHME4fAPZ7WPwL8C8`. (The secret is the example's, not a\nreal one.)\n\n**What the gateway does with an entry.** When you have a secret, an entry whose context carries a signature is\nchecked: a signature that does not match is refused with 403 `CONTEXT_SIGNATURE_INVALID`, and one that is past\n`widgetic_exp` with 403 `CONTEXT_SIGNATURE_EXPIRED`. When you also require signatures\n(`updatePartnerEntryContextSigning`) an entry whose context carries none is refused with 403\n`CONTEXT_SIGNATURE_REQUIRED`. The two keys are taken out before the entry is stored or announced: your entries\nand your `entry.created` events hold the context you wrote. A change you make reaches every part of the\ngateway within a minute.\n\n**What it does not do.** A signed context that has been seen (a page's source is public) can be sent again by\nanyone until `widgetic_exp`; when you must not accept the same context twice, put a value that is new for each\npage view in it (a `submissionId` you made up) and keep the ones you have seen. The signature covers the\nentries that reach Widgetic's gateway: a row that gets into a table another way (your own code writing\nstraight into a database you brought, or the record calls of the composition's owner) does not pass\nthrough it, and carries no context.\n\nRetry this call with the same `Idempotency-Key` if its answer was lost: the answer, secret included, is then\ngiven again, and no second secret is made. This needs an API key with full access. The caller must be the\nperson who administers the partner, or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose secret to make","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}]},{"name":"rotate-partner-webhook-secret","aliases":[],"operationId":"rotatePartnerWebhookSecret","method":"POST","path":"/partners/{partnerId}/notifications/webhook-secret","server":"api","summary":"Make a new secret for your webhook","description":"Makes a new secret to sign your webhook with, and returns it in `webhookSecret`: **keep it now, it is shown\nonce** and cannot be read again. Use it when the secret was lost, or may have leaked.\n\nThe secret you had keeps signing beside the new one for a day, so that your receiver can switch without a\ncall being rejected: during that day each call carries two signatures (see `updatePartnerNotificationSettings`\nfor how they are verified). Rotating again within the day ends the overlap with the first secret.\n\nRetry this call with the same `Idempotency-Key` if its answer was lost: the answer, secret included, is then\ngiven again, and no second secret is made.\n\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose webhook secret to replace","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}]},{"name":"send-partner-notification-test-event","aliases":[],"operationId":"sendPartnerNotificationTestEvent","method":"POST","path":"/partners/{partnerId}/notifications/test-event","server":"api","summary":"Send a test event to your webhook","description":"Sends ONE signed call, right now, to the webhook address you saved under notification settings, with a sample\nof what an event of the type you name carries, and answers how it went: whether your receiver answered with a\n2xx status, which status, how long it took, and why not when it did not arrive. Use it to write and try your\nreceiver (above all the signature check) before a real event happens.\n\nThe call is made like every other: it is signed with your current secret (and the previous one while a\nrotation overlaps), it carries the headers `webhook-id`, `webhook-timestamp`, `webhook-signature` and\n`x-widgetic-event` (the event type), and its body is `{ \"id\", \"type\", \"createdAt\", \"partnerId\", \"data\" }`.\nThree things differ. `id` starts with `evt_test_` and is new every time, so a receiver that remembers ids can\ntell a test from an event and drop it. `data` holds made-up values of the shape a real event of that type\ncarries (`PartnerNotification` describes each), never anything about your account. And the call is made once:\nit is not tried again, nothing is stored, and it appears neither in the delivery log nor in the notification\nlist.\n\nIt is sent whatever `webhookEvents` says: a test is not filtered by what you subscribed to. The answer is 200\nwhether or not your receiver took the call (read `delivered`); a call that gets no answer within ten seconds\nis given up and reported as a `timeout`.\n\nA webhook address and a signing secret must be saved first (`updatePartnerNotificationSettings`); until then\nthe answer is 409. This needs an API key with full access, and `Idempotency-Key` is required: a retry with the\nsame key gets the first answer back and no second call is made. The caller must be the person who\nadministers the partner, or a Widgetic administrator. Five tests a minute.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose webhook to try","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","description":"Which kind of event to send a sample of.","required":["eventType"],"properties":{"eventType":{"$ref":"#/$defs/PartnerNotificationEventType"}},"example":{"eventType":null},"$defs":{"PartnerNotificationEventType":{"type":"string","description":"What happened. The event types you can be told about.","enum":["invoice.issued","invoice.due_soon","invoice.overdue","invoice.paid","invoice.failed","credits.low","credits.managed_user_cap_reached","partner.activated","partner.paused","partner.resumed","storage.check_failed","composition.created","composition.updated","widget.published","upload.created","entry.created","workflow.created","workflow.published","workflow.state_changed","workflow.deleted"]}}},"flags":[{"flag":"event-type","name":"eventType","type":"string","required":true,"description":"What happened. The event types you can be told about.","enum":["invoice.issued","invoice.due_soon","invoice.overdue","invoice.paid","invoice.failed","credits.low","credits.managed_user_cap_reached","partner.activated","partner.paused","partner.resumed","storage.check_failed","composition.created","composition.updated","widget.published","upload.created","entry.created","workflow.created","workflow.published","workflow.state_changed","workflow.deleted"],"property":"eventType"}]}},{"name":"set-partner-adapter-connection","aliases":[],"operationId":"setPartnerAdapterConnection","method":"PUT","path":"/partners/{partnerId}/adapter-connections/{adapterId}/{credentialKey}","server":"api","summary":"Connect an adapter key to a partner","description":"Gives an adapter the key it needs to act through a service the partner has an account with, for the widgets of\nthe customers the partner manages. Take the `adapterId` and `credentialKey` from\n`getPartnerAdapterConnections`. Saving a key that is already connected replaces it.\n\n**What Widgetic does with it.** The key is stored encrypted, bound to this partner, this adapter and this\ncredential, so that a stored value cannot be moved to another. It is never returned, never logged and never\nshown to a visitor of a widget or to your customers. A server of Widgetic opens it for the one call a widget\nmakes through the adapter, uses it to call the provider on the partner's behalf, and discards it. To cut that\noff, remove the key here, or revoke it with the provider.\n\n**What is checked.** That the adapter exists and takes this credential, that a partner may keep it (some are\nfor an owner only) and that the text is one line of at most 2048 characters. Whether the provider accepts the\nkey is not checked here: it shows the first time a widget uses it.\n\n**An address that has to be confirmed.** Some credentials are not a key but the email address Widgetic sends a\nwidget's messages to (`confirmation: email_code` in `getPartnerAdapterConnections`). Saving one checks that it is\none email address and emails a six-digit code to it; the answer says `awaitingConfirmation: true`, and nothing is\nsent to the address until the code is given back with `confirmPartnerAdapterConnection`. Saving the same address\nagain before it is confirmed emails a new code (not sooner than a minute after the last); saving the address that\nis already confirmed changes nothing. If the code cannot be emailed, nothing is saved.\n\nA widget whose adapter has no key connected, or whose address is not confirmed yet, is not charged and does not\nrun that adapter: its visitors are told the feature is unavailable, and the partner is told once a day what to do.\n\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator. A retry is harmless: saving the same key twice only refreshes when it was set.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner that keeps the key","format":"uuid","in":"path"},{"flag":"adapter-id","name":"adapterId","type":"string","required":true,"description":"The adapter the key is for","maxLength":40,"in":"path"},{"flag":"credential-key","name":"credentialKey","type":"string","required":true,"description":"The key's name within the adapter","maxLength":40,"in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["credential"],"additionalProperties":false,"properties":{"credential":{"type":"string","format":"password","writeOnly":true,"maxLength":2048,"description":"The key exactly as its provider gave it: one line of text, at most 2048 characters. It is stored encrypted and is never returned or logged. For a credential that is an email address (`confirmation: email_code`), write that one address, with nothing else","example":"YOUR_PROVIDER_KEY"}},"example":{"credential":"YOUR_PROVIDER_KEY"}},"flags":[{"flag":"credential","name":"credential","type":"string","required":true,"description":"The key exactly as its provider gave it: one line of text, at most 2048 characters. It is stored encrypted and is never returned or logged. For a credential that is an email address (`confirmation: email_code`), write that one address, with nothing else","maxLength":2048,"format":"password","secret":true,"property":"credential"}]}},{"name":"set-partner-data-database","aliases":[],"operationId":"setPartnerDataDatabase","method":"PUT","path":"/partners/{partnerId}/data-database","server":"api","summary":"Keep the data of a partner's compositions in a Neon database of its own","description":"Points the partner at a Neon database of its own. From then on, what visitors leave in the compositions of\nthe partner's managed users, and of the compositions made in the partner's own account (the votes, ratings,\nanswers and form submissions of the widgets that collect them), is stored in that database and nowhere else.\nWidgetic's own database is not used for them any more, and nothing of it is copied there.\n\n**What to give.** The connection string of a Neon database and a role made for this, as Neon shows it under\n*Connect* (`postgresql://<role>:<password>@ep-<name>.<region>.aws.neon.tech/<database>`). Only Neon databases\nare accepted; a string for any other host is refused with 400. What the database needs:\n- an empty database of its own, so that nothing else of yours shares it. Widgetic makes one schema in it for\n each composition (named `comp_` followed by the composition's id). In that schema go the tables the widget\n declares for what its visitors leave, and two small tables of Widgetic's own, whose names start with\n `_widgetic_`: the context a host page sent with an entry, and how far Widgetic has read the entries to tell\n you about new ones. It touches nothing else;\n- a role that may create schemas and tables in it (the CREATE privilege on the database). The role Neon makes\n with a project has it. A role made in the Neon console can also read and write every other database of the\n same project, so if you keep other data in Neon give Widgetic a project of its own, or make the role with\n SQL (`CREATE ROLE ... LOGIN PASSWORD ...`) and grant it only the CREATE privilege on this database;\n- connections from anywhere: Widgetic's addresses are not fixed, so a Neon project that only accepts\n connections from an allow list of addresses cannot be used. A suspended compute is fine: it wakes on the\n first request.\n\n**It is checked before it is saved.** Widgetic makes a schema in your database, creates a table in it, writes\na row, reads it back, and removes the schema again, all in one step, so that nothing is left if anything\nfails. If it cannot be done nothing is saved and the answer is 422 `PARTNER_DATABASE_UNVERIFIED`:\n`details.reason` says why (`INVALID_CREDENTIALS`, `DATABASE_NOT_FOUND`, `ACCESS_DENIED`, `ACCESS_BLOCKED`,\n`ENDPOINT_NOT_FOUND`, `ENDPOINT_UNREACHABLE`, `TIMEOUT`, `READ_ONLY`, `RATE_LIMITED`, `DATABASE_UNAVAILABLE`,\n`PROBE_MISMATCH` or `DATABASE_ERROR`) and `message` says what to do.\n\n**Existing data does not move.** The data of a composition lives in one database. If any composition of the\npartner already keeps data somewhere else (in Widgetic's database, or in the database you had set), and the\nnew database does not hold it, the change is refused with 409 `PARTNER_DATABASE_HOLDS_DATA` and nothing is\nchanged: those compositions would read as empty from then on. `details.compositionsWithData` says how many.\nSet your database before your customers collect data, or delete the compositions you no longer need. Saving\nthe same database again (a new password) moves nothing and is never refused.\n\n**It is never a fallback.** Once a database is set, a request that needs it while it cannot be used (the\npassword was changed, the project was deleted) is refused with 503 `PARTNER_DATABASE_UNAVAILABLE`. What the\nvisitor sent is not stored with Widgetic instead. Fix the connection and set the database again, or check it\nwith `verifyPartnerDataDatabase`.\n\n**When it takes effect.** A change reaches every part of Widgetic within about a minute.\n\n**What Widgetic keeps.** The connection string is stored encrypted and is never returned or logged; a response\nshows the host, the database and the role, never the password. Widgetic's servers open it to read and write the\ndata of your compositions while they handle a request for it. To cut that off, change the role's password in\nNeon: the compositions stop saving until you set the new string.\n\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator. A retry is harmless: saving the same database twice only refreshes when it was\nchecked and saved.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose database to set","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["connectionString"],"properties":{"connectionString":{"type":"string","format":"password","writeOnly":true,"description":"The connection string of a Neon database and role you made for this, as Neon shows it under Connect: `postgresql://<role>:<password>@ep-<name>.<region>.aws.neon.tech/<database>`. Either the direct or the pooled host works. Everything after the database name (`?sslmode=...`) is ignored. It is stored encrypted and is never returned or logged.","example":"postgresql://widgetic_data:YOUR_PASSWORD@ep-cool-name-123456.eu-central-1.aws.neon.tech/neondb"}},"example":{"connectionString":"postgresql://widgetic_data:YOUR_PASSWORD@ep-cool-name-123456.eu-central-1.aws.neon.tech/neondb"}},"flags":[{"flag":"connection-string","name":"connectionString","type":"string","required":true,"description":"The connection string of a Neon database and role you made for this, as Neon shows it under Connect: `postgresql://<role>:<password>@ep-<name>.<region>.aws.neon.tech/<database>`. Either the direct or the pooled host works. Everything after the database name (`?sslmode=...`) is ignored. It is stored encrypted and is never returned or logged.","format":"password","secret":true,"property":"connectionString"}]}},{"name":"set-partner-design-defaults","aliases":[],"operationId":"setPartnerDesignDefaults","method":"PUT","path":"/partners/{partnerId}/design-defaults","server":"api","summary":"Set or clear partner-wide design defaults","description":"Send `{ \"design\": { ... } }` or `{ \"design\": null }` to remove. New compositions merge this layer over the\nwidget base and under each managed user's default and the request body.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["design"],"properties":{"design":{"type":["object","null"]}}},"flags":[]}},{"name":"set-partner-preset","aliases":[],"operationId":"setPartnerPreset","method":"PUT","path":"/partners/{partnerId}/presets/{key}","server":"api","summary":"Make a widget a preset, or replace a preset","description":"Offers one of the partner's own widgets as a preset under `key`. If the partner already has a preset under\nthat key it is replaced, so sending the same request twice is safe. Saving replaces the whole preset: a\n`title`, `description` or `position` you leave out goes back to its default.\n\nKeys are 1 to 64 characters: lowercase letters, digits, hyphens and underscores, the first a letter or a\ndigit (`rating`, `quiz-3`).\n\nThe widget must belong to the partner's account; a widget that does not exist and one that belongs to\nsomebody else are refused in the same way (404 `PRESET_WIDGET_NOT_FOUND`). A widget is a preset under at\nmost one key (409 `PRESET_WIDGET_ALREADY_USED`; `details.key` says which), and an account can have at\nmost 100 presets (409 `PRESET_LIMIT_REACHED`).\n\nLater changes to the widget reach new copies and new compositions; copies made earlier are not touched.\nDeleting the widget removes its preset.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose preset to set","format":"uuid","in":"path"},{"flag":"key","name":"key","type":"string","required":true,"description":"The name your code uses for the preset","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","additionalProperties":false,"required":["widgetId"],"properties":{"widgetId":{"type":"string","format":"uuid","description":"One of the partner's own widgets. A widget is a preset under at most one key."},"title":{"type":["string","null"],"minLength":1,"maxLength":120,"description":"What your picker shows. Leave it out to show the widget's own name."},"description":{"type":["string","null"],"maxLength":500},"position":{"type":"integer","minimum":0,"maximum":10000,"default":0,"description":"Where the preset sits in the list. Smallest first; presets with the same position are ordered by key."}},"example":{"widgetId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","title":"Example title","description":"Example description","position":42}},"flags":[{"flag":"widget-id","name":"widgetId","type":"string","required":true,"description":"One of the partner's own widgets. A widget is a preset under at most one key.","format":"uuid","property":"widgetId"},{"flag":"title","name":"title","type":"string","required":false,"description":"What your picker shows. Leave it out to show the widget's own name.","minLength":1,"maxLength":120,"property":"title"},{"flag":"description","name":"description","type":"string","required":false,"description":"","maxLength":500,"property":"description"},{"flag":"position","name":"position","type":"integer","required":false,"description":"Where the preset sits in the list. Smallest first; presets with the same position are ordered by key.","default":0,"minimum":0,"maximum":10000,"property":"position"}]}},{"name":"set-partner-storage-destination","aliases":[],"operationId":"setPartnerStorageDestination","method":"PUT","path":"/partners/{partnerId}/storage","server":"api","summary":"Set where a partner's uploaded files are stored","description":"Points the partner at a bucket of its own on an S3-compatible service (Cloudflare R2, Amazon S3,\nWasabi, MinIO and the like). From then on, every file the account that administers the\npartner stores is kept in that bucket instead of with Widgetic, whichever way it arrives: a file sent with\n`uploadsPost` (with an API key, an SDK token, an editor session or from the Widgetic website), a resumable\nupload, the media library, and the files the agent tools and the MCP server save. It takes none of the\naccount's storage allowance, and its `url` is served from `publicBaseUrl`. The chunks of a resumable\nupload wait in Widgetic's temporary staging area until it is completed and are removed then: the finished\nfile is stored in your bucket and nowhere else.\n\nA destination is required. Partner storage is not optional: your customers' files, and your own however they\narrive, are kept in this destination and nowhere else. An editor session\n(`createManagedUserEditorSession`) of any surface cannot be made until a destination is set (409\n`PARTNER_STORAGE_REQUIRED`), a destination removed later stops the uploads of the sessions already made\n(the same 409), and your own upload with no destination (on any route, including a resumable upload, which\nis refused when it is started) is refused in the same way instead of being stored with Widgetic. A partner\ncannot be activated until it has a destination that works.\n\nThe destination is checked before it is saved. Widgetic writes a small test file under\n`<keyPrefix>widgetic-check/`, reads it back from `<publicBaseUrl>/<its key>`, and deletes it. If any\nstep fails nothing is saved and the answer is 422 `STORAGE_DESTINATION_UNVERIFIED`: `details.step` says\nwhere (`endpoint`, `write` or `public_read`), `details.reason` says why, and `param` names the field most\nlikely to be wrong.\n\nWhat the bucket needs:\n- an access key that may put objects into it (it may also delete objects, which lets the test file be\n cleaned up, but that is not required);\n- its files publicly readable at `publicBaseUrl`, either because the bucket is public or through a CDN.\n\nThe secret access key is stored encrypted and is never returned or logged; a response shows only the last\nfour characters of the access key id (`accessKeyHint`). Saving replaces the whole destination, so send\nevery field again, including the secret. Files already stored stay where they are, and keep working for\nas long as that bucket stays publicly readable.\n\nOnce a destination is set, an upload is never stored with Widgetic instead: when the destination cannot be\nused the upload is refused with 503 `STORAGE_DESTINATION_UNAVAILABLE`, and when the bucket rejects the\nfile with 502 `STORAGE_DESTINATION_WRITE_FAILED`.\n\nThe caller must be the person who administers the partner (an API key of that account works); anyone else\nis refused with 403 whether or not the partner exists. Widgetic administrators can set any partner's.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose storage destination to set","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","description":"The bucket to keep the partner's files in, and the key that may write to it. The field names are camelCase, like every answer of the API; the snake_case spelling (`keyPrefix`) is read too.\n","additionalProperties":false,"required":["endpoint","region","bucket","publicBaseUrl","accessKeyId","secretAccessKey"],"properties":{"endpoint":{"type":"string","description":"The HTTPS address of the S3-compatible service, scheme and host only (no path, port or credentials). It must be a host name, not an IP address, and must resolve to the public internet.\n","example":"https://abc123.r2.cloudflarestorage.com"},"region":{"type":"string","description":"The region the service signs requests for. Use auto where the service says so (such as Cloudflare R2).","example":"auto"},"bucket":{"type":"string","example":"acme-widget-media"},"keyPrefix":{"type":"string","default":"","description":"A folder inside the bucket to write every file under, for example widgetic/uploads. Folders are made of letters, digits, dots, hyphens and underscores. Leave it out to write at the root of the bucket.\n","example":"widgetic/"},"publicBaseUrl":{"type":"string","description":"Where the stored files are served from: the bucket's public address, or a CDN in front of it. The address of a file is this value, a slash and the file's key in the bucket, which starts with the key prefix. A path is allowed (https://media.example.com/files/acme-widget-media).\n","example":"https://media.example.com"},"forcePathStyle":{"type":"boolean","default":true,"description":"Put the bucket name in the path of a request. Every S3-compatible service supports this; set it to false only for a service that needs the bucket name in the host name.\n"},"accessKeyId":{"type":"string","description":"The id of an access key that may put objects into the bucket"},"secretAccessKey":{"type":"string","format":"password","writeOnly":true,"description":"The secret of that access key. It is stored encrypted and is never returned."}},"example":{"endpoint":"https://abc123.r2.cloudflarestorage.com","region":"auto","bucket":"acme-widget-media","keyPrefix":"widgetic/","publicBaseUrl":"https://media.example.com","forcePathStyle":true,"accessKeyId":"Example accessKeyId","secretAccessKey":"Example secretAccessKey"}},"flags":[{"flag":"endpoint","name":"endpoint","type":"string","required":true,"description":"The HTTPS address of the S3-compatible service, scheme and host only (no path, port or credentials). It must be a host name, not an IP address, and must resolve to the public internet.","property":"endpoint"},{"flag":"region","name":"region","type":"string","required":true,"description":"The region the service signs requests for. Use auto where the service says so (such as Cloudflare R2).","property":"region"},{"flag":"bucket","name":"bucket","type":"string","required":true,"description":"","property":"bucket"},{"flag":"key-prefix","name":"keyPrefix","type":"string","required":false,"description":"A folder inside the bucket to write every file under, for example widgetic/uploads. Folders are made of letters, digits, dots, hyphens and underscores. Leave it out to write at the root of the bucket.","default":"","property":"keyPrefix"},{"flag":"public-base-url","name":"publicBaseUrl","type":"string","required":true,"description":"Where the stored files are served from: the bucket's public address, or a CDN in front of it. The address of a file is this value, a slash and the file's key in the bucket, which starts with the key prefix. A path is allowed (https://media.example.com/files/acme-widget-media).","property":"publicBaseUrl"},{"flag":"force-path-style","name":"forcePathStyle","type":"boolean","required":false,"description":"Put the bucket name in the path of a request. Every S3-compatible service supports this; set it to false only for a service that needs the bucket name in the host name.","default":true,"property":"forcePathStyle"},{"flag":"access-key-id","name":"accessKeyId","type":"string","required":true,"description":"The id of an access key that may put objects into the bucket","property":"accessKeyId"},{"flag":"secret-access-key","name":"secretAccessKey","type":"string","required":true,"description":"The secret of that access key. It is stored encrypted and is never returned.","format":"password","secret":true,"property":"secretAccessKey"}]}},{"name":"set-partner-workflow-preset","aliases":[],"operationId":"setPartnerWorkflowPreset","method":"PUT","path":"/partners/{partnerId}/workflow-presets/{key}","server":"api","summary":"Offer a workflow as a place to start, or replace what a key offers","description":"Offers one of the partner account's own published workflows to its managed users under `key`. If the partner already\noffers something under that key it is replaced, so sending the same request twice is safe. Saving replaces the whole\nstarting point: a `title`, `description` or `position` you leave out goes back to its default.\n\nKeys are 1 to 64 characters: lowercase letters, digits, hyphens and underscores, the first a letter or a digit.\n\nA workflow is offered once. What people get is the workflow's live version as it is when they start, so publishing\nthe workflow changes what new starts get; workflows people already started are never touched. The offer is\nrefused, and `error.details.reason` says why, when the workflow:\n\n- is not the partner account's own (a workflow of a team or of a customer, or of somebody else), or does not exist:\n 404, the same for both;\n- is archived (409 `workflow_archived`), or was never published (409 `workflow_not_published`);\n- shows no composition (422 `workflow_empty`), or a block shows a composition made for a customer or a team\n (422 `composition_not_own`);\n- chains more than 10 widgets (422 `too_many_widgets`), or a widget that the partner does not offer as a preset\n (422 `widget_not_preset`; `error.details.widgets` lists them, each with its `id` and `name`, so they can be offered\n first with `setPartnerPreset`);\n- is already offered under another key (409 `workflow_already_offered`; `error.details.key` says which), or the\n partner already offers 50 (409 `too_many_presets`).\n\nThe caller must be the person who administers the partner (an API key of that account works), or a Widgetic\nadministrator, who offers the partner's workflow on its behalf.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose starting point to set","format":"uuid","in":"path"},{"flag":"key","name":"key","type":"string","required":true,"description":"The name your code and the picker use for the starting point","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","additionalProperties":false,"required":["workflowId"],"properties":{"workflowId":{"type":"string","format":"uuid","description":"One of your own published workflows."},"title":{"type":["string","null"],"minLength":1,"maxLength":120,"description":"What the picker shows. Leave it out to show the workflow's name."},"description":{"type":["string","null"],"maxLength":500},"position":{"type":"integer","minimum":0,"maximum":10000,"default":0,"description":"Where it sits in the list. Smallest first; the same position is ordered by key."}},"example":{"workflowId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","title":"Example title","description":"Example description","position":42}},"flags":[{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"One of your own published workflows.","format":"uuid","property":"workflowId"},{"flag":"title","name":"title","type":"string","required":false,"description":"What the picker shows. Leave it out to show the workflow's name.","minLength":1,"maxLength":120,"property":"title"},{"flag":"description","name":"description","type":"string","required":false,"description":"","maxLength":500,"property":"description"},{"flag":"position","name":"position","type":"integer","required":false,"description":"Where it sits in the list. Smallest first; the same position is ordered by key.","default":0,"minimum":0,"maximum":10000,"property":"position"}]}},{"name":"update-partner-entry-context-signing","aliases":[],"operationId":"updatePartnerEntryContextSigning","method":"PUT","path":"/partners/{partnerId}/entry-context-signing","server":"api","summary":"Require a signed context, or stop requiring it","description":"Chooses what the gateway does with an entry whose `context` carries no valid signature.\n\nWith `required: false` (the default) an entry that carries a signature is checked and refused when the\nsignature is wrong or out of date, and an entry that carries none is accepted, as it always was. With\n`required: true` an entry without a valid signature is refused with 403 `CONTEXT_SIGNATURE_REQUIRED`: use it\nonce every page of yours that embeds a composition signs its context.\n\nSignatures cannot be required before you have made a secret (`rotatePartnerEntryContextSecret`): the answer\nis 409, so that you cannot lock your own compositions out of taking entries. The change reaches every part of\nthe gateway within a minute.\n\nSaving the same choice twice leaves the same state, so a retry is harmless and `Idempotency-Key` is optional.\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose context signing to change","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":false,"description":"Optional: without it the call is carried out every time it is sent. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["required"],"properties":{"required":{"type":"boolean","description":"True to refuse every entry whose context carries no valid signature. False to check the signature of the entries that carry one and accept the others, as before."}},"example":{"required":true}},"flags":[{"flag":"required","name":"required","type":"boolean","required":true,"description":"True to refuse every entry whose context carries no valid signature. False to check the signature of the entries that carry one and accept the others, as before.","property":"required"}]}},{"name":"update-partner-notification-settings","aliases":[],"operationId":"updatePartnerNotificationSettings","method":"PUT","path":"/partners/{partnerId}/notifications/settings","server":"api","summary":"Choose where your notifications go","description":"Changes who gets the emails, which events you want by email and by webhook, and the address of your webhook.\nA key you leave out of the body is left as it was; send only what changes.\n\n**Email.** `emailRecipients` is the list of addresses (at most five). While it is empty the emails about\ninvoices go to the billing emails of your billing profile (your contact email when there are none), and the\nothers to your contact email. The events about money and about the standing of your account are always\nemailed, whatever `emailEvents` says; it narrows the others.\n\n**Webhook.** `webhookUrl` is where Widgetic sends a signed `POST` for each event, so that your own system\ncan act on it (mark an invoice paid, warn your team that credits are low). The address must be `https` on the\nstandard port, on a public site, and must not redirect. Setting an address when you have no secret yet makes\none, and the answer carries it as `webhookSecret`: **keep it now, it is shown once**. `webhookEvents` narrows\nwhat is sent.\n\nEach call has a JSON body, `{ \"id\": \"evt_...\", \"type\": \"invoice.paid\", \"createdAt\": \"...\", \"partnerId\": \"...\",\n\"data\": { ... } }` (`data` is the `data` of `PartnerNotification`), and these headers:\n`webhook-id` (the same as `id`, and the same on every retry), `webhook-timestamp` (seconds since 1970) and\n`webhook-signature` (`v1,` and a base64 HMAC-SHA256). It is the Standard Webhooks scheme, so any of its\nlibraries verifies it. By hand: take your secret without the `whsec_` prefix, decode it from base64, and sign\nthe text `<webhook-id>.<webhook-timestamp>.<the raw body>` with it; the header holds `v1,<base64 of the\nresult>` (two signatures separated by a space while a rotated secret overlaps). Reject a `webhook-timestamp`\nthat is more than five minutes old.\n\nAnswer with a 2xx status within ten seconds. Anything else is tried again, eight attempts in all over about\ntwo days (`listPartnerNotificationDeliveries` shows each). Delivery is at least once: if your answer was lost\nthe same event arrives again with the same `id`, so drop what you have already handled by it.\n\nChanging the settings needs an API key with full access: they decide where your notifications go.\nThe caller must be the person who administers the partner, or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose notification settings to change","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":false,"description":"Optional: without it the call is carried out every time it is sent. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","description":"What to change. A key you leave out is left as it was.","properties":{"emailRecipients":{"type":"array","maxItems":5,"description":"The addresses the emails go to, at most five. An empty list goes back to the defaults.","items":{"type":"string"}},"emailEvents":{"type":["array","null"],"description":"The events you want by email. Null for every event.","items":{"$ref":"#/$defs/PartnerNotificationEventType"}},"webhookEvents":{"type":["array","null"],"description":"The events to send to your webhook. Null for every event.","items":{"$ref":"#/$defs/PartnerNotificationEventType"}},"webhookUrl":{"type":["string","null"],"maxLength":2000,"description":"Where to send the signed calls. It must be an https address on the standard port, on a public site, and it must not redirect. Null or an empty text switches the webhook off. Setting an address when you have no secret yet makes one."}},"example":{"emailRecipients":[],"emailEvents":["Example PartnerNotificationEventType"],"webhookEvents":["Example PartnerNotificationEventType"],"webhookUrl":"Example webhookUrl"},"$defs":{"PartnerNotificationEventType":{"type":"string","description":"What happened. The event types you can be told about.","enum":["invoice.issued","invoice.due_soon","invoice.overdue","invoice.paid","invoice.failed","credits.low","credits.managed_user_cap_reached","partner.activated","partner.paused","partner.resumed","storage.check_failed","composition.created","composition.updated","widget.published","upload.created","entry.created","workflow.created","workflow.published","workflow.state_changed","workflow.deleted"]}}},"flags":[{"flag":"email-recipients","name":"emailRecipients","type":"string[]","required":false,"description":"The addresses the emails go to, at most five. An empty list goes back to the defaults.","property":"emailRecipients"},{"flag":"email-events","name":"emailEvents","type":"string[]","required":false,"description":"The events you want by email. Null for every event.","enum":["invoice.issued","invoice.due_soon","invoice.overdue","invoice.paid","invoice.failed","credits.low","credits.managed_user_cap_reached","partner.activated","partner.paused","partner.resumed","storage.check_failed","composition.created","composition.updated","widget.published","upload.created","entry.created","workflow.created","workflow.published","workflow.state_changed","workflow.deleted"],"property":"emailEvents"},{"flag":"webhook-events","name":"webhookEvents","type":"string[]","required":false,"description":"The events to send to your webhook. Null for every event.","enum":["invoice.issued","invoice.due_soon","invoice.overdue","invoice.paid","invoice.failed","credits.low","credits.managed_user_cap_reached","partner.activated","partner.paused","partner.resumed","storage.check_failed","composition.created","composition.updated","widget.published","upload.created","entry.created","workflow.created","workflow.published","workflow.state_changed","workflow.deleted"],"property":"webhookEvents"},{"flag":"webhook-url","name":"webhookUrl","type":"string","required":false,"description":"Where to send the signed calls. It must be an https address on the standard port, on a public site, and it must not redirect. Null or an empty text switches the webhook off. Setting an address when you have no secret yet makes one.","maxLength":2000,"property":"webhookUrl"}]}},{"name":"verify-partner-data-database","aliases":[],"operationId":"verifyPartnerDataDatabase","method":"POST","path":"/partners/{partnerId}/data-database/verify","server":"api","summary":"Check that a partner's own database still works","description":"Runs the same check as `setPartnerDataDatabase` against the database already saved: a schema is made in it, a\ntable created, a row written and read back, and the schema removed again. Use it after changing the role's\npassword or the project's settings in Neon, or when saving is failing with 503 `PARTNER_DATABASE_UNAVAILABLE`.\n\nThe result is recorded either way. A pass refreshes `verifiedAt` and clears `lastVerificationError`; a failure\nanswers 422 `PARTNER_DATABASE_UNVERIFIED` and is recorded in `lastVerificationError` and\n`lastVerificationFailedAt`, so the database shows what went wrong the next time it is read. A failed check does\nnot remove the database. `details.reason` is one of the reasons `setPartnerDataDatabase` lists, or\n`STORED_STRING_UNREADABLE` when the connection string Widgetic stored can no longer be read: set the database\nagain, with its connection string.\n\nThis needs an API key with full access. The caller must be the person who administers the partner, or a\nWidgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose database to check","format":"uuid","in":"path"}]},{"name":"verify-partner-storage-destination","aliases":[],"operationId":"verifyPartnerStorageDestination","method":"POST","path":"/partners/{partnerId}/storage/verify","server":"api","summary":"Check that a partner's storage destination still works","description":"Runs the same check as `setPartnerStorageDestination` against the destination already saved: a test\nfile is written, read back from the public address and deleted. Use it after rotating a key or changing\na bucket policy, or when uploads are failing with 502 or 503.\n\nThe result is recorded either way. A pass refreshes `verifiedAt` and clears `lastVerificationError`; a\nfailure answers 422 `STORAGE_DESTINATION_UNVERIFIED` and is recorded in `lastVerificationError` and\n`lastVerificationFailedAt`, so the destination shows what went wrong the next time it is read. A failed\ncheck does not remove the destination or stop uploads from using it.\n\nThe caller must be the person who administers the partner, or a Widgetic administrator.","accessLevel":"partner","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"partner-id","name":"partnerId","type":"string","required":true,"description":"The partner whose storage destination to check","format":"uuid","in":"path"}]}]},{"name":"public-embed","title":"Public Embed","description":"The Public Embed operations of the Widgetic API.","hidden":true,"commands":[{"name":"call-public-composition-adapter","aliases":[],"operationId":"callPublicCompositionAdapter","method":"POST","path":"/embed/compositions/{compositionId}/adapters/{adapterId}/{operation}","server":"root","summary":"Call a gateway operation of an adapter a published widget uses","description":"The door a widget's adapter module calls, from the visitor's browser, for the operations that cannot run in the browser: anything with a secret, a charge in credits or the owner's storage behind it. A widget that uses such an adapter is handed the address of this route as `adapters.url` in its `widgetic:host` message (see the host bridge contract) and calls `{adapters.url}/{adapterId}/{operation}`. It is not meant to be called by hand, and it takes no API key.\n**Address.** `https://api.widgetic.com/embed/compositions/{compositionId}/adapters/{adapterId}/{operation}`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.\n**What it serves.** Only an adapter, and a version of it, that the widget's LIVE manifest (what the version that is published uses) lists, and only an operation that runs on the gateway. Anything else is one 404 `ADAPTER_NOT_AVAILABLE`, whatever the reason, so the route is no way to ask what a widget contains. An adapter an administrator stopped offering to NEW widgets keeps serving the widgets that already use it.\n**Who may call.** The same rule as the entry route: a permitted `Origin` header, which is the artifact origin the widget runs from or any website when the owner has not restricted the allowed websites (when the owner has, one of the websites the owner listed, Widgetic's own sites, the visitor's own machine or the owner's own platform). Page script cannot forge it. A request without a permitted `Origin` is 403 `ORIGIN_NOT_ALLOWED`. A composition that is not served (unpublished, or its owner's plan does not serve it) is 404, with the codes of the other public embed routes. When the partner of the owner signs the host context, a context that is not signed as the partner requires is refused with 403 `CONTEXT_SIGNATURE_REQUIRED`, `CONTEXT_SIGNATURE_INVALID` or `CONTEXT_SIGNATURE_EXPIRED`.\n**Cost and retries.** An operation priced in credits is charged to the owner of the widget (for a customer a partner manages, to the partner's administrator, within the monthly credits set for that customer), once for each `requestId`, before it runs, and given back if it does not answer. A `requestId` that was charged and not given back is 409 `REQUEST_ALREADY_USED` and the operation is NOT run a second time; one whose charge was given back may be tried again with the same `requestId`. A visitor never sees an owner's balance: a feature that cannot be used because its owner has no credits left, because the customer's monthly credits are spent, or because an administrator stopped the adapter, is the same 503 `ADAPTER_UNAVAILABLE` as every other \"not right now\". The owner is told through a notice.\n**Limits.** In front of everything, 120 calls a minute for each visitor address and composition and 600 a minute for a composition (429 `RATE_LIMITED`). Then, for each operation of a widget, a rate for each visitor address a minute and a cap for the widget a day: 30 and 5000 for an operation that is free, 10 and 500 for one that costs credits, unless the adapter says otherwise (never above 120 and 100000). A visitor over the rate is 429 `RATE_LIMITED`; a widget over its cap is 429 `WIDGET_LIMIT_REACHED`. Both carry a `Retry-After` header. The body is at most 64 KiB (413 `PAYLOAD_TOO_LARGE`) and JSON (415 `UNSUPPORTED_MEDIA_TYPE`); an operation has 30 seconds to answer (504 `ADAPTER_TIMEOUT`, and the charge is given back).","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"composition-id","name":"compositionId","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"adapter-id","name":"adapterId","type":"string","required":true,"description":"The id of the adapter, the name its module is known by, for example `upload`.","maxLength":40,"pattern":"^[a-z][a-z0-9]*(-[a-z0-9]+)*$","in":"path"},{"flag":"operation","name":"operation","type":"string","required":true,"description":"The name of one operation of the adapter, as its documentation gives it.","maxLength":40,"pattern":"^[a-z][a-z0-9]*(-[a-z0-9]+)*$","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["requestId"],"additionalProperties":false,"properties":{"requestId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_-]{7,63}$","description":"Names this attempt: 8 to 64 characters, letters, digits, `_` and `-`, the first a letter or a digit (a UUID fits). A module makes a new one for each action of the visitor. An operation that costs credits is charged once for each `requestId`, so a repeat of one that was charged and not given back is refused (409 `REQUEST_ALREADY_USED`) and does no work."},"version":{"type":"integer","minimum":1,"maximum":1000,"description":"The version of the adapter the widget's code was built against (the `vN` of its import). When it is left out, the newest version the widget's live manifest lists."},"input":{"type":"object","additionalProperties":true,"description":"What the operation is asked to do. Each operation says what it accepts and answers 400, 413 or 422 with a code of its own for what it cannot use. Defaults to an empty object."},"context":{"type":["object","null"],"additionalProperties":true,"description":"The host context the embedding page gave the widget, as the entry route takes it (see the description of `createPublicCompositionEntry`, with its **Signed context**). It is not authenticated unless the partner signs it. At most 4096 bytes of JSON, the two signature keys included."},"captchaToken":{"type":["string","null"],"maxLength":4096,"description":"The visitor's CAPTCHA token, for an operation that asks for one (the answer `CAPTCHA_REQUIRED` says so)."}},"example":{"requestId":"Example requestId","version":42,"input":{},"context":{},"captchaToken":"Example captchaToken"}},"flags":[{"flag":"request-id","name":"requestId","type":"string","required":true,"description":"Names this attempt: 8 to 64 characters, letters, digits, `_` and `-`, the first a letter or a digit (a UUID fits). A module makes a new one for each action of the visitor. An operation that costs credits is charged once for each `requestId`, so a repeat of one that was charged and not given back is refused (409 `REQUEST_ALREADY_USED`) and does no work.","pattern":"^[A-Za-z0-9][A-Za-z0-9_-]{7,63}$","property":"requestId"},{"flag":"version","name":"version","type":"integer","required":false,"description":"The version of the adapter the widget's code was built against (the `vN` of its import). When it is left out, the newest version the widget's live manifest lists.","minimum":1,"maximum":1000,"property":"version"},{"flag":"captcha-token","name":"captchaToken","type":"string","required":false,"description":"The visitor's CAPTCHA token, for an operation that asks for one (the answer `CAPTCHA_REQUIRED` says so).","maxLength":4096,"property":"captchaToken"}]}},{"name":"create-public-composition-entry","aliases":[],"operationId":"createPublicCompositionEntry","method":"POST","path":"/embed/compositions/{compositionId}/entries/{table}","server":"root","summary":"Append a visitor entry through the gateway","description":"Public append-only write into a published composition database. No API key. The browser must send a permitted `Origin` header: the artifact iframe origin, or any website when the owner has not restricted the allowed websites (when the owner has, one of the websites the owner listed, Widgetic's own sites, the visitor's own machine or the owner's own platform). When the request comes from the shared artifact origin, it must also carry the per-composition write token (`X-Widgetic-Write-Token` header or `writeToken` body field, handed to the widget in `widgetic:host` as `entries.writeToken` and returned as `entryWriteToken` on `getPublicCompositionEmbed`). Host `context` is stored for partner attribution; it is not authenticated unless the partner signs it (see **Signed context**). Request bodies are limited to 16 KB before parsing.\n**Address.** `https://api.widgetic.com/embed/compositions/{compositionId}/entries/{table}`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.\n**Signed context.** A partner that wants to trust `context` (to bill a customer by it, say) makes a signing secret (`rotatePartnerEntryContextSecret`), and its server signs the context it hands to the embed. The signature travels inside `context` as two reserved keys, `widgetic_exp` (UTC seconds, at most seven days ahead) and `widgetic_sig`. The gateway checks them after the `Origin` and before anything else is judged, stored or announced, and keeps and announces the context WITHOUT them. A context that is not signed as the partner requires is refused with 403 `CONTEXT_SIGNATURE_REQUIRED`, `CONTEXT_SIGNATURE_INVALID` or `CONTEXT_SIGNATURE_EXPIRED`; when the check cannot be made just now the answer is a retryable 503 `CONTEXT_SIGNATURE_UNAVAILABLE`, never an unchecked entry. Until a partner makes a secret nothing changes: the two keys are dropped and the rest is taken as it came. A signature is good until its `widgetic_exp` and may be used again until then, so a partner that must count each submission once keeps a unique value of its own (a `submissionId`, say) in the context. The recipe and a test vector are in the description of `rotatePartnerEntryContextSecret`.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"composition-id","name":"compositionId","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"table","name":"table","type":"string","required":true,"description":"","in":"path"},{"flag":"write-token","name":"X-Widgetic-Write-Token","type":"string","required":false,"description":"Signed per-composition write token handed to the widget in `widgetic:host` as `entries.writeToken` (and returned by `GET /embed/compositions/{compositionId}` as `entryWriteToken`). Required when the request comes from the shared Widgetic artifact origin; verified whenever present.","in":"header"},{"flag":"visitor-id","name":"X-Widgetic-Visitor-Id","type":"string","required":false,"description":"A random id the widget's browser makes once and keeps (16 to 64 characters of `A-Za-z0-9_-`), sent so a table's per-visitor `limit` (declared in the widget database file) can count this browser. Not an identity and not a secret. Without it, or with a malformed value, the limit counts only by network address.","pattern":"^[A-Za-z0-9_-]{16,64}$","in":"header"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["values"],"properties":{"values":{"type":"object","additionalProperties":true,"description":"Column values to insert (never id or time columns)."},"context":{"type":["object","null"],"description":"Host attribution from the embedding page. It is not authenticated unless the partner signs it (see the description of this operation): never use it for authorization on its own. At most 20 keys; string values up to 500 characters or finite numbers; 2048 bytes JSON total. A signed context carries two more keys, `widgetic_exp` and `widgetic_sig`: the gateway checks them and drops them, so they are neither stored nor announced."},"writeToken":{"type":["string","null"],"description":"Optional per-composition write token (`entries.writeToken` from `widgetic:host`, or `entryWriteToken` from `GET /embed/compositions/{compositionId}`) when not passed in the `X-Widgetic-Write-Token` header."}},"example":{"values":{},"context":{},"writeToken":"Example writeToken"}},"flags":[{"flag":"body-write-token","name":"writeToken","type":"string","required":false,"description":"Optional per-composition write token (`entries.writeToken` from `widgetic:host`, or `entryWriteToken` from `GET /embed/compositions/{compositionId}`) when not passed in the `X-Widgetic-Write-Token` header.","property":"writeToken"}]}},{"name":"get-public-composition-embed","aliases":[],"operationId":"getPublicCompositionEmbed","method":"GET","path":"/embed/compositions/{compositionId}","server":"root","summary":"Resolve a composition UUID to a published widget URL","description":"Public customer embed lookup. No API key. Used by embed-sdk.js on third-party sites.\nIf the composition's owner listed the websites it may appear on (the workspace's domain list, or the `allowedDomains` of a partner the owner administers), a request from any other website is refused with 403 `DOMAIN_NOT_ALLOWED`. The website is read from the `Origin` header, or the host of `Referer` when there is no `Origin`; a request that names neither (a server, a tool) is not refused. An owner who listed nothing is not restricted, and a page on the visitor's own machine (`localhost`, `127.0.0.1` or `[::1]`, on any port) is always allowed. So is the website where a partner integrates Widgetic, with every subdomain of it (`platformDomain` in the answer of `GET /partners/mine`), for the widgets of that partner: a partner's list never has to name it. The list stops other websites from showing the embed in a visitor's browser; it does not hide the published content from someone who holds the composition ID.\n**Address.** `https://api.widgetic.com/embed/compositions/{compositionId}`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.\n**Limit.** 500 calls for each visitor address and browser in one counting window, shared with the frame and the status routes. Over it the answer is 429 `RATE_LIMITED` with a `Retry-After` header.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"composition-id","name":"compositionId","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-public-composition-iframe","aliases":[],"operationId":"getPublicCompositionIframe","method":"GET","path":"/embed/compositions/{compositionId}/frame","server":"root","summary":"Embed a published composition with its saved values","description":"Public HTML wrapper with an inner published artifact iframe. No API key or SDK required.\nIf the composition's owner listed the websites it may appear on, the page is sent with `Content-Security-Policy: frame-ancestors` naming those websites (and Widgetic's own, and the website where the partner the owner administers integrates Widgetic, with its subdomains), so a browser refuses to show it inside any other website whatever `Referer` policy that website uses. Opening the address directly still works. An owner who listed nothing is not restricted.\n**Address.** `https://api.widgetic.com/embed/compositions/{compositionId}/frame`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"composition-id","name":"compositionId","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-public-workflow","aliases":[],"operationId":"getPublicWorkflow","method":"GET","path":"/embed/workflows/{workflowId}","server":"root","summary":"Read the published Workflow of an embed","description":"Public read of a workflow for the embed that carries `workflow-id`. No API key. The answer is the live version (its graph, the composition a new visitor starts on, the time zone, the languages its compositions can be shown in, and the token that lets the browser report counts), or `status: paused` with nothing else, in which case the embed shows only its first composition. A workflow that is a draft, archived or unknown answers 404, and the embed then behaves as a plain embed of its `composition-id`. So does a workflow whose owner is not being served (a plan that lapsed, a partner whose agreement is not in force): the answer is the one the compositions of that owner give.\n**Address.** `https://api.widgetic.com/embed/workflows/{workflowId}`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.\nThe answer may be reused for a few seconds (`Cache-Control: public, max-age=15`), so a publish or a pause reaches visitors within moments. The graph is readable by anyone who has the workflow's id: its rules run in the visitor's browser, so a rule must never hold a secret.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-public-workflow-frame","aliases":[],"operationId":"getPublicWorkflowFrame","method":"GET","path":"/embed/workflows/{workflowId}/frame","server":"root","summary":"Show a published Workflow in a page of its own","description":"A page that runs a published workflow, for a place that can only be given an address or an iframe: a chat messenger, a link page, a site builder, a blog that follows oEmbed. No API key, no SDK to add. The page holds the Embed SDK and the same tag a website carries (`<widgetic workflow-id composition-id>`), so a visitor moves from one composition to the next in place, by the same rules, exactly as on a website. A paused workflow shows its first composition alone, as its embeds do.\n**What the frame can count.** Only what happens inside it: entries, a widget's goals and actions, and waits. The page events a website can send to a workflow (`widgetic:workflow-event`) are events of the page around the embed, and they do not cross an iframe, so a connection that waits for one never fires from a frame.\nA workflow that is a draft, is archived, or does not exist, and one whose owner is not being served, answers 404 with a short page that gives no reason. If the owner of the workflow's first composition listed the websites it may appear on, the page is sent with `Content-Security-Policy: frame-ancestors` naming those websites (and Widgetic's own), so a browser refuses to show it inside any other website. Opening the address directly still works. An owner who listed nothing is not restricted.\nThe page links its oEmbed card, so a site that follows oEmbed turns the address of the frame into an embed.\n**Address.** `https://api.widgetic.com/embed/workflows/{workflowId}/frame`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"get-public-workflow-version","aliases":[],"operationId":"getPublicWorkflowVersion","method":"GET","path":"/embed/workflows/{workflowId}/versions/{versionId}","server":"root","summary":"Read one published version of a Workflow","description":"Public read of one version of a workflow, for a visitor who started on it before a newer one was published: they finish on the version they started on (for up to 30 days; after that they get the latest). The graph of a version never changes once published; the languages its compositions can be shown in (`translations`) are read each time, as for the live version. Like the read of the live version, it answers `status: paused` with nothing else while the workflow is paused, and 404 when the workflow is a draft or archived, or has no such version.\n**Address.** `https://api.widgetic.com/embed/workflows/{workflowId}/versions/{versionId}`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"version-id","name":"versionId","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"post-public-composition-embed-status","aliases":[],"operationId":"postPublicCompositionEmbedStatus","method":"POST","path":"/embed/compositions/status","server":"root","summary":"Batch-check whether compositions are currently served for embed","description":"Public batch health check for embed-sdk.js. No API key. Uses the same serving gate and domain policy as `GET /embed/compositions/{compositionId}`: the caller's website is read from `Origin`, or the host of `Referer` when there is no `Origin`. A request that names neither (a server or tool) is not refused for domain reasons.\n**Address.** `https://api.widgetic.com/embed/compositions/status`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.\n**Limit.** The route counts against the same limit as the lookup and the frame routes: 500 calls for each visitor address and browser in one counting window. A call is ONE hit whatever the number of `ids` it carries (up to 100), so a server that checks many compositions spends one call for each hundred of them. A visitor who is over the limit gets 429 `RATE_LIMITED` for the WHOLE call, with a `Retry-After` header and no state for any id: there is no per-composition rate-limit state in the answer.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["ids"],"properties":{"ids":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"string","format":"uuid"}}}},"flags":[{"flag":"ids","name":"ids","type":"string[]","required":true,"description":"","property":"ids"}]}},{"name":"report-public-workflow-events","aliases":[],"operationId":"reportPublicWorkflowEvents","method":"POST","path":"/embed/workflows/{workflowId}/events","server":"root","summary":"Report what visitors did in a Workflow, as counts","description":"Public report of counts for a workflow, sent by the embed. No API key. Each event is one id and a number: a visitor reached a block (`n`), took a connection (`e`), or could not be moved on (`f`). Nothing else is kept: no visitor, no value a visitor typed, no address. A batch carries at most 100 events and 16 KB. The same report may carry `heights`: how tall a step settled at in the browser, which the embed code of the workflow uses to start its box at a real height (a composition, a whole number of pixels, nothing about the visitor).\nA report is accepted only with the `token` the workflow's own read handed out for that `versionId`, so a page that never read the workflow cannot raise its numbers. An expired token (`EVENT_TOKEN_EXPIRED`) is refused with 401: read the workflow again for a new one. Counts for ids that are not in that version, or for a workflow that is not live, are not recorded and the answer says so (`recorded: false`) with 200: the visitor's page has nothing to fix.\nThe body is JSON, sent as `application/json` or as `text/plain` (a \"simple\" cross-origin request, which is what lets a report leave a page that is being closed).\n**Address.** `https://api.widgetic.com/embed/workflows/{workflowId}/events`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","description":"One report from a visitor's browser: counts (`events`), the heights steps settled at (`heights`), or both. A report\nwith neither is refused.\n","required":["versionId","token"],"properties":{"versionId":{"type":"string","format":"uuid"},"token":{"type":"string","description":"The `eventToken` of the workflow, read for the same version."},"events":{"type":"array","maxItems":100,"description":"The counts of what visitors did. At least one of `events` and `heights` is needed.","items":{"type":"object","required":["k","id"],"properties":{"k":{"type":"string","enum":["n","e","f"],"description":"`n` a visitor reached a block, `e` a visitor took a connection, `f` moving a visitor on failed."},"id":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$","description":"The block or connection counted."},"c":{"type":"integer","minimum":1,"maximum":50,"description":"How many times (1 when not given)."}}}},"heights":{"type":"array","maxItems":10,"description":"How tall steps of this version settled in the visitor's browser, so that the embed code of the workflow can\nstart its box at the height visitors saw instead of a guess. The Embed SDK sends one for a visitor who has no\nremembered height for the step. The gateway keeps the latest few for each composition (the median is what the\ncode gets); a composition that this version does not show is left out. At least one of `events` and `heights`\nis needed.\n","items":{"type":"object","required":["compositionId","height"],"properties":{"compositionId":{"type":"string","format":"uuid","description":"The composition the step shows."},"height":{"type":"integer","minimum":1,"maximum":100000,"description":"The height the step settled at, in pixels. One under 100 is not kept, one over 2000 is kept as 2000."}}}}},"example":{"versionId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","token":"Example token","events":[],"heights":[]}},"flags":[{"flag":"version-id","name":"versionId","type":"string","required":true,"description":"","format":"uuid","property":"versionId"},{"flag":"token","name":"token","type":"string","required":true,"description":"The `eventToken` of the workflow, read for the same version.","property":"token"}]}},{"name":"upload-public-composition-adapter-file","aliases":[],"operationId":"uploadPublicCompositionAdapterFile","method":"PUT","path":"/embed/compositions/{compositionId}/adapters/upload/files/{ticket}","server":"root","summary":"Send the file a visitor chose through the upload adapter of a published widget","description":"The second of the two calls the `upload` adapter's module makes from the visitor's browser (the first, the `begin-upload` operation of `callPublicCompositionAdapter`, answers with the ticket this route takes). The module sends the file itself, as the body, with its media type as the `Content-Type`. It is not meant to be called by hand, and it takes no API key.\n**Address.** `https://api.widgetic.com/embed/compositions/{compositionId}/adapters/upload/files/{ticket}`: the root of the API host, NOT under `/v1`. A client generated from this description for `https://api.widgetic.com/v1` must be given the base address `https://api.widgetic.com` for the calls of the `Public Embed` tag.\n**The ticket.** Single-use and valid for ten minutes, given for one file: its name, its media type and its exact size are what the visitor said when the ticket was asked for, after the widget's owner's settings (the kinds of file they take, the largest size) were applied. The `Content-Type` and the `Content-Length` of this request must be those, and the body must be exactly that many bytes (400 `UPLOAD_FILE_CHANGED` otherwise). The gateway never believes the type it was told: the first bytes of the file must be a file of that type and one the owner accepts (422 otherwise). A ticket whose file was stored and is sent again is answered with the same file, so a visitor whose connection dropped after the file landed does not store it twice; a ticket whose attempt failed is spent, and the module asks for a new one.\n**The virus check.** Before anything is stored, the file is checked for viruses, wherever it would be kept (Widgetic's storage or a partner's own bucket). A file that fails the check is refused with 422 `UPLOAD_FILE_UNSAFE` and nothing is stored. When the check cannot be done, nothing is stored either, and the answer is the same neutral 503 `UPLOAD_UNAVAILABLE` as for any other cause the visitor is not told. The ticket of a refused file is spent.\n**What it stores.** The file goes to the widget owner's storage: Widgetic's, or the partner's own bucket for a widget of a customer a partner manages. It counts against the owner's storage allowance and is filed in the folder the owner chose, as a file sent by a visitor, which the owner's own file list leaves out. A full allowance, a partner's storage that cannot be used and a stopped feature are all the same 503 `UPLOAD_UNAVAILABLE` to a visitor, and the owner is told through a notice.\n**Who may call.** The same rule as the entry route: a permitted `Origin` header, which is the artifact origin the widget runs from or any website when the owner has not restricted the allowed websites (when the owner has, one of the websites the owner listed, Widgetic's own sites, the visitor's own machine or the owner's own platform). A request without a permitted `Origin` is 403 `ORIGIN_NOT_ALLOWED`. A composition that is not served (unpublished, or its owner's plan does not serve it) is 404, with the codes of the other public embed routes.\n**Limits.** 30 requests a minute for each visitor address and composition and 240 a minute for a composition (429 `RATE_LIMITED`, with a `Retry-After` header); at most 10 MB for a file, and only so many files held in memory at once on each gateway process (503 `UPLOAD_BUSY`, with a `Retry-After`). A file that does not arrive within two minutes is 408 `UPLOAD_TIMEOUT`. Every answer but a success closes the connection, so a client that is refused before it has sent the file reads the answer and stops.","accessLevel":"public","auth":false,"hidden":true,"confirm":false,"parameters":[{"flag":"composition-id","name":"compositionId","type":"string","required":true,"description":"","format":"uuid","in":"path"},{"flag":"ticket","name":"ticket","type":"string","required":true,"description":"The ticket the `begin-upload` operation answered with.","minLength":20,"maxLength":128,"pattern":"^[A-Za-z0-9_-]{20,128}$","in":"path"}],"body":{"required":true,"kind":"binary","contentType":"application/pdf","contentTypes":["application/pdf","audio/aac","audio/flac","audio/mp4","audio/mpeg","audio/ogg","audio/wav","audio/webm","image/gif","image/heic","image/heif","image/jpeg","image/png","image/webp","video/mp4","video/quicktime","video/webm"],"flags":[]}}]},{"name":"tokens","title":"Tokens","description":"The Tokens operations of the Widgetic API.","hidden":false,"commands":[{"name":"validate-token","aliases":[],"operationId":"validateToken","method":"GET","path":"/tokens/validate","server":"api","summary":"Validate current authentication token","description":"","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[]}]},{"name":"uploads","title":"Uploads","description":"The Uploads operations of the Widgetic API.","hidden":false,"commands":[{"name":"uploads-get","aliases":["list"],"operationId":"uploadsGet","method":"GET","path":"/uploads","server":"api","summary":"List the account's uploads","description":"The files the account has uploaded, newest first. Deleted files are not listed. Returns at most `limit` files and has no further pages: a client that needs more narrows the list instead, for example to one managed user's files with `managedUserId` or `managedUserExternalId`. The files that visitors sent through a widget's upload adapter (`source` is `widget-visitor-upload`) are not part of this list, so that a popular widget does not push the account's own files out of it; naming one widget with `widgetId` lists what visitors sent to that widget as well.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"limit","name":"limit","type":"integer","required":false,"description":"Maximum number of files to return","default":50,"minimum":1,"maximum":100,"in":"query"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"List only the files uploaded for this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both.","format":"uuid","in":"query"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"List only the files uploaded for the managed user you registered with this `externalId` (your own identifier for the user). Send this or `managedUserId`, not both.","maxLength":255,"in":"query"}]},{"name":"uploads-id-delete","aliases":["delete"],"operationId":"uploadsIdDelete","method":"DELETE","path":"/uploads/{id}","server":"api","summary":"Delete an upload","description":"Removes the stored file and the upload row. A file in Widgetic's own storage gives back the space it took from\nyour storage allowance; a file in your own bucket is removed from your bucket. Deleting an upload that is\nalready gone succeeds the same way as the first delete. An upload that belongs to another account is not found.\nIf the storage cannot be reached the call fails and nothing is marked deleted, so it can be repeated.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"","format":"uuid","in":"path"}]},{"name":"uploads-id-get","aliases":["get"],"operationId":"uploadsIdGet","method":"GET","path":"/uploads/{id}","server":"api","summary":"Get an upload by id","description":"The details of one of the account's uploads. An upload that belongs to another account is not found.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The upload to read","format":"uuid","in":"path"}]},{"name":"uploads-limits-get","aliases":[],"operationId":"uploadsLimitsGet","method":"GET","path":"/uploads/limits","server":"api","summary":"Upload size limits and accepted file types","description":"The size limit and the accepted file types of the upload endpoints. Read them from here rather than hard-coding thresholds: they are configuration and can change. The limits are the same for every account, whatever its plan: `multipart_max_bytes` for a file sent in one request (10 MB) and `tus_max_bytes` for a resumable upload (250 MB).","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[]},{"name":"uploads-post","aliases":["create"],"operationId":"uploadsPost","method":"POST","path":"/uploads","server":"api","summary":"Upload a file","description":"Stores one file for the account and returns its public address. This is how a user's own images,\nvideo and PDFs reach a widget's content.\n\nSend a `multipart/form-data` request with the file in the field named `file`. The file counts\nagainst the storage of the workspace it is uploaded in, and a full workspace is refused with 413 and\n`QUOTA_STORAGE_EXCEEDED`. For a person signed in on widgetic.com that is the workspace they are currently\nworking in: inside a team, the team's shared storage; inside their own workspace, their own allowance. An\norganization API key, and a Shopify store's session, always work in that team's shared storage. A personal\nAPI key or an SDK token always uses the account's own allowance. The two never add up and one never lends\nroom to the other. A file over the size limit is refused with 413 and `UPLOAD_TOO_LARGE`, and a\ntype that is not accepted with 415 and `UPLOAD_UNSUPPORTED_TYPE`; `uploadsLimitsGet` lists the size\nlimit and the accepted types.\n\nA file is judged by what is in it, not by the type it is sent as. A file that is not the type it claims (a\nweb page sent as `image/png`) is refused with 415 and `UPLOAD_UNSUPPORTED_TYPE`, and a picture sent under\nthe wrong picture type is stored under the type it really is, which `mime_type` in the answer says. An SVG\nis rewritten as it is stored: script, `foreignObject`, animation and references to other addresses are\ntaken out, it must be UTF-8 and at most 2 MB, and the stored file can be larger or smaller than the one\nsent. Anything that is not an image, audio or video (a PDF, for example) is served as a download rather\nthan shown in a page.\n\nWhen the full storage is a Shopify store's team's, extra storage cannot be added through Shopify yet, so the\nmessage ends by saying so and what to do instead: delete files, or contact support. On a Solo plan it names\nthe Teams plan (which holds more) and `error.details.planPageUrl` is the address of the plan page on Shopify.\n`error.suggestedAction` says the same in a few words, instead of the usual \"upgrade your storage plan\". Any\nother team, and a person's own workspace, get the shorter message.\n\nTo upload the file for one of your managed users, send `managedUserId` (Widgetic's id for the user) or\n`managedUserExternalId` (your own identifier for the user), not both. The file stays in your account and\ncounts against its allowance; the tag lets you list one user's files with `uploadsGet`. The managed user\nmust be yours and active: one that is not yours, or does not exist, is refused with 404 and\n`MANAGED_USER_NOT_FOUND`, and one that is switched off with 409 and `MANAGED_USER_INACTIVE`, before the\nfile is stored. The tag is set here and does not change afterwards.\n\nCalled with an editor session (`createManagedUserEditorSession`), the file is always filed for the\nsession's own managed user and is always media for widget content: naming another managed user is\nrefused with 403 and `MANAGED_USER_SESSION_MISMATCH`, and `contextType`, `source`, `widgetId` and\n`conversationId` are ignored. A full storage allowance is refused with the same 413 and\n`QUOTA_STORAGE_EXCEEDED`, but the message only says that it is not available right now (not how much is\nstored: that is the partner's business) and `error.details.limit` is `storage`.\n\nAn account that administers a partner with a storage destination (`setPartnerStorageDestination`) has\nits files stored in the partner's own bucket instead. Such a file takes none of the allowance, its `url`\nis served from the partner's public address, and its `storage_key` is the key in the partner's bucket.\nThe file is never stored with Widgetic instead: when the destination cannot be used the upload is refused\nwith 503 and `STORAGE_DESTINATION_UNAVAILABLE`, and when the partner's storage rejects the file with\n502 and `STORAGE_DESTINATION_WRITE_FAILED`.\n\nPartner storage is not optional, and it does not depend on how the file arrives. A partner's own uploads (with\nits API key, an SDK token, or from the Widgetic website, the resumable route and the media library alike)\nand the uploads of its customers' editor sessions are kept in the partner's own storage or not at all, so a\npartner with no destination (or one that removed it after the session was made) has them refused with 409\nand `PARTNER_STORAGE_REQUIRED`; they are never stored with Widgetic. An editor session's browser is only\ntold that the feature is not available right now.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[],"body":{"required":true,"kind":"multipart","contentType":"multipart/form-data","schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"The file to store"},"contextType":{"type":"string","enum":["widget-codegen","browser-ic","content-property"],"default":"content-property","description":"What the file is for. Send `content-property` for media that becomes widget content (images, video and PDF); the other values are used by Widgetic's own editor.\n"},"source":{"type":"string","enum":["chat-upload","chat-screenshot","browser-ic-screenshot","preview-screenshot","file-browser-upload"],"default":"file-browser-upload","description":"Which part of the product, or which client, the file comes from"},"managedUserId":{"type":"string","format":"uuid","description":"Upload the file for one of your managed users, by Widgetic's id for the user. Send this or `managedUserExternalId`, not both."},"managedUserExternalId":{"type":"string","maxLength":255,"description":"The same, by your own identifier for the user, the `externalId` you gave when you created the managed user. Send this or `managedUserId`, not both."},"purpose":{"type":"string","enum":["canvas","chat","field"],"description":"Where in your product you use the file. It is kept with the file, so that a customer's storage can be counted by purpose (`getManagedUserStorageUsage`). Optional; any other value is refused with 400 and `UPLOAD_INVALID_PURPOSE`."}}},"flags":[{"flag":"file","name":"file","type":"file","required":true,"description":"The file to store","property":"file"},{"flag":"context-type","name":"contextType","type":"string","required":false,"description":"What the file is for. Send `content-property` for media that becomes widget content (images, video and PDF); the other values are used by Widgetic's own editor.","enum":["widget-codegen","browser-ic","content-property"],"default":"content-property","property":"contextType"},{"flag":"source","name":"source","type":"string","required":false,"description":"Which part of the product, or which client, the file comes from","enum":["chat-upload","chat-screenshot","browser-ic-screenshot","preview-screenshot","file-browser-upload"],"default":"file-browser-upload","property":"source"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"Upload the file for one of your managed users, by Widgetic's id for the user. Send this or `managedUserExternalId`, not both.","format":"uuid","property":"managedUserId"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"The same, by your own identifier for the user, the `externalId` you gave when you created the managed user. Send this or `managedUserId`, not both.","maxLength":255,"property":"managedUserExternalId"},{"flag":"purpose","name":"purpose","type":"string","required":false,"description":"Where in your product you use the file. It is kept with the file, so that a customer's storage can be counted by purpose (`getManagedUserStorageUsage`). Optional; any other value is refused with 400 and `UPLOAD_INVALID_PURPOSE`.","enum":["canvas","chat","field"],"property":"purpose"}]}}]},{"name":"users","title":"Users","description":"The Users operations of the Widgetic API.","hidden":false,"commands":[{"name":"add-personal-workspace-domain","aliases":[],"operationId":"addPersonalWorkspaceDomain","method":"POST","path":"/users/{id}/workspace/domains","server":"api","summary":"Allow a website to show your widgets","description":"Adds a website to an account's own list. Give a website name such as `example.com`, or `*.example.com` for a website and all of its subdomains. A scheme, a port, a path, capital letters and a leading `www.` are dropped, so `https://www.Example.com/shop` is kept as `example.com`, and `example.com` covers `www.example.com` too. Something that is not a website name is refused, not stored.\nUntil the first website is added nothing is restricted, and a widget can be shown anywhere. Once the list has one, a widget of the account is shown on the listed websites (and on Widgetic's own) and nowhere else. A page on your own machine (`localhost`, `127.0.0.1` or `[::1]`, on any port) is always allowed, so there is nothing to add to test locally. So is the website where a partner your account administers integrates Widgetic, with every subdomain of it (`platformDomain` in the answer of `GET /partners/mine`), so there is nothing to add for it either and a list can never lock it out. A change can take about 15 seconds to reach every server.\nThe list is a safeguard against other websites showing your widgets in a visitor's browser. It is not access control: anyone who knows a widget's id can still fetch its published content with a plain request that names no website.\nThere is no limit on the list of an account that administers a partner, so a partner can add the website of each of its customers as they publish on their own domain, one call each. Any other account can list at most 50 websites, and the 51st is answered with a 409 (`BUSINESS_STATE_CONFLICT`). A wildcard such as `*.example.com` counts as one website, not one per subdomain, so use it instead of listing each subdomain.\nA page that frames a widget can name only so many websites to the browser (about fifty of ordinary length). Past that, the browser no longer checks the list for that page; the lookup that the Embed SDK makes for the widget still refuses every website that is not on the list.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the list belongs to. For your own, your own user id","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["domain"],"properties":{"domain":{"type":"string","description":"Domain pattern to allow (supports wildcards like *.example.com)","example":"example.com"}},"example":{"domain":"example.com"}},"flags":[{"flag":"domain","name":"domain","type":"string","required":true,"description":"Domain pattern to allow (supports wildcards like *.example.com)","property":"domain"}]}},{"name":"confirm-user-adapter-connection","aliases":[],"operationId":"confirmUserAdapterConnection","method":"POST","path":"/users/{userId}/adapter-connections/{adapterId}/{credentialKey}/confirm","server":"api","summary":"Confirm an adapter address with the code that was emailed to it","description":"Some credentials are an email address Widgetic sends a widget's messages to (`confirmation: email_code` in\n`getUserAdapterConnections`). Saving one with `setUserAdapterConnection` emails a six-digit code to it; this gives\nthe code back. Only then is the address used: until it is confirmed, the widgets that need it are not charged and\ntheir visitors are told the feature is unavailable.\n\nThe code works for 30 minutes and for five wrong tries. After that, save the address again and a new code is\nemailed (not sooner than a minute after the last one). A code that is not made of digits is refused without\ncounting as a try.\n\nConfirming an address that is already confirmed answers 200 with the same state, so a retry of a request that\nwent through is harmless. Saving a different address, or the same one before it was confirmed, starts again.\n\nThis needs an API key with full access. The caller must be the account itself or a Widgetic administrator.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"user-id","name":"userId","type":"string","required":true,"description":"The account that keeps the address. For your own, your own user id","format":"uuid","in":"path"},{"flag":"adapter-id","name":"adapterId","type":"string","required":true,"description":"The adapter the address is for","maxLength":40,"in":"path"},{"flag":"credential-key","name":"credentialKey","type":"string","required":true,"description":"The credential's name within the adapter","maxLength":40,"in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["code"],"additionalProperties":false,"properties":{"code":{"type":"string","maxLength":32,"description":"The digits of the code Widgetic emailed to the address, with nothing else. It works for 30 minutes and for five wrong tries; after that, save the address again and a new code is emailed","example":"123456"}},"example":{"code":"123456"}},"flags":[{"flag":"code","name":"code","type":"string","required":true,"description":"The digits of the code Widgetic emailed to the address, with nothing else. It works for 30 minutes and for five wrong tries; after that, save the address again and a new code is emailed","maxLength":32,"property":"code"}]}},{"name":"create-user-api-key","aliases":[],"operationId":"createUserApiKey","method":"POST","path":"/users/{id}/keys","server":"api","summary":"Create an API key","description":"Makes a new API key for an account and returns its secret in `key`. That is the only time the secret is shown: Widgetic keeps a hash, so a key that was not stored cannot be recovered, only replaced. To rotate a key, make the new one, switch the integration over to it, then delete the old one.\nOnly a person signed in on widgetic.com can make a key: a request made with an API key, or with an SDK token, is refused with 403. That way a leaked key cannot be used to make a second one that survives its revocation. Make keys from the dashboard; a key can still list, rename, switch off and delete keys.\nAn `Idempotency-Key` header is required (8 to 50 characters: letters, digits, `-` and `_`). A retry with the same header and the same body returns the same answer instead of making a second key, and the same header with a different body is refused with 422. Make keys one at a time: two requests for the same account that run at the same moment can overwrite each other.\nA key can be limited to what it needs. Send a `preset` (`read_only`, `content` or `full_access`) or a list of `scopes`, never both. Send neither for a key that can do everything its owner can, as every key made before scopes existed. A limited key is refused with 403 and the code `API_KEY_SCOPE_MISSING` on anything outside its scopes, so a key held by a build server or by a partner's backend can be given only the part of the API it uses. The scopes of a key are fixed when it is made: to widen one, make a new key and delete the old one. A limited key cannot make, change or delete keys, exchange tokens or reach the administration routes. The list of scopes is in the `ApiKeyScope` schema.\nAccounts with API access (Solo, Teams and partner accounts) have no limit on how many keys they hold. An account on a plan without API access cannot make one (a partner account is not asked about a plan).","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the key is for. For your own, your own user id","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"A name for the key, to tell it from the account's other keys","example":"Production"},"expiresAt":{"type":"string","format":"date-time","description":"When the key stops working, as a date and time in the future with a zone. Omit it for a key that does not expire","example":"2027-01-31T00:00:00Z"},"preset":{"type":"string","enum":["read_only","content","full_access"],"description":"A ready-made list of scopes. `read_only` looks at everything the account holds and changes nothing. `content` builds and manages widgets and compositions and runs the AI, and touches no billing, no people and no partners. `full_access` is the same as sending neither `preset` nor `scopes`. Send either `preset` or `scopes`, not both","example":"content"},"scopes":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/ApiKeyScope"},"description":"What the key may do, when a preset is not enough. Naming a write scope includes its read scope, and a list that holds every scope is a key with full access. Send either `scopes` or `preset`, not both","example":["content:read","generate"]}},"required":["name"],"example":{"name":"Production","expiresAt":"2027-01-31T00:00:00Z","preset":"content","scopes":["content:read","generate"]},"$defs":{"ApiKeyScope":{"type":"string","enum":["content:read","content:write","generate","partner:read","partner:write","billing:read","billing:write","account:read","account:write"],"description":"One thing an API key may be allowed to do. A key made with a list of scopes does only what the list\nnames; a key made without one has full access, and does everything its owner can.\n\n- `content:read` reads widgets, compositions, canvases, projects, folders, uploads, translations and the\n analytics of your content.\n- `content:write` creates, changes, publishes and deletes the same. It includes `content:read`.\n- `generate` runs the AI that builds widgets and compositions, and follows or cancels what it is doing.\n This spends credits.\n- `partner:read` reads managed users, what they spent, presets, the contract and the storage destination.\n- `partner:write` creates, changes and deletes managed users and presets. It includes `partner:read`.\n- `billing:read` reads credits, usage, invoices, subscriptions and payment methods.\n- `billing:write` buys credits, pays invoices and changes a subscription or a payment method. It\n includes `billing:read`.\n- `account:read` reads the profile, organizations and members, allowed domains, notices and the names\n of API keys.\n- `account:write` changes the profile, organizations and members, allowed domains and integrations. It\n includes `account:read`.\n\nStarting an editor session for a managed user needs `partner:write`, `content:write` and `generate`\ntogether. Making, changing and deleting keys, exchanging tokens, signing in and the administration\nroutes need full access: a limited key cannot make itself a bigger one.\n\nA request outside the scopes of the key is refused with 403 and the code `API_KEY_SCOPE_MISSING`.\n`error.details.missingScopes` names what a new key needs, and `error.details.fullAccessRequired` is true\nwhen no list of scopes would have been enough. The scopes of a key cannot be changed once it is made."}}},"flags":[{"flag":"name","name":"name","type":"string","required":true,"description":"A name for the key, to tell it from the account's other keys","minLength":1,"maxLength":100,"property":"name"},{"flag":"expires-at","name":"expiresAt","type":"string","required":false,"description":"When the key stops working, as a date and time in the future with a zone. Omit it for a key that does not expire","format":"date-time","property":"expiresAt"},{"flag":"preset","name":"preset","type":"string","required":false,"description":"A ready-made list of scopes. `read_only` looks at everything the account holds and changes nothing. `content` builds and manages widgets and compositions and runs the AI, and touches no billing, no people and no partners. `full_access` is the same as sending neither `preset` nor `scopes`. Send either `preset` or `scopes`, not both","enum":["read_only","content","full_access"],"property":"preset"},{"flag":"scopes","name":"scopes","type":"string[]","required":false,"description":"What the key may do, when a preset is not enough. Naming a write scope includes its read scope, and a list that holds every scope is a key with full access. Send either `scopes` or `preset`, not both","enum":["content:read","content:write","generate","partner:read","partner:write","billing:read","billing:write","account:read","account:write"],"property":"scopes"}]}},{"name":"delete-personal-workspace-domain","aliases":[],"operationId":"deletePersonalWorkspaceDomain","method":"DELETE","path":"/users/{id}/workspace/domains/{domainId}","server":"api","summary":"Stop allowing a website to show your widgets","description":"Removes one entry from an account's own list. When it was the last one, nothing is restricted any more and a widget can be shown anywhere again. A change can take about 15 seconds to reach every server.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the list belongs to. For your own, your own user id","format":"uuid","in":"path"},{"flag":"domain-id","name":"domainId","type":"string","required":true,"description":"The `id` of the entry, as the list shows it","format":"uuid","in":"path"}]},{"name":"delete-user-api-key","aliases":[],"operationId":"deleteUserApiKey","method":"DELETE","path":"/users/{id}/keys/{keyId}","server":"api","summary":"Delete an API key","description":"Deletes a key and the record of what it was used for. The key stops working at once on the server that took the request and within a minute on the others, and cannot be brought back: make a new one instead. To stop a key without losing it, switch it off with `updateUserApiKey`.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the key belongs to","format":"uuid","in":"path"},{"flag":"key-id","name":"keyId","type":"string","required":true,"description":"The `keyId` of the key, as the list shows it","in":"path"}]},{"name":"get-personal-workspace-domains","aliases":[],"operationId":"getPersonalWorkspaceDomains","method":"GET","path":"/users/{id}/workspace/domains","server":"api","summary":"List the websites allowed to show your widgets","description":"Lists the websites on an account's own list, newest first. It is the list Widgetic checks when a widget of the account is embedded: when it holds at least one website, the widget is shown on those websites (and on Widgetic's own) and nowhere else; when it is empty, the widget can be shown anywhere. If the account administers a partner, the websites set on the partner record by Widgetic are added to them. Widgets that belong to an organization use the organization's list instead.\nThe answer holds the whole list, however long it is: a partner's list has no limit.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the list belongs to. For your own, your own user id","format":"uuid","in":"path"}]},{"name":"get-user-adapter-connections","aliases":[],"operationId":"getUserAdapterConnections","method":"GET","path":"/users/{userId}/adapter-connections","server":"api","summary":"See which adapter keys an account has connected","description":"Widget adapters are capabilities a widget can use, and some of them act through a service the owner has an\naccount with: an adapter that sends email needs the owner's email provider key, one that sends texts needs the\nowner's SMS provider key. This lists every key an adapter takes from an owner and whether this account has\nconnected it, so a settings page can show what is missing.\n\nAn empty list means no adapter takes a key from an owner at the moment. The list never contains a key: only\nwhether one is connected and when it was last set.\n\nConnect a key with `setUserAdapterConnection`; take it away with `removeUserAdapterConnection`. The widgets of\ncustomers a partner manages use the partner's keys instead (`getPartnerAdapterConnections`).\n\nThe caller must be the account itself (an API key of that account works, with the `account:read` scope), or a\nWidgetic administrator.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"user-id","name":"userId","type":"string","required":true,"description":"The account whose adapter keys to list. For your own, your own user id","format":"uuid","in":"path"}]},{"name":"get-user-api-key-usage-logs","aliases":[],"operationId":"getUserApiKeyUsageLogs","method":"GET","path":"/users/{id}/api-keys/{keyId}/logs","server":"api","summary":"Read what an API key was used for","description":"Lists the calls made with one key, newest first: when, which path and method, and the status each ended with. Use it to see whether a key is still in use before deleting it, or to see what a key that should not be in use has been doing.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the key belongs to","format":"uuid","in":"path"},{"flag":"key-id","name":"keyId","type":"string","required":true,"description":"The `keyId` of the key, as the list shows it","in":"path"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"How many calls to return, newest first","default":500,"minimum":1,"maximum":500,"in":"query"}]},{"name":"get-user-api-keys","aliases":[],"operationId":"getUserApiKeys","method":"GET","path":"/users/{id}/keys","server":"api","summary":"List the API keys of an account","description":"Lists the API keys of an account, in the order they were made: name, the first characters of the key, when it was made, when it expires, when it was last used, whether it is switched on and what it is limited to (`scopes` and `preset`). The secret of a key is never part of the list. A caller can list its own account's keys; Widgetic staff can list any account's.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the keys belong to. For your own keys, your own user id","format":"uuid","in":"path"}]},{"name":"remove-user-adapter-connection","aliases":[],"operationId":"removeUserAdapterConnection","method":"DELETE","path":"/users/{userId}/adapter-connections/{adapterId}/{credentialKey}","server":"api","summary":"Remove an adapter key from an account","description":"Removes a key an adapter was given. The adapter stops working for the widgets that need it: their visitors are\ntold the feature is unavailable. Nothing else of the widgets changes, and connecting a key again brings them\nback.\n\nRemoving a key that is not connected answers 200 with `removed: false`: the state asked for is the state it is\nin. A key of an adapter that has since been retired from the catalog can still be removed.\n\nThis needs an API key with full access. The caller must be the account itself or a Widgetic administrator.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"user-id","name":"userId","type":"string","required":true,"description":"The account that keeps the key. For your own, your own user id","format":"uuid","in":"path"},{"flag":"adapter-id","name":"adapterId","type":"string","required":true,"description":"The adapter the key is for","maxLength":40,"in":"path"},{"flag":"credential-key","name":"credentialKey","type":"string","required":true,"description":"The key's name within the adapter","maxLength":40,"in":"path"}]},{"name":"set-user-adapter-connection","aliases":[],"operationId":"setUserAdapterConnection","method":"PUT","path":"/users/{userId}/adapter-connections/{adapterId}/{credentialKey}","server":"api","summary":"Connect an adapter key to an account","description":"Gives an adapter the key it needs to act through a service you have an account with, for the widgets you made.\nTake the `adapterId` and `credentialKey` from `getUserAdapterConnections`. Saving a key that is already\nconnected replaces it.\n\n**What Widgetic does with it.** The key is stored encrypted, bound to this account, this adapter and this\ncredential, so that a stored value cannot be moved to another. It is never returned, never logged and never\nshown to a visitor of a widget. A server of Widgetic opens it for the one call a widget makes through the\nadapter, uses it to call the provider on your behalf, and discards it. To cut that off, remove the key here, or\nrevoke it with the provider.\n\n**What is checked.** That the adapter exists and takes this credential, that an owner may keep it (some are\nfor a partner only) and that the text is one line of at most 2048 characters. Whether the provider accepts\nthe key is not checked here: it shows the first time a widget uses it, and the widget tells its owner.\n\n**An address that has to be confirmed.** Some credentials are not a key but the email address Widgetic sends a\nwidget's messages to (`confirmation: email_code` in `getUserAdapterConnections`). Saving one checks that it is one\nemail address and emails a six-digit code to it; the answer says `awaitingConfirmation: true`, and nothing is sent\nto the address until the code is given back with `confirmUserAdapterConnection`. Saving the same address again\nbefore it is confirmed emails a new code (not sooner than a minute after the last); saving the address that is\nalready confirmed changes nothing. If the code cannot be emailed, nothing is saved.\n\nA widget whose adapter has no key connected, or whose address is not confirmed yet, is not charged and does not\nrun that adapter: its visitors are told the feature is unavailable, and the owner is told once a day what to do.\n\nThis needs an API key with full access. The caller must be the account itself or a Widgetic administrator. A\nretry is harmless: saving the same key twice only refreshes when it was set.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"user-id","name":"userId","type":"string","required":true,"description":"The account that keeps the key. For your own, your own user id","format":"uuid","in":"path"},{"flag":"adapter-id","name":"adapterId","type":"string","required":true,"description":"The adapter the key is for","maxLength":40,"in":"path"},{"flag":"credential-key","name":"credentialKey","type":"string","required":true,"description":"The key's name within the adapter","maxLength":40,"in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["credential"],"additionalProperties":false,"properties":{"credential":{"type":"string","format":"password","writeOnly":true,"maxLength":2048,"description":"The key exactly as its provider gave it: one line of text, at most 2048 characters. It is stored encrypted and is never returned or logged. For a credential that is an email address (`confirmation: email_code`), write that one address, with nothing else","example":"YOUR_PROVIDER_KEY"}},"example":{"credential":"YOUR_PROVIDER_KEY"}},"flags":[{"flag":"credential","name":"credential","type":"string","required":true,"description":"The key exactly as its provider gave it: one line of text, at most 2048 characters. It is stored encrypted and is never returned or logged. For a credential that is an email address (`confirmation: email_code`), write that one address, with nothing else","maxLength":2048,"format":"password","secret":true,"property":"credential"}]}},{"name":"update-personal-workspace-domain","aliases":[],"operationId":"updatePersonalWorkspaceDomain","method":"PUT","path":"/users/{id}/workspace/domains/{domainId}","server":"api","summary":"Change a website on your list","description":"Replaces the website of one entry on an account's own list. The same rules apply as when adding one (`addPersonalWorkspaceDomain`): a website name such as `example.com` or `*.example.com`, kept in its cleaned form, and refused when it is not a website name or is already on the list.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the list belongs to. For your own, your own user id","format":"uuid","in":"path"},{"flag":"domain-id","name":"domainId","type":"string","required":true,"description":"The `id` of the entry, as the list shows it","format":"uuid","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","description":"The body of a request that changes an entry of a list of allowed websites. The property is `domain`, as when an entry is added: the routes read nothing called `name`.","required":["domain"],"properties":{"domain":{"type":"string","description":"The website name to keep instead, such as `example.com`, or `*.example.com` for a website and all of its subdomains","example":"example.com"}},"example":{"domain":"example.com"}},"flags":[{"flag":"domain","name":"domain","type":"string","required":true,"description":"The website name to keep instead, such as `example.com`, or `*.example.com` for a website and all of its subdomains","property":"domain"}]}},{"name":"update-user-api-key","aliases":[],"operationId":"updateUserApiKey","method":"PUT","path":"/users/{id}/keys/{keyId}","server":"api","summary":"Rename an API key, or switch it off or on","description":"Renames a key, or switches it off (`isActive: false`) or back on. A key that is switched off is refused at once by the server that took the request and within a minute by the others, and can be switched on again; deleting is for a key that is not coming back. The secret, the expiry and the scopes of a key cannot be changed. Only a key with full access can make these requests: a key limited to some scopes is refused with 403 and the code `API_KEY_SCOPE_MISSING`.\nRenaming and switching off can be done with an API key. Switching a key on needs a person signed in on widgetic.com: a request that asks for it with an API key or an SDK token is refused with 403, so a leaked key cannot bring a dormant key back to life.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The account the key belongs to","format":"uuid","in":"path"},{"flag":"key-id","name":"keyId","type":"string","required":true,"description":"The `keyId` of the key, as the list shows it","in":"path"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","description":"At least one of the two is required","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"A new name. It cannot be one that another key of the account already has"},"isActive":{"type":"boolean","description":"false switches the key off, true switches it on again. A key that is switched off stops working within a minute at the latest, and at once on the server that took the request"}},"example":{"name":"Example name","isActive":true}},"flags":[{"flag":"name","name":"name","type":"string","required":false,"description":"A new name. It cannot be one that another key of the account already has","minLength":1,"maxLength":100,"property":"name"},{"flag":"is-active","name":"isActive","type":"boolean","required":false,"description":"false switches the key off, true switches it on again. A key that is switched off stops working within a minute at the latest, and at once on the server that took the request","property":"isActive"}]}}]},{"name":"widgets","title":"Widgets","description":"The Widgets operations of the Widgetic API.","hidden":false,"commands":[{"name":"create-composition-from-widget","aliases":[],"operationId":"createCompositionFromWidget","method":"POST","path":"/widgets/{id}/composition","server":"api","summary":"Create a composition for a widget in one call","description":"After a managed user publishes a widget from the creator, call this with the widget id, the customer's\nmanaged user (when the widget is not already filed under one), and optional content, design, embed settings\nand locale. Every input is validated before a single insert; a failure never leaves a half-made composition.\n\nWhen the widget is already tagged with a managed user, you may omit `managedUserId`. When you send it, it\nmust match that tag.\n\nA widget that keeps what visitors send (a form, a vote, a rating) declares its tables in its own source\n(`src/lib/meta/database.json`); they are made for the composition here, in a schema of its own, as the widget\nwas when it was last published. A widget that keeps nothing leaves the composition without tables. If the\ntables cannot be made just now, or the widget's file cannot be used, the composition is still created (`201`)\nand `meta.warnings` says why; the tables are made the next time the widget is generated.\n\nThe widget must be published. The `Idempotency-Key` header is required.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"id","name":"id","type":"string","required":true,"description":"The widget id","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"content":{"type":"object"},"design":{"type":["object","null"]},"embedSettings":{"type":"object"},"locale":{"type":"string"},"managedUserId":{"type":"string","format":"uuid"}}},"flags":[{"flag":"name","name":"name","type":"string","required":true,"description":"","property":"name"},{"flag":"locale","name":"locale","type":"string","required":false,"description":"","property":"locale"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"","format":"uuid","property":"managedUserId"}]}},{"name":"create-widget","aliases":["create"],"operationId":"createWidget","method":"POST","path":"/widgets","server":"api","summary":"Create Widget","description":"Creates a widget for your account: a named record with a type and some starting content, on a canvas of\nits own (or one of yours that you name). It costs no credits and has no code yet; the code is generated for\nit afterwards. The new widget is yours, and you can list it with `getWidgets`. A widget is a draft until\nyou say otherwise (`isDraft: false`).\n\nIf a step after the widget was saved does not go through, the widget is still created, and\n`data.warnings` says what did not. Read it before relying on the widget.\n\nA widget can be placed on one of your canvases (`canvasId`), in a project you can edit (`projectId`) or in an\norganization where you may create widgets (`organizationId`). Naming anything you cannot use is refused\nwith 403, and the answer is the same whether it exists or not.\n\nTo make the widget for one of your managed users, send `managedUserId` or `managedUserExternalId`. The widget\nstays in your account and is yours to edit; the tag lets you list one user's widgets with `getWidgets`. The\nmanaged user must be yours and active: one that is not yours, or does not exist, is answered with 404 and\n`MANAGED_USER_NOT_FOUND`, and one that is switched off with 409 and `MANAGED_USER_INACTIVE`. The tag is set\nhere and does not change afterwards.\n\nThis call has a limit of its own: 120 per minute for each account (ask Widgetic if you need more). Past\nthat the call is answered with 429 and a `Retry-After`. A widget has no database of its own: what the\nvisitors of a composition leave is kept in the tables of that composition, made when the composition is.\nThe `Idempotency-Key` header is required; send the same key when you retry, so one widget is created, not\ntwo.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"A unique value (a UUID is fine) that makes a retry of this request safe.","in":"header","role":"idempotency-key"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","description":"What a new widget starts from. Only the fields listed here are used. The widget is created without\ncode: it is a record with a name, a type and some starting content, on a canvas (a new one, unless you\nname one of yours). It has no database: what the visitors of a composition leave is kept in the tables\nof that composition.\n","required":["name","type","content"],"properties":{"name":{"type":"string","minLength":1,"description":"Name of the widget.","example":"New Chart Widget"},"type":{"type":"string","minLength":1,"description":"What kind of widget this is, as a label of your choosing (for example chart, form, gallery).","example":"chart"},"content":{"type":"object","description":"Starting content for the widget. An empty object is fine.","additionalProperties":true,"example":{"chartType":"bar","dataSource":"api"}},"isPublic":{"type":"boolean","default":false,"description":"Whether the widget is public.","example":false},"isDraft":{"type":"boolean","default":true,"description":"Whether the widget starts as a draft. A widget created without this is a draft.","example":false},"canvasId":{"type":"string","format":"uuid","description":"Put the widget on this existing canvas instead of a new one. You must own the canvas.","example":"abc12345-def6-7890-ab12-cd3456ef7890"},"projectId":{"type":"string","format":"uuid","description":"Project to file the widget in. You must be able to edit the project.","example":"7c1e2f30-4a5b-4c6d-8e9f-0a1b2c3d4e5f"},"organizationId":{"type":"string","format":"uuid","description":"Organization the widget belongs to. You must be a member who may create widgets there.","example":"5e2d3f4a-1b6c-4a1f-8b9c-3c2d4e5f6a7b"},"workspaceType":{"type":"string","enum":["personal","organization"],"description":"Whether the widget lives in your personal workspace or in an organization's.","example":"personal"},"managedUserId":{"type":"string","format":"uuid","description":"Make the widget for one of your managed users, by Widgetic's id for the user. Send this or `managedUserExternalId`, not both. The managed user must be yours and active. The widget stays in your account; the tag lets you list one user's widgets and keeps it out of the others' lists. It is set here and does not change afterwards.","example":"b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e"},"managedUserExternalId":{"type":"string","maxLength":255,"description":"The same, by your own identifier for the user, the `externalId` you gave when you created the managed user (answers show it as `externalId`). Send this or `managedUserId`, not both.","example":"user-8841"}},"example":{"name":"New Chart Widget","type":"chart","content":{"chartType":"bar","dataSource":"api"},"isPublic":false,"isDraft":false,"canvasId":"abc12345-def6-7890-ab12-cd3456ef7890","projectId":"7c1e2f30-4a5b-4c6d-8e9f-0a1b2c3d4e5f","organizationId":"5e2d3f4a-1b6c-4a1f-8b9c-3c2d4e5f6a7b","workspaceType":"personal","managedUserId":"b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e","managedUserExternalId":"user-8841"}},"flags":[{"flag":"name","name":"name","type":"string","required":true,"description":"Name of the widget.","minLength":1,"property":"name"},{"flag":"type","name":"type","type":"string","required":true,"description":"What kind of widget this is, as a label of your choosing (for example chart, form, gallery).","minLength":1,"property":"type"},{"flag":"is-public","name":"isPublic","type":"boolean","required":false,"description":"Whether the widget is public.","default":false,"property":"isPublic"},{"flag":"is-draft","name":"isDraft","type":"boolean","required":false,"description":"Whether the widget starts as a draft. A widget created without this is a draft.","default":true,"property":"isDraft"},{"flag":"canvas-id","name":"canvasId","type":"string","required":false,"description":"Put the widget on this existing canvas instead of a new one. You must own the canvas.","format":"uuid","property":"canvasId"},{"flag":"project-id","name":"projectId","type":"string","required":false,"description":"Project to file the widget in. You must be able to edit the project.","format":"uuid","property":"projectId"},{"flag":"organization-id","name":"organizationId","type":"string","required":false,"description":"Organization the widget belongs to. You must be a member who may create widgets there.","format":"uuid","property":"organizationId"},{"flag":"workspace-type","name":"workspaceType","type":"string","required":false,"description":"Whether the widget lives in your personal workspace or in an organization's.","enum":["personal","organization"],"property":"workspaceType"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"Make the widget for one of your managed users, by Widgetic's id for the user. Send this or `managedUserExternalId`, not both. The managed user must be yours and active. The widget stays in your account; the tag lets you list one user's widgets and keeps it out of the others' lists. It is set here and does not change afterwards.","format":"uuid","property":"managedUserId"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"The same, by your own identifier for the user, the `externalId` you gave when you created the managed user (answers show it as `externalId`). Send this or `managedUserId`, not both.","maxLength":255,"property":"managedUserExternalId"}]}},{"name":"get-widgets","aliases":["list"],"operationId":"getWidgets","method":"GET","path":"/widgets","server":"api","summary":"List Widgets","description":"Lists the widgets your account owns, newest first. A widget is the reusable piece a composition is made\nfrom; this is your own library, not a catalogue of Widgetic's widgets.\n\nPass `page` (counting from 1) and `size` (1 to 100, 20 when left out) to read the list one page at a time.\n`meta` then carries `total`, `hasNextPage` and `hasPreviousPage`, so a client can walk the list to its end.\nA page past the end is an empty list. Without `page` and `size` the call returns the whole list in one\nresponse, which only suits an account with a few widgets.\n\nPass `canvasId` to list only the widgets on one of your canvases. That list is always returned whole,\nwhatever `page` and `size` say.\n\nPass `managedUserId` (Widgetic's id for one of your managed users) or `managedUserExternalId` (your own\nidentifier for the user) to list only the widgets made for that managed user. The widgets of your other\nmanaged users, and your own, are left out. It combines with `page`, `size` and `canvasId`. A managed user\nthat is not yours, or does not exist, is answered with 404 and `MANAGED_USER_NOT_FOUND`.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"page","name":"page","type":"integer","required":false,"description":"Page to return, counting from 1. Giving `page` or `size` turns paging on.","minimum":1,"in":"query"},{"flag":"size","name":"size","type":"integer","required":false,"description":"How many widgets a page holds (20 when `page` is given without it). Giving `page` or `size` turns paging on.","minimum":1,"maximum":100,"in":"query"},{"flag":"canvas-id","name":"canvasId","type":"string","required":false,"description":"List only the widgets on this canvas. The list is returned whole.","format":"uuid","in":"query"},{"flag":"managed-user-id","name":"managedUserId","type":"string","required":false,"description":"List only the widgets made for this managed user (Widgetic's id for the user). Send this or `managedUserExternalId`, not both.","format":"uuid","in":"query"},{"flag":"managed-user-external-id","name":"managedUserExternalId","type":"string","required":false,"description":"List only the widgets made for the managed user you registered with this `externalId` (your own identifier for the user). Send this or `managedUserId`, not both.","maxLength":255,"in":"query"}]},{"name":"get-widgets-batch","aliases":[],"operationId":"getWidgetsBatch","method":"POST","path":"/widgets/batch","server":"api","summary":"Get Widgets Batch","description":"Retrieves multiple widgets by their IDs in a single request. An id that is not found, or that is not yours, is reported in `errors` exactly like one that does not exist. Called with an editor session, only the widgets made for the session's managed user are returned.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["widgetIds"],"properties":{"widgetIds":{"type":"array","items":{"type":"string"}}}},"flags":[{"flag":"widget-ids","name":"widgetIds","type":"string[]","required":true,"description":"","property":"widgetIds"}]}}]},{"name":"workflows","title":"Workflows","description":"The Workflows operations of the Widgetic API.","hidden":false,"commands":[{"name":"archive-workspace-workflow","aliases":[],"operationId":"archiveWorkspaceWorkflow","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/archive","server":"api","summary":"Archive a Workflow","description":"Takes a workflow out of the default list and stops it serving: its embeds show only their first composition,\nas when paused. It is kept, with its versions and counts, until it is deleted.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can archive.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"create-workspace-workflow","aliases":[],"operationId":"createWorkspaceWorkflow","method":"POST","path":"/workspaces/{workspaceId}/workflows","server":"api","summary":"Create a Workflow","description":"Makes a draft workflow. Give it a composition and the workflow is born with one block, which starts it; without\none it starts empty. A workspace holds up to 500 workflows.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can create one.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":120,"example":"Welcome journey"},"compositionId":{"type":["string","null"],"format":"uuid","description":"A composition of this workspace to start with."},"timezone":{"type":["string","null"],"description":"An IANA time zone name; UTC when not given.","example":"Europe/Bucharest"}}},"flags":[{"flag":"name","name":"name","type":"string","required":true,"description":"","minLength":1,"maxLength":120,"property":"name"},{"flag":"composition-id","name":"compositionId","type":"string","required":false,"description":"A composition of this workspace to start with.","format":"uuid","property":"compositionId"},{"flag":"timezone","name":"timezone","type":"string","required":false,"description":"An IANA time zone name; UTC when not given.","property":"timezone"}]}},{"name":"create-workspace-workflow-from-preset","aliases":[],"operationId":"createWorkspaceWorkflowFromPreset","method":"POST","path":"/workspaces/{workspaceId}/workflows/from-preset","server":"api","summary":"Start a Workflow from a starting point","description":"Makes your own copy of a starting point (`listWorkspaceWorkflowPresets`): a copy of every widget it chains (an\nordinary private widget that serves the starting point's published code), a composition of your own on each copy\n(a draft, with the content, design, embed settings and tables of the original), and a draft workflow wired like the\nstarting point's live version. The workflow is named as you ask, or after the starting point. Nothing you do to the\ncopy reaches the starting point, and nothing its offerer changes afterwards reaches your copy. The workflow stays a\ndraft: publish it when it is ready.\n\nA start takes a few seconds, because each widget is copied. Send an `Idempotency-Key` so that asking again after\na timeout makes one copy. When a start fails (503 with `details.reason` `preset_copy_failed`) the compositions it\nmade are removed again, so asking again is safe; the widget copies it made stay in the workspace as ordinary\nwidgets. The workspace's limit of workflows is checked before anything is copied.\n\nA starting point that is not offered to you, or that can no longer be started, is 404.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can start one.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","additionalProperties":false,"required":["key"],"properties":{"key":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","description":"The starting point, as `listWorkspaceWorkflowPresets` gives it.","example":"welcome-then-offer"},"name":{"type":["string","null"],"minLength":1,"maxLength":120,"description":"The new workflow's name. The starting point's title when left out."}}},"flags":[{"flag":"key","name":"key","type":"string","required":true,"description":"The starting point, as `listWorkspaceWorkflowPresets` gives it.","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","property":"key"},{"flag":"name","name":"name","type":"string","required":false,"description":"The new workflow's name. The starting point's title when left out.","minLength":1,"maxLength":120,"property":"name"}]}},{"name":"delete-workspace-workflow","aliases":[],"operationId":"deleteWorkspaceWorkflow","method":"DELETE","path":"/workspaces/{workspaceId}/workflows/{workflowId}","server":"api","summary":"Delete a Workflow","description":"Deletes a workflow, in whatever state it is in (draft, live, paused or archived), with all its versions and\ncounts. It cannot be undone: archive the workflow instead to keep it. The compositions it chained are not\ntouched, and a page whose embed still names the workflow shows the embed's first composition, as for a paused\none. Asking for a workflow that is already gone is \"not found\".\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can delete.","accessLevel":"user","auth":true,"hidden":false,"confirm":true,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"discard-workspace-workflow-changes","aliases":[],"operationId":"discardWorkspaceWorkflowChanges","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/discard","server":"api","summary":"Discard the Unpublished Changes of a Workflow","description":"Puts the draft back to the live version (its graph and its time zone). Only for a workflow that has been\npublished; one that never was has no live version to go back to. Names the `revision` the editor read.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can discard.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["revision"],"properties":{"revision":{"type":"integer","minimum":1}}},"flags":[{"flag":"revision","name":"revision","type":"integer","required":true,"description":"","minimum":1,"property":"revision"}]}},{"name":"duplicate-workspace-workflow","aliases":[],"operationId":"duplicateWorkspaceWorkflow","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/duplicate","server":"api","summary":"Duplicate a Workflow","description":"Makes a draft copy of a workflow (its draft graph, not its versions or counts), named \"<name> (copy)\".\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can duplicate.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-workspace-workflow","aliases":[],"operationId":"getWorkspaceWorkflow","method":"GET","path":"/workspaces/{workspaceId}/workflows/{workflowId}","server":"api","summary":"Get a Workflow","description":"Reads a workflow with its draft graph and the version that is live.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can read it.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-workspace-workflow-stats","aliases":[],"operationId":"getWorkspaceWorkflowStats","method":"GET","path":"/workspaces/{workspaceId}/workflows/{workflowId}/stats","server":"api","summary":"Get what Visitors Did in a Workflow","description":"Counts per block and per connection over a range of UTC days (the last 30 days when not given, up to 366), for one\nversion or for all of them. Only counts: how many visitors reached a block, took a connection, or could not be\nmoved on. No visitor and no value a visitor typed is ever stored.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can read.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"version-id","name":"versionId","type":"string","required":false,"description":"Count one version only","format":"uuid","in":"query"},{"flag":"from","name":"from","type":"string","required":false,"description":"First UTC day (YYYY-MM-DD)","in":"query"},{"flag":"to","name":"to","type":"string","required":false,"description":"Last UTC day (YYYY-MM-DD)","in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"get-workspace-workflow-version","aliases":[],"operationId":"getWorkspaceWorkflowVersion","method":"GET","path":"/workspaces/{workspaceId}/workflows/{workflowId}/versions/{versionId}","server":"api","summary":"Get a Published Version of a Workflow","description":"One published version with its graph, for the read-only preview and for the Runs view.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can read.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"version-id","name":"versionId","type":"string","required":true,"description":"The id of a published version of the workflow","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"list-workspace-workflow-composition-options","aliases":[],"operationId":"listWorkspaceWorkflowCompositionOptions","method":"GET","path":"/workspaces/{workspaceId}/workflows/composition-options","server":"api","summary":"List the compositions a workflow can show, and what each can do","description":"The workspace's compositions as the workflow editor offers them, newest first. For each one: whether it can be\nshown (its widget has a published version), the websites it may be embedded on, and which triggers it supports.\n\n- **Input added** (`inputs`) is enabled only for a composition that declares at least one table a visitor's\n entries can be written to. Its `tables` are all of them; its `fields` are the columns a rule may read (not\n `id` and `created_at`, not a column that is not text, a number or yes/no, and not one whose name says it holds\n a person's private details, such as an email or a phone number).\n- **Action in the widget** (`actions`) is enabled only when the published widget version declares at least one\n goal. Its `items` are the goals a trigger can wait for.\n\nWhen one of them is not enabled, `reason` says why (`widget_keeps_no_data`, `widget_reports_no_actions` or\n`widget_not_published`), so the editor can show the trigger disabled with an explanation. Publishing checks every\nreference again against what the composition can do at that moment.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can read it.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"search","name":"search","type":"string","required":false,"description":"Text the composition's name must contain","maxLength":120,"in":"query"},{"flag":"ids","name":"ids","type":"string","required":false,"description":"Only these compositions (comma-separated ids, at most 50), those of them you may see. Ask for the\ncompositions a workflow shows with this, so each is found whatever page it would be on. An id you may not\nsee is left out, as one that does not exist is.","in":"query"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"Page size (1-100, 50 when not given)","in":"query"},{"flag":"offset","name":"offset","type":"integer","required":false,"description":"How many to skip (0 when not given)","in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"list-workspace-workflow-presets","aliases":[],"operationId":"listWorkspaceWorkflowPresets","method":"GET","path":"/workspaces/{workspaceId}/workflows/presets","server":"api","summary":"List the starting points for a new Workflow","description":"The workflows you can start from in this workspace: published workflows that Widgetic offers or, for one of a\npartner's managed users, that the partner offers (a managed user is never shown Widgetic's, and a person is never\nshown a partner's). Only what can be started now is listed, in the order the offerer set (smallest `position`\nfirst, then by `key`). Each shows what you would get, and none shows who made it or any id of its own.\nStart from one with `createWorkspaceWorkflowFromPreset`.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can read the list.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"list-workspace-workflow-versions","aliases":[],"operationId":"listWorkspaceWorkflowVersions","method":"GET","path":"/workspaces/{workspaceId}/workflows/{workflowId}/versions","server":"api","summary":"List the Published Versions of a Workflow","description":"Every version the workflow has had, newest first, with who published it and when. The one visitors get is marked\n`isLive`.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can read.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"list-workspace-workflows","aliases":[],"operationId":"listWorkspaceWorkflows","method":"GET","path":"/workspaces/{workspaceId}/workflows","server":"api","summary":"List the Workflows of a Workspace","description":"Lists the workspace's workflows, newest first unless `sort` says otherwise. Archived workflows are left out\nunless `status` asks for them (`archived`, or `all`).\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can read the list.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"search","name":"search","type":"string","required":false,"description":"Text the workflow's name must contain","maxLength":120,"in":"query"},{"flag":"status","name":"status","type":"string","required":false,"description":"One state to list (`draft`, `live`, `paused`, `archived`), or `all` for every state. Without it, the ones in use (draft, live and paused).","enum":["draft","live","paused","archived","all"],"in":"query"},{"flag":"sort","name":"sort","type":"string","required":false,"description":"`<field>:<asc|desc>` where field is createdAt, lastPublishedAt, runsCount or name (or just asc / desc for createdAt). Newest first when not given.","in":"query"},{"flag":"limit","name":"limit","type":"integer","required":false,"description":"Page size (1-100, 50 when not given)","in":"query"},{"flag":"offset","name":"offset","type":"integer","required":false,"description":"How many to skip (0 when not given)","in":"query"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"pause-workspace-workflow","aliases":[],"operationId":"pauseWorkspaceWorkflow","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/pause","server":"api","summary":"Pause a Workflow","description":"While a workflow is paused, its embeds show only their first composition (the `composition-id` of the embed) and no\nvisitor moves on. The live version is kept, and visitors partway through carry on from where they were when it is\nresumed. Only a live workflow can be paused.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can pause.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"publish-workspace-workflow","aliases":[],"operationId":"publishWorkspaceWorkflow","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/publish","server":"api","summary":"Publish the Draft of a Workflow","description":"Checks the draft and, when nothing stops it, makes it the next version and the one new visitors get. Versions never\nchange once published. A visitor already partway through keeps the version they started on (for up to 30 days).\nThe check reads what each composition can do right now (a table the visitor adds to, a goal the widget reports,\nthe websites it may be embedded on), so a workflow never goes live pointing at something that has gone.\nIt is refused with 422 (`BUSINESS_OPERATION_INVALID`) when the draft has errors; the `issues` in the error's\n`details` name the blocks to fix: an unconnected trigger, a condition without rules, a composition that is not\npublished, a field or action that is gone, a composition whose allowed websites exclude the site the workflow\nstarts on. Nothing is published when the draft has no changes since the live version.\nThe `revision` is the one the editor read, and a publish of a draft that has moved on is refused with 409.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can publish.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"idempotency-key","name":"Idempotency-Key","type":"string","required":true,"description":"Required: a call without it is refused with `400`. A key of your own that names this one request; a UUID is ideal. It is 8 to 50 characters of letters, digits, `-` and `_`. Send the same key again when you retry (after a timeout, a dropped connection or a `5xx`) and the call is carried out once: the retry gets the first answer back, marked `X-Idempotency-Cached: true`. The same key with a different body is refused with `422`, and a retry that arrives while the first call is still running with `409` (see `Retry-After`).","minLength":8,"maxLength":50,"pattern":"^[a-zA-Z0-9_-]{8,50}$","in":"header","role":"idempotency-key"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["revision"],"properties":{"revision":{"type":"integer","minimum":1}}},"flags":[{"flag":"revision","name":"revision","type":"integer","required":true,"description":"","minimum":1,"property":"revision"}]}},{"name":"restore-workspace-workflow-version","aliases":[],"operationId":"restoreWorkspaceWorkflowVersion","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/versions/{versionId}/restore","server":"api","summary":"Restore a Version of a Workflow into the Draft","description":"Copies a published version into the draft, so it can be changed and published again. Nothing is published and\nnothing visitors see changes: the version stays in the history as it was. Names the `revision` the editor read.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can restore.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"version-id","name":"versionId","type":"string","required":true,"description":"The id of a published version of the workflow","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["revision"],"properties":{"revision":{"type":"integer","minimum":1}}},"flags":[{"flag":"revision","name":"revision","type":"integer","required":true,"description":"","minimum":1,"property":"revision"}]}},{"name":"resume-workspace-workflow","aliases":[],"operationId":"resumeWorkspaceWorkflow","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/resume","server":"api","summary":"Resume a Paused Workflow","description":"Makes a paused workflow live again, on the version that was live when it was paused.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can resume.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]},{"name":"save-workspace-workflow","aliases":[],"operationId":"saveWorkspaceWorkflow","method":"PUT","path":"/workspaces/{workspaceId}/workflows/{workflowId}","server":"api","summary":"Save the Draft of a Workflow","description":"Saves the name, the time zone and/or the graph of the draft. Send at least one. The save names the `revision` the\neditor started from and is refused with 409 (`BUSINESS_STATE_CONFLICT`) when the draft has moved on, so two\npeople editing do not overwrite each other without knowing; read the workflow again and apply the change to it.\nA save is never refused for what is wrong with the graph (a block with no connection yet is normal in a draft):\nonly the shape is judged here, and what stops a workflow going live is told when it is published. A graph larger\nthan 200,000 bytes, or with more than 100 blocks or 300 connections, is refused.\nAn archived workflow cannot be saved. Saving a live workflow changes the draft only: visitors keep getting the\npublished version until the changes are published.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can save.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}],"body":{"required":true,"kind":"json","contentType":"application/json","schema":{"type":"object","required":["revision"],"properties":{"revision":{"type":"integer","minimum":1,"description":"The revision the editor read."},"name":{"type":"string","minLength":1,"maxLength":120},"timezone":{"type":"string","example":"Europe/Bucharest"},"graph":{"$ref":"#/$defs/WorkflowGraph"}},"$defs":{"WorkflowConditionCase":{"type":"object","description":"One rule of a condition block, and one path out of it. Rules are read in order and the first that matches wins;\nthe block's Default output is taken when none does.\n","required":["id","match","tests"],"properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$","description":"The output port of the rule. `default` is reserved for the Default output."},"label":{"type":"string"},"match":{"type":"string","enum":["all","any"],"description":"`all` - every comparison must hold; `any` - one is enough."},"tests":{"type":"array","items":{"$ref":"#/$defs/WorkflowConditionTest"}}},"example":{"id":"Example id","label":"Example label","match":"Example match","tests":["Example WorkflowConditionTest"]}},"WorkflowConditionTest":{"type":"object","description":"One comparison of a rule - a field, an operator and (for most operators) a value to compare with.","required":["field","operator"],"properties":{"field":{"$ref":"#/$defs/WorkflowFieldReference"},"operator":{"type":"string","enum":["is","isNot","contains","doesNotContain","startsWith","endsWith","greaterThan","atLeast","lessThan","atMost","isEmpty","isNotEmpty"],"description":"Text fields take is, isNot, contains, doesNotContain, startsWith, endsWith, isEmpty and isNotEmpty.\nNumber fields take is, isNot, greaterThan, atLeast, lessThan, atMost, isEmpty and isNotEmpty.\nYes/no fields take is and isNot.\n"},"value":{"description":"What the field is compared with; not used by isEmpty and isNotEmpty. A text for a text field, a number for\na number field, true or false for a yes/no field.\n"}},"example":{"field":null,"operator":"Example operator","value":null}},"WorkflowEdge":{"type":"object","description":"A connection from an output port of one block to the input of another. Connections run forward only.","required":["id","source","sourcePort","target"],"properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"source":{"type":"string"},"sourcePort":{"type":"string","description":"`out` for compositions and triggers; a rule's id, or `default`, for conditions."},"target":{"type":"string"}},"example":{"id":"Example id","source":"Example source","sourcePort":"Example sourcePort","target":"Example target"}},"WorkflowFieldReference":{"type":"object","description":"A value a rule reads: a column the visitor filled in (`input`) or a value that came with a page event\n(`event`). The type is saved with the reference so the graph can be evaluated without the widget's schema.\n","required":["kind","name","type"],"properties":{"kind":{"type":"string","enum":["input","event"]},"name":{"type":"string"},"type":{"type":"string","enum":["text","number","boolean"]}},"example":{"kind":"Example kind","name":"Example name","type":"Example type"}},"WorkflowGraph":{"type":"object","description":"A whole workflow, as the editor saves it and the gateway publishes it.","required":["schemaVersion","nodes","edges"],"properties":{"schemaVersion":{"type":"integer","enum":[1]},"nodes":{"type":"array","maxItems":100,"items":{"$ref":"#/$defs/WorkflowNode"}},"edges":{"type":"array","maxItems":300,"items":{"$ref":"#/$defs/WorkflowEdge"}}},"example":{"schemaVersion":42,"nodes":["Example WorkflowNode"],"edges":["Example WorkflowEdge"]}},"WorkflowNode":{"type":"object","description":"A block. A `composition` block shows one composition (exactly one block of a workflow has `startsHere`);\na `trigger` block waits for one thing to happen while the composition before it is shown; a `condition`\nblock chooses the next step from what the trigger before it carried. At most 25 blocks.\n","required":["id","type"],"properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$"},"type":{"type":"string","enum":["composition","trigger","condition"]},"position":{"$ref":"#/$defs/WorkflowNodePosition"},"compositionId":{"type":"string","format":"uuid","description":"The composition a `composition` block shows."},"startsHere":{"type":"boolean","description":"Set on the one composition block a visitor starts at."},"trigger":{"$ref":"#/$defs/WorkflowTrigger"},"cases":{"type":"array","description":"The rules of a `condition` block, in the order they are read.","items":{"$ref":"#/$defs/WorkflowConditionCase"}}},"example":{"id":"Example id","type":"Example type","position":null,"compositionId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","startsHere":true,"trigger":null,"cases":["Example WorkflowConditionCase"]}},"WorkflowNodePosition":{"type":"object","description":"Where a block sits on the editor's canvas. Embeds never read it.","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"example":{"x":42,"y":42}},"WorkflowSchedule":{"type":"object","description":"When a Scheduled trigger fires - a wait counted from reaching the step, or a fixed moment.","required":["mode"],"properties":{"mode":{"type":"string","enum":["after","at"]},"amount":{"type":"number","description":"How long to wait (mode `after`)."},"unit":{"type":"string","enum":["seconds","minutes","hours","days","weeks"],"description":"The unit of `amount` (mode `after`)."},"at":{"type":"string","format":"date-time","description":"The moment to fire at, as an ISO time in UTC (mode `at`)."}},"example":{"mode":"Example mode","amount":42,"unit":"Example unit","at":"2026-01-15T09:30:00.000Z"}},"WorkflowTrigger":{"type":"object","description":"What a trigger block waits for. `input`: a row the visitor adds to a table of the composition (`table`).\n`action`: an action the widget reports (`actionId`). `pageEvent`: an event the page sends (`name`, and the\n`values` it promises to carry). `scheduled`: a wait or a date (`schedule`).\n","required":["kind"],"properties":{"kind":{"type":"string","enum":["input","action","pageEvent","scheduled"]},"table":{"type":"string"},"actionId":{"type":"string"},"name":{"type":"string"},"values":{"type":"array","items":{"type":"object","required":["name","type"],"properties":{"name":{"type":"string"},"type":{"type":"string","enum":["text","number","boolean"]}}}},"schedule":{"$ref":"#/$defs/WorkflowSchedule"}},"example":{"kind":"Example kind","table":"Example table","actionId":"Example actionId","name":"Example name","values":[],"schedule":null}}}},"flags":[{"flag":"revision","name":"revision","type":"integer","required":true,"description":"The revision the editor read.","minimum":1,"property":"revision"},{"flag":"name","name":"name","type":"string","required":false,"description":"","minLength":1,"maxLength":120,"property":"name"},{"flag":"timezone","name":"timezone","type":"string","required":false,"description":"","property":"timezone"}]}},{"name":"unarchive-workspace-workflow","aliases":[],"operationId":"unarchiveWorkspaceWorkflow","method":"POST","path":"/workspaces/{workspaceId}/workflows/{workflowId}/unarchive","server":"api","summary":"Unarchive a Workflow","description":"Brings an archived workflow back into the list: as paused when it was ever published (resume it to serve visitors\nagain), as a draft when it never was.\nFor personal workspace, use 'personal' as workspaceId.\nFor organization workspace, use the organization UUID as workspaceId. Any active member can unarchive.","accessLevel":"user","auth":true,"hidden":false,"confirm":false,"parameters":[{"flag":"workspace-id","name":"workspaceId","type":"string","required":true,"description":"The workspace: 'personal' for your own workspace, or the UUID of an organization. A call made for one of your\nmanaged users (`X-Managed-User-Id`) always uses 'personal': a managed user has no other workspace, and a call\nthat names an organization is refused with 403.","in":"path"},{"flag":"workflow-id","name":"workflowId","type":"string","required":true,"description":"The workflow's id","format":"uuid","in":"path"},{"flag":"managed-user-id","name":"X-Managed-User-Id","type":"string","required":false,"description":"The id Widgetic gave the managed user (`POST /managed-users`). Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","format":"uuid","in":"header","role":"managed-user-id"},{"flag":"managed-user-external-id","name":"X-Managed-User-External-Id","type":"string","required":false,"description":"Your own id for the managed user (its `externalId`), when you do not keep Widgetic's. Makes this call for one of your managed users, so that a mistake in your own code cannot reach another customer's work. Send this header, or the other one, never both. Left out, the call works on your whole account, as it always has. With it, the call reaches only that user's compositions and workflows: a list shows only theirs, a composition or a workflow the call makes is filed under them, and one of another user, or one that does not exist, answers `404` (the same answer, so the call cannot be used to find out what another customer has). A managed user that is not yours answers `404` with `MANAGED_USER_NOT_FOUND`. An empty, repeated or malformed header is `400` with `MANAGED_USER_SCOPE_INVALID`; an empty header is never read as no header. A call that is about the whole account, such as the list of your managed users, is refused with `403` and `MANAGED_USER_SCOPE_ROUTE_NOT_AVAILABLE`: send it without the header. Only your API key can send it. The header is a limit the call asks for: whoever holds the key can leave it out, so for a credential that a customer's browser holds, use an editor session. An `Idempotency-Key` is kept apart for each managed user.","minLength":1,"maxLength":255,"in":"header","role":"managed-user-external-id"}]}]}]}
|