{"openapi":"3.1.0","info":{"title":"DiffDeck API","version":"1.0.0","description":"REST API exposing the actions of the DiffDeck product. Authenticate with a Kavaro API key as a bearer token."},"servers":[{"url":"https://diffdeck.ai"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"http","scheme":"bearer","description":"A Kavaro API key (user- or organization-scoped)."}}},"security":[{"ApiKeyAuth":[]}],"paths":{"/api/rest/actions/list_repos":{"post":{"operationId":"list_repos","summary":"List repositories","description":"List the organization's connected repositories (id, full name, default branch). Use this to find a repository's id.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["repos:view"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/list_github_repos":{"post":{"operationId":"list_github_repos","summary":"List GitHub repositories","description":"List the repositories the connected GitHub account can access, so the user can pick which ones to add. Requires a connected GitHub account.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["repos:manage"],"x-read-only":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/list_builds":{"post":{"operationId":"list_builds","summary":"List builds","description":"List a repository's published Storybook builds (id, number, product key, branch, commit, status, diff count, review state). Newest first. A monorepo can have several products (each with its own builds and baselines); pass productKey to list only one product's builds.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["builds:view"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"repoId":{"type":"string","description":"The repository whose builds to list"},"productKey":{"description":"Only this monorepo product's builds; omit for all products","type":"string"},"limit":{"description":"Max builds to return; defaults to 25","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["repoId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/get_build":{"post":{"operationId":"get_build","summary":"Inspect build","description":"Get one build in full — its metadata and every story screenshot with its variant, diff state and diff ratio. Use this to see what changed in a build.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["builds:view"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"buildId":{"type":"string","description":"The build to inspect"}},"required":["buildId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/connect_repo":{"post":{"operationId":"connect_repo","summary":"Add repository","description":"Register a GitHub repository so it can publish Storybook builds. Returns a one-time CI project token to store as the UI_REVIEW_TOKEN secret. Pass the repository's full name (owner/repo).","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["repos:manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"fullName":{"type":"string","minLength":1,"description":"The repository full name, e.g. 'acme/widgets'"},"defaultBranch":{"description":"The default/base branch used as the baseline source; defaults to 'main'","type":"string"}},"required":["fullName"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/rotate_repo_token":{"post":{"operationId":"rotate_repo_token","summary":"Rotate project token","description":"Generate a new CI project token for a repository, invalidating the old one. Returns the new token once.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["repos:manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"repoId":{"type":"string","description":"The repository whose token to rotate"}},"required":["repoId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/remove_repo":{"post":{"operationId":"remove_repo","summary":"Remove repository","description":"Remove a repository and all of its builds and screenshots.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["repos:manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"repoId":{"type":"string","description":"The repository to remove"}},"required":["repoId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/set_repo_capture":{"post":{"operationId":"set_repo_capture","summary":"Set capture settings","description":"Configure which of the six screenshot variants (theme × device) a repository product captures (settings are per monorepo product; omit productKey for the default product), so CI stops screenshotting variants the target doesn't support (e.g. no dark mode, or not responsive). Pass a subset of themes and/or devices to override; omit both (or set resetToAuto) to derive the matrix from the auto-detected capabilities. Set redetect to re-run capability auto-detection on the next full build (temporarily captures all six again).","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["repos:manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"repoId":{"type":"string","description":"The repository to configure"},"productKey":{"description":"Monorepo product key within the repository (e.g. 'web-app'); omit for the repo's default product","type":"string"},"themes":{"description":"Themes to capture (subset of light/dark). Omit to derive from detection.","type":"array","items":{"type":"string","enum":["light","dark"]}},"devices":{"description":"Devices to capture (subset of phone/tablet/desktop). Omit to derive from detection.","type":"array","items":{"type":"string","enum":["phone","tablet","desktop"]}},"resetToAuto":{"description":"Clear the theme/device override so capture derives from the auto-detected capabilities.","type":"boolean"},"redetect":{"description":"Re-arm capability auto-detection: the next full build re-measures dark-mode support and responsiveness.","type":"boolean"}},"required":["repoId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/set_org_capture":{"post":{"operationId":"set_org_capture","summary":"Set org capture default","description":"Set the organization-level default capture matrix (which of the six screenshot variants — theme × device — every repository product captures unless that product overrides it). Pass a subset of themes and/or devices; omit both (or set resetToAuto) to clear the default so each product derives its matrix from its own auto-detected capabilities. Optionally hide or re-show the auto-detected capabilities notice on the repository capture cards (dismissDetectionNotice / showDetectionNotice).","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["repos:manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"themes":{"description":"Org-default themes to capture (subset of light/dark). Omit to leave the theme default unchanged.","type":"array","items":{"type":"string","enum":["light","dark"]}},"devices":{"description":"Org-default devices to capture (subset of phone/tablet/desktop). Omit to leave the device default unchanged.","type":"array","items":{"type":"string","enum":["phone","tablet","desktop"]}},"resetToAuto":{"description":"Clear the org default so capture derives from each product's auto-detected capabilities.","type":"boolean"},"dismissDetectionNotice":{"description":"Permanently hide the auto-detected capabilities notice on the repository capture cards for the whole org.","type":"boolean"},"showDetectionNotice":{"description":"Re-show the auto-detected capabilities notice on the repository capture cards for the whole org.","type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/approve_build":{"post":{"operationId":"approve_build","summary":"Approve build","description":"Approve a build, accepting its screenshots as the new baseline for its branch. Future builds on that branch (and PRs targeting it) diff against this one.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["builds:review"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"buildId":{"type":"string","description":"The build to approve"}},"required":["buildId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/reject_build":{"post":{"operationId":"reject_build","summary":"Reject build","description":"Reject a build, marking its visual changes as not accepted. Does not change the baseline.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["builds:review"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"buildId":{"type":"string","description":"The build to reject"}},"required":["buildId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/review_screenshot":{"post":{"operationId":"review_screenshot","summary":"Review screenshot","description":"Approve or reject a single screenshot in a build. Rejecting may carry a comment; when the build's commit is linked to a GitHub pull request the comment is posted back to that PR. Rejecting a screenshot does NOT finalize the build — the reviewer can keep reviewing/commenting the remaining screens. Once every changed/new screenshot is approved the build becomes approved (its screenshots become the branch baseline); finalizing a build as rejected is a separate action (reject_build).","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["builds:review"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"screenshotId":{"type":"string","description":"The screenshot to review"},"state":{"type":"string","enum":["approved","rejected"],"description":"The review decision for this screenshot"},"comment":{"description":"Optional reviewer comment (surfaced on the screenshot; fed back to the PR on rejection)","type":"string"}},"required":["screenshotId","state"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/approve_all_screenshots":{"post":{"operationId":"approve_all_screenshots","summary":"Approve all screenshots","description":"Approve every screenshot in a build in one step and approve the build, accepting its screenshots as the new branch baseline.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["builds:review"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"buildId":{"type":"string","description":"The build whose screenshots to approve"}},"required":["buildId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/approve_story_screenshots":{"post":{"operationId":"approve_story_screenshots","summary":"Approve all screens in a story","description":"Approve every changed/new screenshot for ONE story (all its theme × device variants) in a build in a single step, instead of deciding each variant individually. Does NOT finalize the whole build — the other stories still need review; the build becomes approved (its screenshots become the branch baseline) only once every changed/new screen across all stories is approved.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["builds:review"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"buildId":{"type":"string","description":"The build the story belongs to"},"storyId":{"type":"string","description":"The Storybook story id whose screenshots to approve"}},"required":["buildId","storyId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/enable_recordings":{"post":{"operationId":"enable_recordings","summary":"Enable recordings","description":"Enable Playwright recordings for a repository so CI can upload test-run videos. This is a separately-priced add-on (per repository) — enabling it starts the charge. Returns the existing enrollment if already enabled.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["recordings:manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"repoId":{"type":"string","description":"The repository to enable recordings for"}},"required":["repoId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/disable_recordings":{"post":{"operationId":"disable_recordings","summary":"Disable recordings","description":"Disable Playwright recordings for a repository (stops the charge). Existing recordings are kept but CI uploads are rejected until re-enabled.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["recordings:manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"repoId":{"type":"string","description":"The repository to disable recordings for"}},"required":["repoId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/list_recordings":{"post":{"operationId":"list_recordings","summary":"List recordings","description":"List a repository's Playwright recordings (id, title, linked test, status, duration, created time). Newest first.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["recordings:view"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"repoId":{"type":"string","description":"The repository whose recordings to list"},"productKey":{"description":"Only this monorepo product's recordings; omit for all products","type":"string"},"limit":{"description":"Max recordings to return; defaults to 50","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["repoId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/get_recording":{"post":{"operationId":"get_recording","summary":"Inspect recording","description":"Get one Playwright recording in full — its metadata, linked test details and the video URL.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["recordings:view"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"recordingId":{"type":"string","description":"The recording to inspect"}},"required":["recordingId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/delete_recording":{"post":{"operationId":"delete_recording","summary":"Delete recording","description":"Delete a Playwright recording and its stored video.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["recordings:manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"recordingId":{"type":"string","description":"The recording to delete"}},"required":["recordingId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}},"/api/rest/actions/set_dynamic_regions":{"post":{"operationId":"set_dynamic_regions","summary":"Set dynamic regions","description":"Replace the dynamic (ignored) regions for a story variant. Diffs inside these rectangles are disregarded, so animated/clock/random content does not flag as a change. Regions are fractional rectangles (0..1) and stick across future builds of the same repository product.","tags":["actions"],"security":[{"ApiKeyAuth":[]}],"x-required-permissions":["builds:review"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"repoId":{"type":"string","description":"The repository the story belongs to"},"productKey":{"description":"Monorepo product key within the repository (e.g. 'web-app'); omit for the repo's default product","type":"string"},"storyId":{"type":"string","description":"The Storybook story id"},"theme":{"type":"string","enum":["light","dark"],"description":"Which colour scheme the regions apply to"},"device":{"type":"string","enum":["phone","tablet","desktop"],"description":"Which device size the regions apply to"},"regions":{"type":"array","items":{"type":"object","properties":{"x":{"type":"number","minimum":0,"maximum":1,"description":"Left edge as a fraction (0..1) of the image width"},"y":{"type":"number","minimum":0,"maximum":1,"description":"Top edge as a fraction (0..1) of the image height"},"width":{"type":"number","minimum":0,"maximum":1,"description":"Width as a fraction (0..1) of the image width"},"height":{"type":"number","minimum":0,"maximum":1,"description":"Height as a fraction (0..1) of the image height"}},"required":["x","y","width","height"],"additionalProperties":false},"description":"The masked rectangles (replaces the current set; pass [] to clear)"}},"required":["repoId","storyId","theme","device","regions"],"additionalProperties":false}}}},"responses":{"200":{"description":"Action result","content":{"application/json":{"schema":{"type":"object","properties":{"result":{}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Missing or invalid API key"},"403":{"description":"The key may not invoke this action"},"404":{"description":"Unknown action"}}}}}}