Every procedure under the project, ordered by lastRunAt descending then createdAt descending, so the most recently exercised ones come first. Returns whole rows, including stepsJson with every saved secret masked as ***, plus a derived expectation. Used by testorim list --project <id> and by testorim init to build its alias map.
Format (services/api-keys.ts): the literal prefix tst_live_
followed by 24 random bytes rendered as 32 base64url characters.
Only the SHA-256 hash is stored server-side. The shape check that
routes a token down the API-key path rather than the Clerk JWT path
requires the tst_live_ prefix and a total length of at least 25
characters.
Keys are minted in the dashboard at /settings/api-keys, in any
workspace, personal included. The same header also accepts a Clerk
session JWT, which is how the web app authenticates, but the JWT path
is out of scope for this document.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Testorim","description":"Documentation for Testorim: plain-English browser QA tests that run in a real browser.","colors":{"primary":"#059669","light":"#10b981","dark":"#047857"},"logo":{"light":"https://mintcdn.com/fulgic/ZOzvtyg298rAaunK/logo/wordmark.png?fit=max\u0026auto=format\u0026n=ZOzvtyg298rAaunK\u0026q=85\u0026s=c49b84c50b62f2ccafcc2660391619e7","dark":"https://mintcdn.com/fulgic/ZOzvtyg298rAaunK/logo/wordmark-dark.png?fit=max\u0026auto=format\u0026n=ZOzvtyg298rAaunK\u0026q=85\u0026s=d74e70ecf0f91872956e46385b5665e1","href":"https://testorim.com"},"favicon":"https://mintcdn.com/fulgic/MOzJBi6Mn9MbYgbw/favicon.png?fit=max\u0026auto=format\u0026n=MOzJBi6Mn9MbYgbw\u0026q=85\u0026s=783a1618974414f0ee72cf911352ab64","appearance":{"default":"system"},"navbar":{"links":[{"href":"https://status.testorim.com","label":"Status"},{"href":"mailto:info@fulgic.com","label":"Support"}],"primary":{"type":"button","label":"Open Testorim","href":"https://app.testorim.com"}},"footer":{"links":[{"header":"Product","items":[{"label":"Testorim","href":"https://testorim.com"},{"label":"Pricing","href":"https://testorim.com/pricing"},{"label":"Open the app","href":"https://app.testorim.com"},{"label":"Status","href":"https://status.testorim.com"}]},{"header":"Documentation","items":[{"label":"Quickstart","href":"https://docs.testorim.com/getting-started/quickstart"},{"label":"Plans and limits","href":"https://docs.testorim.com/resources/plans-and-limits"},{"label":"API reference","href":"https://docs.testorim.com/api-reference/introduction"},{"label":"CLI","href":"https://docs.testorim.com/developers/cli"}]},{"header":"Legal","items":[{"label":"Terms","href":"https://testorim.com/terms"},{"label":"Privacy","href":"https://testorim.com/privacy"}]}]},"search":{"prompt":"Search the Testorim docs"},"icons":{"library":"lucide"},"styling":{"latex":false},"navigation":{"global":{"anchors":[{"anchor":"Testorim app","icon":"monitor","href":"https://app.testorim.com"},{"anchor":"Website","icon":"globe","href":"https://testorim.com"}]},"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"fulgic","hasAudioNarration":false,"actualSubdomain":"fulgic","customDomains":["docs.testorim.com"],"gitSource":{"type":"github","owner":"sallar-ba","repo":"testorim-docs","deployBranch":"main","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"entitlements":{"AI_CHAT":{"status":"DISABLED"},"WEB_MCP":{"status":"DISABLED"},"NARRATION":{"status":"DISABLED"}},"buildId":"6abb3a2d9ae82eb23a513b7a","clientVersion":"0.0.4094","searchFilterCounts":[{"language":"default","tag":"Documentation","count":29},{"language":"default","tag":"API reference","count":19},{"language":"default","tag":"Endpoints","count":17},{"language":"default","tag":"Runs","count":7},{"language":"default","tag":"Developers","count":6},{"language":"default","tag":"Developer documentation","count":6},{"language":"default","tag":"Getting started","count":4},{"language":"default","tag":"Integrations","count":4},{"language":"default","tag":"Resources","count":4},{"language":"default","tag":"Test creation","count":4}],"searchSettings":{"showContentSnippets":true}},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true,"deflection":{"enabled":true,"email":"support@fulgic.com"}},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-metadata","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"comment-mode","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"privacy":{"consentBanner":{"enabled":true}},"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"api-reference/procedures/list-a-projects-saved-procedures","slug":"/api-reference/procedures/list-a-projects-saved-procedures","pageMetadata":{"title":"List a project's saved procedures","description":"Every procedure under the project, ordered by `lastRunAt` descending then `createdAt` descending, so the most recently exercised ones come first. Returns whole rows, including `stepsJson` with every saved secret masked as `***`, plus a derived `expectation`. Used by `testorim list --project \u003cid>` and by `testorim init` to build its alias map.","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /api/projects/{projectId}/procedures","href":"/api-reference/procedures/list-a-projects-saved-procedures","autogeneratedByOpenApi":true},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx, jsxs: _jsxs} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n code: \"code\",\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsxs(_components.p, {\n children: [\"Every procedure under the project, ordered by \", _jsx(_components.code, {\n children: \"lastRunAt\"\n }), \" descending then \", _jsx(_components.code, {\n children: \"createdAt\"\n }), \" descending, so the most recently exercised ones come first. Returns whole rows, including \", _jsx(_components.code, {\n children: \"stepsJson\"\n }), \" with every saved secret masked as \", _jsx(_components.code, {\n children: \"***\"\n }), \", plus a derived \", _jsx(_components.code, {\n children: \"expectation\"\n }), \". Used by \", _jsx(_components.code, {\n children: \"testorim list --project \u003cid>\"\n }), \" and by \", _jsx(_components.code, {\n children: \"testorim init\"\n }), \" to build its alias map.\"]\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){return _jsx(_Fragment,{})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"List a project's saved procedures","description":"Every procedure under the project, ordered by `lastRunAt` descending then `createdAt` descending, so the most recently exercised ones come first. Returns whole rows, including `stepsJson` with every saved secret masked as `***`, plus a derived `expectation`. Used by `testorim list --project \u003cid>` and by `testorim init` to build its alias map.","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /api/projects/{projectId}/procedures","href":"/api-reference/procedures/list-a-projects-saved-procedures","autogeneratedByOpenApi":true}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[],"codeExamples":{}},"jsLinks":[],"openApiReferenceData":{"operation":{"tags":["Procedures"],"operationId":"listProcedures","summary":"List a project's saved procedures","description":"Every procedure under the project, ordered by `lastRunAt` descending then `createdAt` descending, so the most recently exercised ones come first. Returns whole rows, including `stepsJson` with every saved secret masked as `***`, plus a derived `expectation`. Used by `testorim list --project \u003cid>` and by `testorim init` to build its alias map.","security":["98cb7a2d-90c3-4dc0-9f32-0145d70adb61"],"parameters":["036c559c-91f6-473c-bb61-16a0dbac655d"],"responses":{"200":"4ff81cea-24b1-415b-84e0-4a86daeec8a9","401":"afde0673-6dea-4765-85fb-a174382bdf8d","404":"e7ff1c9d-bde9-4a18-9806-7db4978807f3","429":"29472aa1-862a-4267-a76c-c7a39335d8ca"},"method":"get","type":"path","servers":["d4db3c55-f4bc-46f2-9432-5f123e333c25","7ea51c90-14cf-4c02-9601-e57633a5e03f"],"path":"/api/projects/{projectId}/procedures","baseUrlOptions":["https://app.testorim.com","http://localhost:3001"],"title":"List a project's saved procedures","codeSamples":[],"requiredOnlyExamples":false,"requestExampleType":"generated","responseExampleType":"generated"},"dependencies":{"parameters":{"036c559c-91f6-473c-bb61-16a0dbac655d":{"name":"projectId","in":"path","required":true,"description":"Project id (UUID).","schema":{"type":"string","format":"uuid","isRequired":true,"description":"Project id (UUID).","uniqueKey":"","typeLabel":"string\u003cuuid>"}}},"responses":{"4ff81cea-24b1-415b-84e0-4a86daeec8a9":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["procedures"],"properties":{"procedures":{"type":"array","items":{"type":"object","title":"Procedure","description":"A saved, replayable test sequence. Handlers return the whole DB row.","required":["id","projectId","name","stepsJson","runOnMobile","runThrice","isLoginSetup","authMode","createdAt","runCount"],"properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"procedures.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"projectId":{"type":"string","format":"uuid","description":"The owning project. This is the field the CLI reads to satisfy the trigger endpoint's required `projectId`.","isRequired":true,"uniqueKey":"procedures.items.[INDEX].projectId","typeLabel":"string\u003cuuid>"},"name":{"type":"string","isRequired":true,"uniqueKey":"procedures.items.[INDEX].name","typeLabel":"string"},"stepsJson":{"type":"array","description":"The saved steps. Typed as untyped JSONB in the database, so a legacy row could in principle hold another shape.","items":{"type":"object","title":"ProcedureStep","description":"One saved step. The persisted array is untyped JSONB, but the write path validates this shape (`StepSchema` in `routes/procedures.ts`). A step whose value is a saved secret reads `***`.","required":["action","target"],"properties":{"action":{"type":"string","minLength":1,"maxLength":40,"description":"e.g. `navigate`, `click`, `type`, `assert`, `prompt_user`. Not constrained to an enum at this boundary.","isRequired":true,"uniqueKey":"procedures.items.[INDEX].stepsJson.items.[INDEX].action","typeLabel":"string"},"target":{"type":"string","minLength":1,"maxLength":2000,"description":"Natural-language element description or URL.","isRequired":true,"uniqueKey":"procedures.items.[INDEX].stepsJson.items.[INDEX].target","typeLabel":"string"},"value":{"type":"string","maxLength":8000,"description":"Text to type, or the expected value for an assertion. Supports `{{token}}` substitution from project variables and built-in helpers such as `{{random.email}}`.","uniqueKey":"procedures.items.[INDEX].stepsJson.items.[INDEX].value","typeLabel":"string"}},"additionalProperties":true,"uniqueKey":"procedures.items.[INDEX].stepsJson.items.[INDEX]","typeLabel":"ProcedureStep · object"},"isRequired":true,"uniqueKey":"procedures.items.[INDEX].stepsJson","typeLabel":"ProcedureStep · object[]"},"runOnMobile":{"type":"boolean","description":"Fan out each run to desktop and mobile viewports. Applies to replays started from the app; runs from this API, schedules and the PR bot use one desktop leg. Needs a subscription.","isRequired":true,"uniqueKey":"procedures.items.[INDEX].runOnMobile","typeLabel":"boolean"},"runThrice":{"type":"boolean","description":"Run three times to surface flakiness. Applies to replays started from the app.","isRequired":true,"uniqueKey":"procedures.items.[INDEX].runThrice","typeLabel":"boolean"},"isLoginSetup":{"type":"boolean","description":"Marks this as the project's authoritative login flow. On success the browser's session is captured onto the project, right after the sign-in, so other procedures start pre-authenticated. At most one procedure per project carries the flag; a procedure that only signs in is marked when saved.","isRequired":true,"uniqueKey":"procedures.items.[INDEX].isLoginSetup","typeLabel":"boolean"},"authMode":{"type":"string","enum":["unknown","fresh_logged_out","reuse_login_state"],"description":"Browser-state policy between matrix legs. `fresh_logged_out` starts every leg clean (login/signup/logout flows); `reuse_login_state` preloads saved login state (dashboard workflows); `unknown` preserves legacy behaviour.","isRequired":true,"uniqueKey":"procedures.items.[INDEX].authMode","typeLabel":"enum\u003cstring>"},"createdAt":{"type":"string","format":"date-time","isRequired":true,"uniqueKey":"procedures.items.[INDEX].createdAt","typeLabel":"string\u003cdate-time>"},"runCount":{"type":"integer","isRequired":true,"uniqueKey":"procedures.items.[INDEX].runCount","typeLabel":"integer"},"description":{"oneOf":[{"type":"string","uniqueKey":"procedures.items.[INDEX].description.oneOf.0","typeLabel":"string"},{"type":"null","uniqueKey":"procedures.items.[INDEX].description.oneOf.1","typeLabel":"null"}],"uniqueKey":"procedures.items.[INDEX].description","typeLabel":"string | null"},"browsersJson":{"oneOf":[{"type":"array","description":"Cross-browser fan-out for replays started from the app. Null or empty means Chromium only. Firefox and WebKit legs run on the Team plan; Pro and unsubscribed accounts replay in Chromium only. Runs from this API run in Chromium.","items":{"type":"string","enum":["chromium","firefox","webkit"],"uniqueKey":"procedures.items.[INDEX].browsersJson.oneOf.0.items.[INDEX]","typeLabel":"enum\u003cstring>"},"uniqueKey":"procedures.items.[INDEX].browsersJson.oneOf.0","typeLabel":"enum\u003cstring>[]"},{"type":"null","description":"Cross-browser fan-out for replays started from the app. Null or empty means Chromium only. Firefox and WebKit legs run on the Team plan; Pro and unsubscribed accounts replay in Chromium only. Runs from this API run in Chromium.","items":{"type":"string","enum":["chromium","firefox","webkit"]},"uniqueKey":"procedures.items.[INDEX].browsersJson.oneOf.1","typeLabel":"null"}],"uniqueKey":"procedures.items.[INDEX].browsersJson","typeLabel":"enum\u003cstring>[] | null"},"tagsJson":{"oneOf":[{"type":"array","description":"Free-form lowercase tags, e.g. `smoke`, `regression`.","items":{"type":"string","uniqueKey":"procedures.items.[INDEX].tagsJson.oneOf.0.items.[INDEX]","typeLabel":"string"},"uniqueKey":"procedures.items.[INDEX].tagsJson.oneOf.0","typeLabel":"string[]"},{"type":"null","description":"Free-form lowercase tags, e.g. `smoke`, `regression`.","items":{"type":"string"},"uniqueKey":"procedures.items.[INDEX].tagsJson.oneOf.1","typeLabel":"null"}],"uniqueKey":"procedures.items.[INDEX].tagsJson","typeLabel":"string[] | null"},"lastRunAt":{"oneOf":[{"type":"string","format":"date-time","uniqueKey":"procedures.items.[INDEX].lastRunAt.oneOf.0","typeLabel":"string\u003cdate-time>"},{"type":"null","format":"date-time","uniqueKey":"procedures.items.[INDEX].lastRunAt.oneOf.1","typeLabel":"null"}],"uniqueKey":"procedures.items.[INDEX].lastRunAt","typeLabel":"string\u003cdate-time> | null"},"maxDurationMinutes":{"oneOf":[{"type":"integer","description":"Per-procedure time cap in minutes. Null means the platform default of 10. The 5..120 bound is enforced by the API, not the column, so legacy rows may sit outside it.","uniqueKey":"procedures.items.[INDEX].maxDurationMinutes.oneOf.0","typeLabel":"integer"},{"type":"null","description":"Per-procedure time cap in minutes. Null means the platform default of 10. The 5..120 bound is enforced by the API, not the column, so legacy rows may sit outside it.","uniqueKey":"procedures.items.[INDEX].maxDurationMinutes.oneOf.1","typeLabel":"null"}],"uniqueKey":"procedures.items.[INDEX].maxDurationMinutes","typeLabel":"integer | null"},"expectation":{"type":"string","enum":["pass","fail"],"description":"Derived, not stored: `fail` when a saved click, type or assert step expects failure. A replay is framed by this, never by a request's `expectation`.","uniqueKey":"procedures.items.[INDEX].expectation","typeLabel":"enum\u003cstring>"}},"uniqueKey":"procedures.items.[INDEX]","typeLabel":"Procedure · object"},"isRequired":true,"uniqueKey":"procedures","typeLabel":"Procedure · object[]"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"procedures":[{"id":"3c90c3cc-0d44-4b50-8888-8dd25736052a","projectId":"3c90c3cc-0d44-4b50-8888-8dd25736052a","name":"\u003cstring>","stepsJson":[{"action":"\u003cstring>","target":"\u003cstring>","value":"\u003cstring>"}],"runOnMobile":true,"runThrice":true,"isLoginSetup":true,"authMode":"unknown","createdAt":"2023-11-07T05:31:56Z","runCount":123,"description":"\u003cstring>","browsersJson":["chromium"],"tagsJson":["\u003cstring>"],"lastRunAt":"2023-11-07T05:31:56Z","maxDurationMinutes":123,"expectation":"pass"}]}}},"exampleType":"generated"}},"exampleType":"generated"},"afde0673-6dea-4765-85fb-a174382bdf8d":{"description":"No `Authorization: Bearer …` header, or the credential did not\nverify. Distinct messages the code can return:\n\n* `Missing or invalid Authorization header`\n* `Invalid or revoked API key`\n* `API key owner not found`\n* `API key's organization is no longer accessible`. The key's user\n has been removed from the org it is bound to\n* `Invalid token` / `Token missing sub claim`. Clerk JWT path\n\nRepeated failures burn the `auth:strict` bucket (5 per 15 minutes\nper IP) and then return `429` instead.\n","content":{"application/json":{"schema":{"type":"object","title":"Error","description":"The universal error envelope. Every failing handler in this surface responds with a JSON object carrying a human-readable `error` string; some add extra fields (see the other error schemas).","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable message. Not a stable machine-readable code.","isRequired":true,"uniqueKey":"error","typeLabel":"string"}},"additionalProperties":true,"uniqueKey":"","typeLabel":"Error · object"},"example":{"error":"Invalid or revoked API key"},"examples":{"Example":{"title":"Example","value":{"error":"Invalid or revoked API key"}}},"exampleType":"examples"}},"exampleType":"examples"},"e7ff1c9d-bde9-4a18-9806-7db4978807f3":{"description":"No live project with this id in the key's organization. Deleted and foreign projects look like missing ones, except that when the key's owner is a member of the organization that holds the project, the body adds `code: \"other_workspace\"` with that organization's `orgId` and `orgName`.","content":{"application/json":{"schema":{"type":"object","title":"Error","description":"The universal error envelope. Every failing handler in this surface responds with a JSON object carrying a human-readable `error` string; some add extra fields (see the other error schemas).","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable message. Not a stable machine-readable code.","isRequired":true,"uniqueKey":"error","typeLabel":"string"}},"additionalProperties":true,"uniqueKey":"","typeLabel":"Error · object"},"example":{"error":"Project not found"},"examples":{"Example":{"title":"Example","value":{"error":"Project not found"}}},"exampleType":"examples"}},"exampleType":"examples"},"29472aa1-862a-4267-a76c-c7a39335d8ca":{"description":"Throttled. Every `/api/*` route sits behind `api:global` (600 / minute per IP); some routes add a tighter per-user limiter.","headers":{"Retry-After":{"description":"Seconds until the window frees up.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","title":"RateLimitError","description":"Emitted by the rate-limit middleware alongside a `Retry-After` header.","required":["error"],"properties":{"error":{"type":"string","description":"The limiter's reason, e.g. `Rate limit exceeded. Try again in 42s.`","isRequired":true,"uniqueKey":"error","typeLabel":"string"},"retryAfter":{"oneOf":[{"type":"integer","description":"Seconds until the window frees. Mirrors `Retry-After`. May be absent/null when the limiter did not report a reset time, in which case the header defaults to 60.","uniqueKey":"retryAfter.oneOf.0","typeLabel":"integer"},{"type":"null","description":"Seconds until the window frees. Mirrors `Retry-After`. May be absent/null when the limiter did not report a reset time, in which case the header defaults to 60.","uniqueKey":"retryAfter.oneOf.1","typeLabel":"null"}],"uniqueKey":"retryAfter","typeLabel":"integer | null"}},"additionalProperties":true,"uniqueKey":"","typeLabel":"RateLimitError · object"},"examples":{"Example":{"title":"Example","value":{"error":"\u003cstring>","retryAfter":123}}},"exampleType":"generated"}},"exampleType":"generated"}},"security":{"98cb7a2d-90c3-4dc0-9f32-0145d70adb61":{"ApiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"tst_live_\u003c32 url-safe base64 chars>","description":"Send `Authorization: Bearer tst_live_…`.\n\nFormat (`services/api-keys.ts`): the literal prefix `tst_live_`\nfollowed by 24 random bytes rendered as 32 base64url characters.\nOnly the SHA-256 hash is stored server-side. The shape check that\nroutes a token down the API-key path rather than the Clerk JWT path\nrequires the `tst_live_` prefix and a total length of at least 25\ncharacters.\n\nKeys are minted in the dashboard at `/settings/api-keys`, in any\nworkspace, personal included. The same header also accepts a Clerk\nsession JWT, which is how the web app authenticates, but the JWT path\nis out of scope for this document.\n","scopes":[]}}},"servers":{"d4db3c55-f4bc-46f2-9432-5f123e333c25":{"url":"https://app.testorim.com","description":"Production. Single-origin deployment: Caddy on one GCP VM serves the SPA at `/` and reverse-proxies `/api/*` and `/ws` to the backend. This is also the default API base URL used by the Testorim CLI."},"7ea51c90-14cf-4c02-9601-e57633a5e03f":{"url":"http://localhost:3001","description":"A locally running Testorim backend, for self-hosted or development use. The default port is 3001."}},"processedSecurityOptions":[{"schemes":[{"scheme":{"type":"http","scheme":"bearer","bearerFormat":"tst_live_\u003c32 url-safe base64 chars>","description":"Send `Authorization: Bearer tst_live_…`.\n\nFormat (`services/api-keys.ts`): the literal prefix `tst_live_`\nfollowed by 24 random bytes rendered as 32 base64url characters.\nOnly the SHA-256 hash is stored server-side. The shape check that\nroutes a token down the API-key path rather than the Clerk JWT path\nrequires the `tst_live_` prefix and a total length of at least 25\ncharacters.\n\nKeys are minted in the dashboard at `/settings/api-keys`, in any\nworkspace, personal included. The same header also accepts a Clerk\nsession JWT, which is how the web app authenticates, but the JWT path\nis out of scope for this document.\n","scopes":[]},"schema":{"title":"Bearer Auth","type":"string","isRequired":true,"description":"Send `Authorization: Bearer tst_live_…`.\n\nFormat (`services/api-keys.ts`): the literal prefix `tst_live_`\nfollowed by 24 random bytes rendered as 32 base64url characters.\nOnly the SHA-256 hash is stored server-side. The shape check that\nroutes a token down the API-key path rather than the Clerk JWT path\nrequires the `tst_live_` prefix and a total length of at least 25\ncharacters.\n\nKeys are minted in the dashboard at `/settings/api-keys`, in any\nworkspace, personal included. The same header also accepts a Clerk\nsession JWT, which is how the web app authenticates, but the JWT path\nis out of scope for this document.\n","format":"bearer","typeLabel":"string\u003cbearer>","deprecated":false},"schemeName":"ApiKeyAuth","name":"Authorization"}],"title":"ApiKeyAuth"}]},"originalFileLocation":"api-reference/openapi.yaml"},"subdomain":"fulgic","siteName":"Testorim","title":"List a project's saved procedures - Testorim","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-09-25T12:52:51.780Z","originalFileLocation":"api-reference/procedures/list-a-projects-saved-procedures.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Every procedure under the project, ordered by lastRunAt descending then createdAt descending, so the most recently exercised ones come first. Returns whole rows, including stepsJson with every saved secret masked as ***, plus a derived expectation. Used by testorim list --project and by testorim init to build its alias map.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Testorim\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/fulgic/HlhB1s7g4HMZVSXy/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/fulgic/HlhB1s7g4HMZVSXy/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/fulgic/HlhB1s7g4HMZVSXy/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/fulgic/HlhB1s7g4HMZVSXy/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/fulgic/HlhB1s7g4HMZVSXy/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/fulgic/HlhB1s7g4HMZVSXy/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/fulgic/HlhB1s7g4HMZVSXy/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/fulgic/HlhB1s7g4HMZVSXy/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/fulgic/HlhB1s7g4HMZVSXy/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"List a project\u0026#x27;s saved procedures - Testorim\">\u003cmeta data-mint-head property=\"og:description\" content=\"Every procedure under the project, ordered by lastRunAt descending then createdAt descending, so the most recently exercised ones come first. Returns whole rows, including stepsJson with every saved secret masked as ***, plus a derived expectation. Used by testorim list --project and by testorim init to build its alias map.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Testorim\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.testorim.com/api-reference/procedures/list-a-projects-saved-procedures\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://fulgic.mintlify.site/_mintlify/api/og/v2?division=Procedures\u0026amp;title=List+a+project%27s+saved+procedures\u0026amp;description=Every+procedure+under+the+project%2C+ordered+by+%60lastRunAt%60+descending+then+%60createdAt%60+descending%2C+so+the+most+recently+exercised+ones+come+first.+Returns+whole+\u0026amp;theme=21e417d08933df01149b8e86\u0026amp;signature=66194ba822bfcca7a0df5ffa693c74d179f68437d6fa113b19d43e2b73986f23\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"List a project\u0026#x27;s saved procedures - Testorim\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Every procedure under the project, ordered by lastRunAt descending then createdAt descending, so the most recently exercised ones come first. Returns whole rows, including stepsJson with every saved secret masked as ***, plus a derived expectation. Used by testorim list --project and by testorim init to build its alias map.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://fulgic.mintlify.site/_mintlify/api/og/v2?division=Procedures\u0026amp;title=List+a+project%27s+saved+procedures\u0026amp;description=Every+procedure+under+the+project%2C+ordered+by+%60lastRunAt%60+descending+then+%60createdAt%60+descending%2C+so+the+most+recently+exercised+ones+come+first.+Returns+whole+\u0026amp;theme=21e417d08933df01149b8e86\u0026amp;signature=66194ba822bfcca7a0df5ffa693c74d179f68437d6fa113b19d43e2b73986f23\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/fulgic/HlhB1s7g4HMZVSXy/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Testorim\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#059669\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.testorim.com/api-reference/procedures/list-a-projects-saved-procedures\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/api-reference/procedures/list-a-projects-saved-procedures.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.testorim.com/#organization\",\"name\":\"Testorim\",\"url\":\"https://docs.testorim.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/fulgic/ZOzvtyg298rAaunK/logo/wordmark.png?fit=max\u0026auto=format\u0026n=ZOzvtyg298rAaunK\u0026q=85\u0026s=c49b84c50b62f2ccafcc2660391619e7\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.testorim.com#website\",\"name\":\"Testorim\",\"url\":\"https://docs.testorim.com\",\"publisher\":{\"@id\":\"https://docs.testorim.com/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.testorim.com/api-reference/procedures/list-a-projects-saved-procedures#webpage\",\"url\":\"https://docs.testorim.com/api-reference/procedures/list-a-projects-saved-procedures\",\"name\":\"List a project's saved procedures\",\"description\":\"Every procedure under the project, ordered by lastRunAt descending then createdAt descending, so the most recently exercised ones come first. Returns whole rows, including stepsJson with every saved secret masked as ***, plus a derived expectation. Used by testorim list --project and by testorim init to build its alias map.\",\"dateModified\":\"2026-09-25T12:52:51.780Z\",\"isPartOf\":{\"@id\":\"https://docs.testorim.com#website\"},\"breadcrumb\":{\"@id\":\"https://docs.testorim.com/api-reference/procedures/list-a-projects-saved-procedures#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.testorim.com/api-reference/procedures/list-a-projects-saved-procedures#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Endpoints\",\"item\":\"https://docs.testorim.com/api-reference/runs/start-a-test-run-headlessly\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"List a project's saved procedures\",\"item\":\"https://docs.testorim.com/api-reference/procedures/list-a-projects-saved-procedures\"}]},{\"@type\":[\"TechArticle\",\"APIReference\"],\"@id\":\"https://docs.testorim.com/api-reference/procedures/list-a-projects-saved-procedures#apireference\",\"headline\":\"List a project's saved procedures\",\"name\":\"List a project's saved procedures\",\"description\":\"Every procedure under the project, ordered by lastRunAt descending then createdAt descending, so the most recently exercised ones come first. Returns whole rows, including stepsJson with every saved secret masked as ***, plus a derived expectation. Used by testorim list --project and by testorim init to build its alias map.\",\"url\":\"https://docs.testorim.com/api-reference/procedures/list-a-projects-saved-procedures\",\"mainEntityOfPage\":{\"@id\":\"https://docs.testorim.com/api-reference/procedures/list-a-projects-saved-procedures#webpage\"},\"dateModified\":\"2026-09-25T12:52:51.780Z\",\"publisher\":{\"@id\":\"https://docs.testorim.com/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.testorim.com#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Testorim\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Testorim","nav":{"global":{"anchors":[{"anchor":"Testorim app","icon":"monitor","href":"https://app.testorim.com"},{"anchor":"Website","icon":"globe","href":"https://testorim.com"}]},"tabs":[{"tab":"Documentation","pages":[{"title":"Documentation","href":""}]},{"tab":"Developers","pages":[{"title":"Developers","href":"/developers/platform-overview"}]},{"tab":"API reference","groups":[{"group":"Overview","pages":[{"title":"API reference","href":"/api-reference/introduction"},{"title":"Errors","href":"/api-reference/errors"}]},{"group":"Endpoints","pages":[{"group":"Runs","pages":[{"title":"Start a test run headlessly","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml post /api/runs/trigger","href":"/api-reference/runs/start-a-test-run-headlessly"},{"title":"List runs across the organization","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /api/runs","href":"/api-reference/runs/list-runs-across-the-organization"},{"title":"List in-flight runs, grouped by batch","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /api/runs/active","href":"/api-reference/runs/list-in-flight-runs-grouped-by-batch"},{"title":"Get one run with all artifacts","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /api/runs/{id}","href":"/api-reference/runs/get-one-run-with-all-artifacts"},{"title":"Cancel a single run","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml post /api/runs/{id}/cancel","href":"/api-reference/runs/cancel-a-single-run"},{"title":"Cancel every run in a batch","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml post /api/runs/batch/{batchId}/cancel","href":"/api-reference/runs/cancel-every-run-in-a-batch"},{"title":"List runs for one project","description":"Runs belonging to a single project, newest first, in the summary shape. The project is resolved org-scoped first, so an unknown or foreign project id 404s before any run is read.","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /api/projects/{projectId}/runs","href":"/api-reference/runs/list-runs-for-one-project"}]},{"group":"Procedures","pages":[{"title":"List a project's saved procedures","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /api/projects/{projectId}/procedures","href":"/api-reference/procedures/list-a-projects-saved-procedures"},{"title":"Get one procedure by id","description":"Flat, org-scoped lookup that does not require knowing the project. This is the call the CLI makes for `testorim run \u003cprocedure-id>`: `POST /api/runs/trigger` requires `projectId`, so the CLI resolves it from `procedure.projectId` here first. A procedure in another organization 404s.","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /api/procedures/{id}","href":"/api-reference/procedures/get-one-procedure-by-id"},{"title":"Export a procedure as a standalone Playwright spec","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /api/procedures/{id}/export","href":"/api-reference/procedures/export-a-procedure-as-a-standalone-playwright-spec"}]},{"group":"Projects","pages":[{"title":"List projects","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /api/projects","href":"/api-reference/projects/list-projects"},{"title":"Create (or re-use) a project","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml post /api/projects","href":"/api-reference/projects/create-or-re-use-a-project"},{"title":"Get one project","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /api/projects/{id}","href":"/api-reference/projects/get-one-project"}]},{"group":"API keys","pages":[{"title":"List active API keys","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /api/keys","href":"/api-reference/api-keys/list-active-api-keys"},{"title":"Mint a new API key (browser session only)","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml post /api/keys","href":"/api-reference/api-keys/mint-a-new-api-key-browser-session-only"},{"title":"Revoke an API key","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml delete /api/keys/{id}","href":"/api-reference/api-keys/revoke-an-api-key"}]},{"group":"Identity","pages":[{"title":"Identify the presented credential","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /api/me","href":"/api-reference/identity/identify-the-presented-credential"}]}]}]}]},"basePath":"","title":"List a project's saved procedures - Testorim","buildId":"dpl_42LwxxU1kGN114H4XzwGYXa9ZDah","deploymentId":"dpl_42LwxxU1kGN114H4XzwGYXa9ZDah","dynamics":["c2aa737661","2b790eda92","b0f84f53ff","a9bb46feb3"],"chunks":["OpenApiReferenceProvider-C-hEh3I2.js","utils-CnF4QiUD.js","cloneDeep-izs3Hzxb.js","_Uint8Array-BVIqJ90l.js","isObjectLike-C8vmzPEN.js","isArray-Dcu1dQ5t.js","utils-C7PVNbel.js","toString-BX-iyh8G.js","isSymbol-CSH9U2Gm.js","addUniqueKeysToSchema-8ytevL2A.js","snippetPresets-BT1alx3v.js","style-to-object-BY3JEAdk.js","v4-DIgz8zy6.js","conditional-requirements-CFM0id0Q.js","ApiExamples-BLXC0i_W.js","isEqual-BfaiBbcl.js","usePlaygroundInputsStore-CyLIGI9_.js","shallow-DC0lGVIW.js","getApiPlaygroundDisplay-CgDQ85tN.js","CodeSnippet-6-I-Gpv9.js","CodeSnippets-BAk1kDdu.js","Page-C945cI-W.js","getPastedJson-B276RuWN.js","icon-BqP8etTm.js","MethodPill-DM6sbxG1.js","DuplicateIcon-PyJglOaE.js","createIcon-CMeOMAc_.js","DynamicMarkdown-BoyYAA8K.js","SectionHeading-DAYv3gvZ.js","axios-vUo_bRtp.js","Markdown-BhTTmazV.js","clsx-CR-RHrVC.js","lib-DuNMoZ05.js","lib-B8HbPgcC.js","html-void-elements-mCSPHezU.js","dist-CBUH0-zX.js","katex-YMM-FMeD.js","marked.esm-CZWythHk.js","AssistantPageLink-BmARFYNC.js"]}