> For the complete documentation index, see [llms.txt](https://docs.pletor.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.pletor.ai/documentation/api-reference/apps.md).

# Apps

Apps are curated, runnable applications built on top of flows. Every app, input, setting and output has a \`slug\`, usable wherever its ID is. Read an app's \`input\_schema\` and \`output\_schema\` (JSON Schema, keyed by slug), start a run with inputs and settings keyed by slug, then poll it and read its \`outputs\`. Pass \`version\` to pin a run to one published version of the app.

Discover apps, read their schema, and run them.

## List Apps

> List the apps you can run: your own, your organization's, and public ones.\
> \
> Each app carries its \`slug\`, usable in place of its \`id\` in every path, and\
> its latest \`version\`. Ordered by Pletor's curated position, then by most\
> recent run in your organization.\
> \
> \*\*Error Codes:\*\*\
> \- \`422\`: Invalid \`visibility\` value, or \`limit\` outside 1–100\
> \- \`401\`: Missing or invalid API key or Bearer token\
> \- \`403\`: The API key is bound to a different organization than \`X-Organization-Id\`\
> \- \`429\`: Rate limit exceeded — retry after the \`Retry-After\` header

```json
{"openapi":"3.1.0","info":{"title":"Pletor - Public API","version":"2.0.0"},"tags":[{"name":"apps","description":"Discover apps, read their schema, and run them."}],"servers":[{"url":"/api/public"}],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key for authentication"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Cognito JWT Bearer token"}},"schemas":{"FlowApplicationVisibility":{"type":"string","enum":["private","workspace","public"],"title":"FlowApplicationVisibility","description":"Visibility levels for flow applications."},"CursorPaginatedResponse_AppV2Summary_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/AppV2Summary"},"type":"array","title":"Data"},"has_more":{"type":"boolean","title":"Has More"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["data","has_more"],"title":"CursorPaginatedResponse[AppV2Summary]"},"AppV2Summary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id","description":"Stable app identifier"},"slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Slug","description":"API name of the app, unique in its organization. Usable wherever an app id is"},"name":{"type":"string","title":"Name","description":"App display name"},"description":{"type":"string","title":"Description","description":"App description and purpose"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url","description":"Public CDN URL for the app thumbnail image"},"visibility":{"$ref":"#/components/schemas/FlowApplicationVisibility","description":"Visibility level: private, workspace, shared, or public"},"version":{"type":"integer","title":"Version","description":"Latest version number"},"estimated_cost":{"type":"integer","title":"Estimated Cost","description":"Estimated credit cost per run at the default settings. 0 when no estimate is available"},"has_human_review":{"type":"boolean","title":"Has Human Review","description":"True when a run pauses on a human review step until a decision is submitted","default":false}},"type":"object","required":["id","slug","name","description","visibility","version","estimated_cost"],"title":"AppV2Summary","description":"An app as listed by GET /apps."},"ApiErrorResponse":{"description":"Standard error response envelope. All API errors follow this format.","properties":{"error":{"$ref":"#/components/schemas/ApiErrorDetail","description":"Error details"}},"required":["error"],"title":"ApiErrorResponse","type":"object"},"ApiErrorDetail":{"description":"Structured details for an API error.","properties":{"code":{"$ref":"#/components/schemas/ApiErrorCode","description":"Machine-readable error code; branch on this, not on `message`"},"message":{"description":"Human-readable error description","title":"Message","type":"string"},"status":{"description":"HTTP status code of this response","title":"Status","type":"integer"},"details":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"number"},{"type":"boolean"},{"items":{"type":"string"},"type":"array"},{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}]},"description":"Additional context. Always holds `request_id` (the response's `X-Request-Id`); `validation_error` from request validation adds `errors`, a list of `location: message` strings, and `rate_limit_exceeded` adds `retry_after`, the seconds to wait.","title":"Details","type":"object"}},"required":["code","message","status","details"],"title":"ApiErrorDetail","type":"object"},"ApiErrorCode":{"description":"Machine-readable error code, with its usual HTTP status: `invalid_input`\n400, `authentication_required` 401, `forbidden` 403, `not_found` 404,\n`conflict` 409, `validation_error` 422, `rate_limit_exceeded` 429,\n`internal_error` 500, `run_failed` 502, `service_unavailable` 503, `timeout`\n504. A 4xx status without a code of its own is sent as `invalid_input`.","enum":["invalid_input","authentication_required","forbidden","not_found","conflict","validation_error","rate_limit_exceeded","internal_error","run_failed","service_unavailable","timeout"],"title":"ApiErrorCode","type":"string"}}},"paths":{"/v2/apps/":{"get":{"tags":["apps"],"summary":"List Apps","description":"List the apps you can run: your own, your organization's, and public ones.\n\nEach app carries its `slug`, usable in place of its `id` in every path, and\nits latest `version`. Ordered by Pletor's curated position, then by most\nrecent run in your organization.\n\n**Error Codes:**\n- `422`: Invalid `visibility` value, or `limit` outside 1–100\n- `401`: Missing or invalid API key or Bearer token\n- `403`: The API key is bound to a different organization than `X-Organization-Id`\n- `429`: Rate limit exceeded — retry after the `Retry-After` header","operationId":"v2_list_apps_apps__get","parameters":[{"name":"visibility","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/FlowApplicationVisibility"}},{"type":"null"}],"description":"Only apps with one of these visibilities (repeatable)","title":"Visibility"},"description":"Only apps with one of these visibilities (repeatable)"},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Case- and accent-insensitive match on name or description","title":"Search"},"description":"Case- and accent-insensitive match on name or description"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Maximum number of items to return","default":20,"title":"Limit"},"description":"Maximum number of items to return"},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Cursor for pagination (from next_cursor)","title":"Cursor"},"description":"Cursor for pagination (from next_cursor)"},{"name":"X-Organization-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Active organization ID for multi-org support","title":"X-Organization-Id"},"description":"Active organization ID for multi-org support"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CursorPaginatedResponse_AppV2Summary_"}}}},"422":{"description":"Validation error (`validation_error`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}}}}}}
```

## Get App

> Get an app and the JSON Schemas of what a run takes and returns.\
> \
> \`input\_schema\` describes a run's \`inputs\`, \`settings\` and, when some outputs\
> are optional, \`outputs\` — every field keyed by its slug, with its display\
> name as \`title\`, its allowed values as \`enum\`, how many items it takes as\
> \`minItems\` / \`maxItems\`, and a setting's \`default\`. \`output\_schema\` describes\
> the \`outputs\` of a finished run. Use them as is to validate a request,\
> generate a client, or build a form.\
> \
> \`versions\` lists every published version, newest first. \`breaking\` marks a\
> version a caller of the previous one has to adapt to; pin \`version\` on runs\
> to keep calling an older one.\
> \
> \*\*Error Codes:\*\*\
> \- \`404\`: App or version not found or not accessible\
> \- \`422\`: \`version\` is not a positive integer\
> \- \`401\`: Missing or invalid API key or Bearer token\
> \- \`403\`: The API key is bound to a different organization than \`X-Organization-Id\`\
> \- \`429\`: Rate limit exceeded — retry after the \`Retry-After\` header

```json
{"openapi":"3.1.0","info":{"title":"Pletor - Public API","version":"2.0.0"},"tags":[{"name":"apps","description":"Discover apps, read their schema, and run them."}],"servers":[{"url":"/api/public"}],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key for authentication"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Cognito JWT Bearer token"}},"schemas":{"AppV2":{"properties":{"id":{"type":"string","format":"uuid","title":"Id","description":"Stable app identifier"},"slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Slug","description":"API name of the app, unique in its organization. Usable wherever an app id is"},"name":{"type":"string","title":"Name","description":"App display name"},"description":{"type":"string","title":"Description","description":"App description and purpose"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url","description":"Public CDN URL for the app thumbnail image"},"visibility":{"$ref":"#/components/schemas/FlowApplicationVisibility","description":"Visibility level: private, workspace, shared, or public"},"version":{"type":"integer","title":"Version","description":"Version this response describes"},"estimated_cost":{"type":"integer","title":"Estimated Cost","description":"Estimated credit cost per run at the default settings. 0 when no estimate is available"},"has_human_review":{"type":"boolean","title":"Has Human Review","description":"True when a run pauses on a human review step until a decision is submitted","default":false},"versions":{"items":{"$ref":"#/components/schemas/AppVersion"},"type":"array","title":"Versions","description":"Every published version, newest first"},"input_schema":{"additionalProperties":true,"type":"object","title":"Input Schema","description":"JSON Schema of a run's `inputs`, `settings` and `outputs`, keyed by slug. Display names are each field's `title`"},"output_schema":{"additionalProperties":true,"type":"object","title":"Output Schema","description":"JSON Schema of a finished run's `outputs`, keyed by slug. `x-data-type` names each output's kind: text, images, videos, audio, files or data"}},"type":"object","required":["id","slug","name","description","visibility","version","estimated_cost","versions","input_schema","output_schema"],"title":"AppV2","description":"An app with its run contract as JSON Schema."},"FlowApplicationVisibility":{"type":"string","enum":["private","workspace","public"],"title":"FlowApplicationVisibility","description":"Visibility levels for flow applications."},"AppVersion":{"properties":{"version":{"type":"integer","title":"Version","description":"Version number, starting at 1"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When this version was published"},"breaking":{"type":"boolean","title":"Breaking","description":"True when a caller of the previous version has to change something to call this one: a field removed or renamed, a new required input, fewer accepted values, a changed type. Pin `version` to keep calling the previous one"}},"type":"object","required":["version","created_at","breaking"],"title":"AppVersion","description":"One published version of an app."},"ApiErrorResponse":{"description":"Standard error response envelope. All API errors follow this format.","properties":{"error":{"$ref":"#/components/schemas/ApiErrorDetail","description":"Error details"}},"required":["error"],"title":"ApiErrorResponse","type":"object"},"ApiErrorDetail":{"description":"Structured details for an API error.","properties":{"code":{"$ref":"#/components/schemas/ApiErrorCode","description":"Machine-readable error code; branch on this, not on `message`"},"message":{"description":"Human-readable error description","title":"Message","type":"string"},"status":{"description":"HTTP status code of this response","title":"Status","type":"integer"},"details":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"number"},{"type":"boolean"},{"items":{"type":"string"},"type":"array"},{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}]},"description":"Additional context. Always holds `request_id` (the response's `X-Request-Id`); `validation_error` from request validation adds `errors`, a list of `location: message` strings, and `rate_limit_exceeded` adds `retry_after`, the seconds to wait.","title":"Details","type":"object"}},"required":["code","message","status","details"],"title":"ApiErrorDetail","type":"object"},"ApiErrorCode":{"description":"Machine-readable error code, with its usual HTTP status: `invalid_input`\n400, `authentication_required` 401, `forbidden` 403, `not_found` 404,\n`conflict` 409, `validation_error` 422, `rate_limit_exceeded` 429,\n`internal_error` 500, `run_failed` 502, `service_unavailable` 503, `timeout`\n504. A 4xx status without a code of its own is sent as `invalid_input`.","enum":["invalid_input","authentication_required","forbidden","not_found","conflict","validation_error","rate_limit_exceeded","internal_error","run_failed","service_unavailable","timeout"],"title":"ApiErrorCode","type":"string"}}},"paths":{"/v2/apps/{app}":{"get":{"tags":["apps"],"summary":"Get App","description":"Get an app and the JSON Schemas of what a run takes and returns.\n\n`input_schema` describes a run's `inputs`, `settings` and, when some outputs\nare optional, `outputs` — every field keyed by its slug, with its display\nname as `title`, its allowed values as `enum`, how many items it takes as\n`minItems` / `maxItems`, and a setting's `default`. `output_schema` describes\nthe `outputs` of a finished run. Use them as is to validate a request,\ngenerate a client, or build a form.\n\n`versions` lists every published version, newest first. `breaking` marks a\nversion a caller of the previous one has to adapt to; pin `version` on runs\nto keep calling an older one.\n\n**Error Codes:**\n- `404`: App or version not found or not accessible\n- `422`: `version` is not a positive integer\n- `401`: Missing or invalid API key or Bearer token\n- `403`: The API key is bound to a different organization than `X-Organization-Id`\n- `429`: Rate limit exceeded — retry after the `Retry-After` header","operationId":"v2_get_app_apps__app__get","parameters":[{"name":"app","in":"path","required":true,"schema":{"type":"string","description":"The app's ID, or its `slug` (an app of your own organization)","title":"App"},"description":"The app's ID, or its `slug` (an app of your own organization)"},{"name":"version","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Describe this version instead of the latest","title":"Version"},"description":"Describe this version instead of the latest"},{"name":"X-Organization-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Active organization ID for multi-org support","title":"X-Organization-Id"},"description":"Active organization ID for multi-org support"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppV2"}}}},"422":{"description":"Validation error (`validation_error`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}}}}}}
```

## List Runs

> List the app's runs in your organization, newest first.\
> \
> Summaries carry no inputs, outputs or reviews: fetch a run for those.\
> \
> \*\*Error Codes:\*\*\
> \- \`404\`: App or version not found or not accessible\
> \- \`422\`: Invalid \`status\`, \`version\` or \`limit\`\
> \- \`401\`: Missing or invalid API key or Bearer token\
> \- \`403\`: The API key is bound to a different organization than \`X-Organization-Id\`\
> \- \`429\`: Rate limit exceeded — retry after the \`Retry-After\` header

```json
{"openapi":"3.1.0","info":{"title":"Pletor - Public API","version":"2.0.0"},"tags":[{"name":"apps","description":"Discover apps, read their schema, and run them."}],"servers":[{"url":"/api/public"}],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key for authentication"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Cognito JWT Bearer token"}},"schemas":{"FlowRunStatus":{"type":"string","enum":["in_progress","completed","failed","canceled"],"title":"FlowRunStatus","description":"Execution status of a workflow run."},"CursorPaginatedResponse_AppV2RunSummary_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/AppV2RunSummary"},"type":"array","title":"Data"},"has_more":{"type":"boolean","title":"Has More"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["data","has_more"],"title":"CursorPaginatedResponse[AppV2RunSummary]"},"AppV2RunSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id","description":"Run identifier"},"app_id":{"type":"string","format":"uuid","title":"App Id","description":"Stable ID of the app"},"version":{"type":"integer","title":"Version","description":"App version the run executes"},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id","description":"Your reference, if any"},"status":{"$ref":"#/components/schemas/FlowRunStatus","description":"in_progress, then exactly one of completed, failed or canceled"},"progress":{"type":"number","maximum":1,"minimum":0,"title":"Progress","description":"Fraction of steps completed","default":0},"awaiting_human_review":{"type":"boolean","title":"Awaiting Human Review","description":"True while paused on a human review step","default":false},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the run was created"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At","description":"When it started"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At","description":"When it ended; null while in_progress"},"cost":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Cost","description":"Credits used so far; null before any step ran"}},"type":"object","required":["id","app_id","version","status","created_at"],"title":"AppV2RunSummary","description":"A run as listed by GET /apps/{app}/runs."},"ApiErrorResponse":{"description":"Standard error response envelope. All API errors follow this format.","properties":{"error":{"$ref":"#/components/schemas/ApiErrorDetail","description":"Error details"}},"required":["error"],"title":"ApiErrorResponse","type":"object"},"ApiErrorDetail":{"description":"Structured details for an API error.","properties":{"code":{"$ref":"#/components/schemas/ApiErrorCode","description":"Machine-readable error code; branch on this, not on `message`"},"message":{"description":"Human-readable error description","title":"Message","type":"string"},"status":{"description":"HTTP status code of this response","title":"Status","type":"integer"},"details":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"number"},{"type":"boolean"},{"items":{"type":"string"},"type":"array"},{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}]},"description":"Additional context. Always holds `request_id` (the response's `X-Request-Id`); `validation_error` from request validation adds `errors`, a list of `location: message` strings, and `rate_limit_exceeded` adds `retry_after`, the seconds to wait.","title":"Details","type":"object"}},"required":["code","message","status","details"],"title":"ApiErrorDetail","type":"object"},"ApiErrorCode":{"description":"Machine-readable error code, with its usual HTTP status: `invalid_input`\n400, `authentication_required` 401, `forbidden` 403, `not_found` 404,\n`conflict` 409, `validation_error` 422, `rate_limit_exceeded` 429,\n`internal_error` 500, `run_failed` 502, `service_unavailable` 503, `timeout`\n504. A 4xx status without a code of its own is sent as `invalid_input`.","enum":["invalid_input","authentication_required","forbidden","not_found","conflict","validation_error","rate_limit_exceeded","internal_error","run_failed","service_unavailable","timeout"],"title":"ApiErrorCode","type":"string"}}},"paths":{"/v2/apps/{app}/runs":{"get":{"tags":["apps"],"summary":"List Runs","description":"List the app's runs in your organization, newest first.\n\nSummaries carry no inputs, outputs or reviews: fetch a run for those.\n\n**Error Codes:**\n- `404`: App or version not found or not accessible\n- `422`: Invalid `status`, `version` or `limit`\n- `401`: Missing or invalid API key or Bearer token\n- `403`: The API key is bound to a different organization than `X-Organization-Id`\n- `429`: Rate limit exceeded — retry after the `Retry-After` header","operationId":"v2_list_runs_apps__app__runs_get","parameters":[{"name":"app","in":"path","required":true,"schema":{"type":"string","description":"The app's ID, or its `slug` (an app of your own organization)","title":"App"},"description":"The app's ID, or its `slug` (an app of your own organization)"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/FlowRunStatus"},{"type":"null"}],"description":"Only runs in this status","title":"Status"},"description":"Only runs in this status"},{"name":"external_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Only the run created with this external_id","title":"External Id"},"description":"Only the run created with this external_id"},{"name":"version","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Only runs of this app version","title":"Version"},"description":"Only runs of this app version"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Maximum number of items to return","default":20,"title":"Limit"},"description":"Maximum number of items to return"},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Cursor for pagination (from next_cursor)","title":"Cursor"},"description":"Cursor for pagination (from next_cursor)"},{"name":"X-Organization-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Active organization ID for multi-org support","title":"X-Organization-Id"},"description":"Active organization ID for multi-org support"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CursorPaginatedResponse_AppV2RunSummary_"}}}},"422":{"description":"Validation error (`validation_error`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}}}}}}
```

## Create Run

> Start a run of the app.\
> \
> Pass \`inputs\`, \`settings\` and \`outputs\` keyed by slug, as the app's\
> \`input\_schema\` describes. Without \`version\`, the run executes the version\
> that is latest when it starts; with it, exactly that version, even after the\
> app is republished.\
> \
> \*\*Idempotency:\*\* an \`external\_id\` is unique per organization. Reusing one\
> answers \`409\` instead of starting (and charging) a second run; fetch the\
> first with \`GET /apps/{app}/runs?external\_id=...\`.\
> \
> \*\*Credits:\*\* the balance is not checked here. A run that exhausts it fails\
> with \`failure\_reason\` \`insufficient\_credits\`.\
> \
> \*\*Error Codes:\*\*\
> \- \`400\`: Unknown slug, missing required input, or a value the schema rejects\
> \- \`404\`: App or version not found or not accessible\
> \- \`409\`: \`external\_id\` already used in this organization\
> \- \`422\`: Malformed body\
> \- \`401\`: Missing or invalid API key or Bearer token\
> \- \`403\`: The API key is bound to a different organization than \`X-Organization-Id\`\
> \- \`429\`: Rate limit exceeded — retry after the \`Retry-After\` header

```json
{"openapi":"3.1.0","info":{"title":"Pletor - Public API","version":"2.0.0"},"tags":[{"name":"apps","description":"Discover apps, read their schema, and run them."}],"servers":[{"url":"/api/public"}],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key for authentication"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Cognito JWT Bearer token"}},"schemas":{"AppV2RunCreateRequest":{"properties":{"version":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"title":"Version","description":"App version to run. Omitted runs the latest version when the run starts; a pinned run keeps that version's inputs, settings, outputs and flow after the app is republished"},"inputs":{"additionalProperties":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"},{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object"}]},"type":"object","title":"Inputs","description":"Input values keyed by slug, as `input_schema.properties.inputs` describes. Text takes a string or a list of strings; media takes {\"asset_ids\": [...]}"},"settings":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"boolean"},{"type":"integer"},{"type":"number"}]},"type":"object","title":"Settings","description":"Setting values keyed by slug. Omitted settings use their default"},"outputs":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Outputs","description":"Output slugs to produce. Omitted produces every output; required outputs are always produced"},"external_id":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"External Id","description":"Your own reference for this run (max 255 chars), unique per organization: reusing one answers 409, so a retried request never starts a second run"}},"additionalProperties":false,"type":"object","title":"AppV2RunCreateRequest","description":"Request to start a run of an app."},"AppV2Run":{"properties":{"id":{"type":"string","format":"uuid","title":"Id","description":"Run identifier"},"app_id":{"type":"string","format":"uuid","title":"App Id","description":"Stable ID of the app"},"version":{"type":"integer","title":"Version","description":"App version the run executes"},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id","description":"Your reference, if any"},"status":{"$ref":"#/components/schemas/FlowRunStatus","description":"in_progress, then exactly one of completed, failed or canceled"},"progress":{"type":"number","maximum":1,"minimum":0,"title":"Progress","description":"Fraction of steps completed","default":0},"awaiting_human_review":{"type":"boolean","title":"Awaiting Human Review","description":"True while paused on a human review step","default":false},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the run was created"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At","description":"When it started"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At","description":"When it ended; null while in_progress"},"cost":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Cost","description":"Credits used so far; null before any step ran"},"pending_reviews":{"items":{"$ref":"#/components/schemas/AppV2PendingReview"},"type":"array","title":"Pending Reviews","description":"Review steps waiting for a decision; empty unless `awaiting_human_review`"},"inputs":{"additionalProperties":true,"type":"object","title":"Inputs","description":"The run's input values, keyed by slug"},"outputs":{"additionalProperties":true,"type":"object","title":"Outputs","description":"Each output's value, keyed by slug, as `output_schema` describes. An output appears once it is produced, so this fills in while in_progress. Media values are asset IDs: GET /assets/{id} resolves them"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Error message when the run failed or was canceled"},"failure_reason":{"anyOf":[{"$ref":"#/components/schemas/FailureReason"},{"type":"null"}],"description":"Categorized cause when failed or canceled"}},"type":"object","required":["id","app_id","version","status","created_at"],"title":"AppV2Run","description":"A run with its inputs and outputs, keyed by slug."},"FlowRunStatus":{"type":"string","enum":["in_progress","completed","failed","canceled"],"title":"FlowRunStatus","description":"Execution status of a workflow run."},"AppV2PendingReview":{"properties":{"id":{"type":"string","format":"uuid","title":"Id","description":"Use as `review_id` in POST /apps/{app}/runs/{run_id}/reviews/{review_id}"},"output_type":{"anyOf":[{"$ref":"#/components/schemas/DataType"},{"type":"null"}],"description":"Kind of output under review"},"proposed_output":{"anyOf":[{"additionalProperties":true,"type":"object"},{"items":{},"type":"array"},{"type":"string"},{"type":"null"}],"title":"Proposed Output","description":"What the reviewed step produced, e.g. {\"asset_ids\": [...]}. Submit it back, optionally trimmed, as `output` to approve"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the review was opened"}},"type":"object","required":["id","created_at"],"title":"AppV2PendingReview","description":"A human review step waiting for a decision."},"DataType":{"type":"string","enum":["text","images","videos","files","audio","data"],"title":"DataType","description":"Kind of content an output, run result or review carries. In run results,\n`text` values are text, `images` / `videos` / `audio` values are asset IDs\nto download, and `data` values are structured JSON."},"FailureReason":{"type":"string","enum":["timeout","content_policy","insufficient_credits","invalid_input","provider_error","provider_auth_required","canceled","unknown"],"title":"FailureReason","description":"Stable category for why a run failed: `timeout` (a step ran out of time),\n`content_policy` (blocked by content moderation), `insufficient_credits`\n(credit or quota exhausted), `invalid_input` (an input, or an uploaded file,\ncould not be used), `provider_error` (an upstream AI provider failed),\n`provider_auth_required` (a connected provider account is missing or expired\nand must be reconnected), `canceled` (the run was canceled), `unknown` (any\ncause without a category of its own)."},"ApiErrorResponse":{"description":"Standard error response envelope. All API errors follow this format.","properties":{"error":{"$ref":"#/components/schemas/ApiErrorDetail","description":"Error details"}},"required":["error"],"title":"ApiErrorResponse","type":"object"},"ApiErrorDetail":{"description":"Structured details for an API error.","properties":{"code":{"$ref":"#/components/schemas/ApiErrorCode","description":"Machine-readable error code; branch on this, not on `message`"},"message":{"description":"Human-readable error description","title":"Message","type":"string"},"status":{"description":"HTTP status code of this response","title":"Status","type":"integer"},"details":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"number"},{"type":"boolean"},{"items":{"type":"string"},"type":"array"},{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}]},"description":"Additional context. Always holds `request_id` (the response's `X-Request-Id`); `validation_error` from request validation adds `errors`, a list of `location: message` strings, and `rate_limit_exceeded` adds `retry_after`, the seconds to wait.","title":"Details","type":"object"}},"required":["code","message","status","details"],"title":"ApiErrorDetail","type":"object"},"ApiErrorCode":{"description":"Machine-readable error code, with its usual HTTP status: `invalid_input`\n400, `authentication_required` 401, `forbidden` 403, `not_found` 404,\n`conflict` 409, `validation_error` 422, `rate_limit_exceeded` 429,\n`internal_error` 500, `run_failed` 502, `service_unavailable` 503, `timeout`\n504. A 4xx status without a code of its own is sent as `invalid_input`.","enum":["invalid_input","authentication_required","forbidden","not_found","conflict","validation_error","rate_limit_exceeded","internal_error","run_failed","service_unavailable","timeout"],"title":"ApiErrorCode","type":"string"}}},"paths":{"/v2/apps/{app}/runs":{"post":{"tags":["apps"],"summary":"Create Run","description":"Start a run of the app.\n\nPass `inputs`, `settings` and `outputs` keyed by slug, as the app's\n`input_schema` describes. Without `version`, the run executes the version\nthat is latest when it starts; with it, exactly that version, even after the\napp is republished.\n\n**Idempotency:** an `external_id` is unique per organization. Reusing one\nanswers `409` instead of starting (and charging) a second run; fetch the\nfirst with `GET /apps/{app}/runs?external_id=...`.\n\n**Credits:** the balance is not checked here. A run that exhausts it fails\nwith `failure_reason` `insufficient_credits`.\n\n**Error Codes:**\n- `400`: Unknown slug, missing required input, or a value the schema rejects\n- `404`: App or version not found or not accessible\n- `409`: `external_id` already used in this organization\n- `422`: Malformed body\n- `401`: Missing or invalid API key or Bearer token\n- `403`: The API key is bound to a different organization than `X-Organization-Id`\n- `429`: Rate limit exceeded — retry after the `Retry-After` header","operationId":"v2_create_run_apps__app__runs_post","parameters":[{"name":"app","in":"path","required":true,"schema":{"type":"string","description":"The app's ID, or its `slug` (an app of your own organization)","title":"App"},"description":"The app's ID, or its `slug` (an app of your own organization)"},{"name":"X-Organization-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Active organization ID for multi-org support","title":"X-Organization-Id"},"description":"Active organization ID for multi-org support"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppV2RunCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppV2Run"}}}},"422":{"description":"Validation error (`validation_error`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}}}}}}
```

## Get Run

> Get a run, with its \`outputs\` keyed by slug.\
> \
> Poll until \`status\` leaves \`in\_progress\`. Outputs appear as they are\
> produced, so they can be partial while running or after a failure. Media\
> outputs are asset IDs: \`GET /v1/assets/{id}\` resolves them. While\
> \`awaiting\_human\_review\` is true the run is paused: resolve each of its\
> \`pending\_reviews\` within a week, or the run fails.\
> \
> \*\*Error Codes:\*\*\
> \- \`404\`: App not accessible, or no run of this app with that ID\
> \- \`422\`: \`run\_id\` is not a UUID\
> \- \`401\`: Missing or invalid API key or Bearer token\
> \- \`403\`: The API key is bound to a different organization than \`X-Organization-Id\`\
> \- \`429\`: Rate limit exceeded — retry after the \`Retry-After\` header

```json
{"openapi":"3.1.0","info":{"title":"Pletor - Public API","version":"2.0.0"},"tags":[{"name":"apps","description":"Discover apps, read their schema, and run them."}],"servers":[{"url":"/api/public"}],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key for authentication"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Cognito JWT Bearer token"}},"schemas":{"AppV2Run":{"properties":{"id":{"type":"string","format":"uuid","title":"Id","description":"Run identifier"},"app_id":{"type":"string","format":"uuid","title":"App Id","description":"Stable ID of the app"},"version":{"type":"integer","title":"Version","description":"App version the run executes"},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id","description":"Your reference, if any"},"status":{"$ref":"#/components/schemas/FlowRunStatus","description":"in_progress, then exactly one of completed, failed or canceled"},"progress":{"type":"number","maximum":1,"minimum":0,"title":"Progress","description":"Fraction of steps completed","default":0},"awaiting_human_review":{"type":"boolean","title":"Awaiting Human Review","description":"True while paused on a human review step","default":false},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the run was created"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At","description":"When it started"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At","description":"When it ended; null while in_progress"},"cost":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Cost","description":"Credits used so far; null before any step ran"},"pending_reviews":{"items":{"$ref":"#/components/schemas/AppV2PendingReview"},"type":"array","title":"Pending Reviews","description":"Review steps waiting for a decision; empty unless `awaiting_human_review`"},"inputs":{"additionalProperties":true,"type":"object","title":"Inputs","description":"The run's input values, keyed by slug"},"outputs":{"additionalProperties":true,"type":"object","title":"Outputs","description":"Each output's value, keyed by slug, as `output_schema` describes. An output appears once it is produced, so this fills in while in_progress. Media values are asset IDs: GET /assets/{id} resolves them"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Error message when the run failed or was canceled"},"failure_reason":{"anyOf":[{"$ref":"#/components/schemas/FailureReason"},{"type":"null"}],"description":"Categorized cause when failed or canceled"}},"type":"object","required":["id","app_id","version","status","created_at"],"title":"AppV2Run","description":"A run with its inputs and outputs, keyed by slug."},"FlowRunStatus":{"type":"string","enum":["in_progress","completed","failed","canceled"],"title":"FlowRunStatus","description":"Execution status of a workflow run."},"AppV2PendingReview":{"properties":{"id":{"type":"string","format":"uuid","title":"Id","description":"Use as `review_id` in POST /apps/{app}/runs/{run_id}/reviews/{review_id}"},"output_type":{"anyOf":[{"$ref":"#/components/schemas/DataType"},{"type":"null"}],"description":"Kind of output under review"},"proposed_output":{"anyOf":[{"additionalProperties":true,"type":"object"},{"items":{},"type":"array"},{"type":"string"},{"type":"null"}],"title":"Proposed Output","description":"What the reviewed step produced, e.g. {\"asset_ids\": [...]}. Submit it back, optionally trimmed, as `output` to approve"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the review was opened"}},"type":"object","required":["id","created_at"],"title":"AppV2PendingReview","description":"A human review step waiting for a decision."},"DataType":{"type":"string","enum":["text","images","videos","files","audio","data"],"title":"DataType","description":"Kind of content an output, run result or review carries. In run results,\n`text` values are text, `images` / `videos` / `audio` values are asset IDs\nto download, and `data` values are structured JSON."},"FailureReason":{"type":"string","enum":["timeout","content_policy","insufficient_credits","invalid_input","provider_error","provider_auth_required","canceled","unknown"],"title":"FailureReason","description":"Stable category for why a run failed: `timeout` (a step ran out of time),\n`content_policy` (blocked by content moderation), `insufficient_credits`\n(credit or quota exhausted), `invalid_input` (an input, or an uploaded file,\ncould not be used), `provider_error` (an upstream AI provider failed),\n`provider_auth_required` (a connected provider account is missing or expired\nand must be reconnected), `canceled` (the run was canceled), `unknown` (any\ncause without a category of its own)."},"ApiErrorResponse":{"description":"Standard error response envelope. All API errors follow this format.","properties":{"error":{"$ref":"#/components/schemas/ApiErrorDetail","description":"Error details"}},"required":["error"],"title":"ApiErrorResponse","type":"object"},"ApiErrorDetail":{"description":"Structured details for an API error.","properties":{"code":{"$ref":"#/components/schemas/ApiErrorCode","description":"Machine-readable error code; branch on this, not on `message`"},"message":{"description":"Human-readable error description","title":"Message","type":"string"},"status":{"description":"HTTP status code of this response","title":"Status","type":"integer"},"details":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"number"},{"type":"boolean"},{"items":{"type":"string"},"type":"array"},{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}]},"description":"Additional context. Always holds `request_id` (the response's `X-Request-Id`); `validation_error` from request validation adds `errors`, a list of `location: message` strings, and `rate_limit_exceeded` adds `retry_after`, the seconds to wait.","title":"Details","type":"object"}},"required":["code","message","status","details"],"title":"ApiErrorDetail","type":"object"},"ApiErrorCode":{"description":"Machine-readable error code, with its usual HTTP status: `invalid_input`\n400, `authentication_required` 401, `forbidden` 403, `not_found` 404,\n`conflict` 409, `validation_error` 422, `rate_limit_exceeded` 429,\n`internal_error` 500, `run_failed` 502, `service_unavailable` 503, `timeout`\n504. A 4xx status without a code of its own is sent as `invalid_input`.","enum":["invalid_input","authentication_required","forbidden","not_found","conflict","validation_error","rate_limit_exceeded","internal_error","run_failed","service_unavailable","timeout"],"title":"ApiErrorCode","type":"string"}}},"paths":{"/v2/apps/{app}/runs/{run_id}":{"get":{"tags":["apps"],"summary":"Get Run","description":"Get a run, with its `outputs` keyed by slug.\n\nPoll until `status` leaves `in_progress`. Outputs appear as they are\nproduced, so they can be partial while running or after a failure. Media\noutputs are asset IDs: `GET /v1/assets/{id}` resolves them. While\n`awaiting_human_review` is true the run is paused: resolve each of its\n`pending_reviews` within a week, or the run fails.\n\n**Error Codes:**\n- `404`: App not accessible, or no run of this app with that ID\n- `422`: `run_id` is not a UUID\n- `401`: Missing or invalid API key or Bearer token\n- `403`: The API key is bound to a different organization than `X-Organization-Id`\n- `429`: Rate limit exceeded — retry after the `Retry-After` header","operationId":"v2_get_run_apps__app__runs__run_id__get","parameters":[{"name":"app","in":"path","required":true,"schema":{"type":"string","description":"The app's ID, or its `slug` (an app of your own organization)","title":"App"},"description":"The app's ID, or its `slug` (an app of your own organization)"},{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"ID of the run","title":"Run Id"},"description":"ID of the run"},{"name":"X-Organization-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Active organization ID for multi-org support","title":"X-Organization-Id"},"description":"Active organization ID for multi-org support"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppV2Run"}}}},"422":{"description":"Validation error (`validation_error`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}}}}}}
```

## Cancel Run

> Cancel a run that is \`in\_progress\`, including one paused on a review.\
> \
> Cancellation lands within a few seconds; the run then reads \`canceled\`, and\
> stays readable.\
> \
> \*\*Error Codes:\*\*\
> \- \`404\`: App not accessible, no run of this app with that ID, or the run is\
> &#x20; no longer \`in\_progress\`\
> \- \`422\`: \`run\_id\` is not a UUID\
> \- \`401\`: Missing or invalid API key or Bearer token\
> \- \`403\`: The API key is bound to a different organization than \`X-Organization-Id\`\
> \- \`429\`: Rate limit exceeded — retry after the \`Retry-After\` header

```json
{"openapi":"3.1.0","info":{"title":"Pletor - Public API","version":"2.0.0"},"tags":[{"name":"apps","description":"Discover apps, read their schema, and run them."}],"servers":[{"url":"/api/public"}],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key for authentication"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Cognito JWT Bearer token"}},"schemas":{"AppV2Run":{"properties":{"id":{"type":"string","format":"uuid","title":"Id","description":"Run identifier"},"app_id":{"type":"string","format":"uuid","title":"App Id","description":"Stable ID of the app"},"version":{"type":"integer","title":"Version","description":"App version the run executes"},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id","description":"Your reference, if any"},"status":{"$ref":"#/components/schemas/FlowRunStatus","description":"in_progress, then exactly one of completed, failed or canceled"},"progress":{"type":"number","maximum":1,"minimum":0,"title":"Progress","description":"Fraction of steps completed","default":0},"awaiting_human_review":{"type":"boolean","title":"Awaiting Human Review","description":"True while paused on a human review step","default":false},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the run was created"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At","description":"When it started"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At","description":"When it ended; null while in_progress"},"cost":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Cost","description":"Credits used so far; null before any step ran"},"pending_reviews":{"items":{"$ref":"#/components/schemas/AppV2PendingReview"},"type":"array","title":"Pending Reviews","description":"Review steps waiting for a decision; empty unless `awaiting_human_review`"},"inputs":{"additionalProperties":true,"type":"object","title":"Inputs","description":"The run's input values, keyed by slug"},"outputs":{"additionalProperties":true,"type":"object","title":"Outputs","description":"Each output's value, keyed by slug, as `output_schema` describes. An output appears once it is produced, so this fills in while in_progress. Media values are asset IDs: GET /assets/{id} resolves them"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Error message when the run failed or was canceled"},"failure_reason":{"anyOf":[{"$ref":"#/components/schemas/FailureReason"},{"type":"null"}],"description":"Categorized cause when failed or canceled"}},"type":"object","required":["id","app_id","version","status","created_at"],"title":"AppV2Run","description":"A run with its inputs and outputs, keyed by slug."},"FlowRunStatus":{"type":"string","enum":["in_progress","completed","failed","canceled"],"title":"FlowRunStatus","description":"Execution status of a workflow run."},"AppV2PendingReview":{"properties":{"id":{"type":"string","format":"uuid","title":"Id","description":"Use as `review_id` in POST /apps/{app}/runs/{run_id}/reviews/{review_id}"},"output_type":{"anyOf":[{"$ref":"#/components/schemas/DataType"},{"type":"null"}],"description":"Kind of output under review"},"proposed_output":{"anyOf":[{"additionalProperties":true,"type":"object"},{"items":{},"type":"array"},{"type":"string"},{"type":"null"}],"title":"Proposed Output","description":"What the reviewed step produced, e.g. {\"asset_ids\": [...]}. Submit it back, optionally trimmed, as `output` to approve"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"When the review was opened"}},"type":"object","required":["id","created_at"],"title":"AppV2PendingReview","description":"A human review step waiting for a decision."},"DataType":{"type":"string","enum":["text","images","videos","files","audio","data"],"title":"DataType","description":"Kind of content an output, run result or review carries. In run results,\n`text` values are text, `images` / `videos` / `audio` values are asset IDs\nto download, and `data` values are structured JSON."},"FailureReason":{"type":"string","enum":["timeout","content_policy","insufficient_credits","invalid_input","provider_error","provider_auth_required","canceled","unknown"],"title":"FailureReason","description":"Stable category for why a run failed: `timeout` (a step ran out of time),\n`content_policy` (blocked by content moderation), `insufficient_credits`\n(credit or quota exhausted), `invalid_input` (an input, or an uploaded file,\ncould not be used), `provider_error` (an upstream AI provider failed),\n`provider_auth_required` (a connected provider account is missing or expired\nand must be reconnected), `canceled` (the run was canceled), `unknown` (any\ncause without a category of its own)."},"ApiErrorResponse":{"description":"Standard error response envelope. All API errors follow this format.","properties":{"error":{"$ref":"#/components/schemas/ApiErrorDetail","description":"Error details"}},"required":["error"],"title":"ApiErrorResponse","type":"object"},"ApiErrorDetail":{"description":"Structured details for an API error.","properties":{"code":{"$ref":"#/components/schemas/ApiErrorCode","description":"Machine-readable error code; branch on this, not on `message`"},"message":{"description":"Human-readable error description","title":"Message","type":"string"},"status":{"description":"HTTP status code of this response","title":"Status","type":"integer"},"details":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"number"},{"type":"boolean"},{"items":{"type":"string"},"type":"array"},{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}]},"description":"Additional context. Always holds `request_id` (the response's `X-Request-Id`); `validation_error` from request validation adds `errors`, a list of `location: message` strings, and `rate_limit_exceeded` adds `retry_after`, the seconds to wait.","title":"Details","type":"object"}},"required":["code","message","status","details"],"title":"ApiErrorDetail","type":"object"},"ApiErrorCode":{"description":"Machine-readable error code, with its usual HTTP status: `invalid_input`\n400, `authentication_required` 401, `forbidden` 403, `not_found` 404,\n`conflict` 409, `validation_error` 422, `rate_limit_exceeded` 429,\n`internal_error` 500, `run_failed` 502, `service_unavailable` 503, `timeout`\n504. A 4xx status without a code of its own is sent as `invalid_input`.","enum":["invalid_input","authentication_required","forbidden","not_found","conflict","validation_error","rate_limit_exceeded","internal_error","run_failed","service_unavailable","timeout"],"title":"ApiErrorCode","type":"string"}}},"paths":{"/v2/apps/{app}/runs/{run_id}/cancel":{"post":{"tags":["apps"],"summary":"Cancel Run","description":"Cancel a run that is `in_progress`, including one paused on a review.\n\nCancellation lands within a few seconds; the run then reads `canceled`, and\nstays readable.\n\n**Error Codes:**\n- `404`: App not accessible, no run of this app with that ID, or the run is\n  no longer `in_progress`\n- `422`: `run_id` is not a UUID\n- `401`: Missing or invalid API key or Bearer token\n- `403`: The API key is bound to a different organization than `X-Organization-Id`\n- `429`: Rate limit exceeded — retry after the `Retry-After` header","operationId":"v2_cancel_run_apps__app__runs__run_id__cancel_post","parameters":[{"name":"app","in":"path","required":true,"schema":{"type":"string","description":"The app's ID, or its `slug` (an app of your own organization)","title":"App"},"description":"The app's ID, or its `slug` (an app of your own organization)"},{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"ID of the run","title":"Run Id"},"description":"ID of the run"},{"name":"X-Organization-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Active organization ID for multi-org support","title":"X-Organization-Id"},"description":"Active organization ID for multi-org support"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppV2Run"}}}},"422":{"description":"Validation error (`validation_error`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}}}}}}
```

## Submit Review

> Resolve a pending human review and resume the run.\
> \
> \- \`approve\` continues with \`output\`, in the shape of the review's\
> &#x20; \`proposed\_output\`. Keep only the items you want; at least one.\
> \- \`retry\` regenerates the reviewed step. The same review stays pending and\
> &#x20; its \`proposed\_output\` refreshes when the new one is ready.\
> \
> \*\*Error Codes:\*\*\
> \- \`400\`: Missing, empty or mismatched \`output\` on \`approve\`\
> \- \`404\`: App not accessible, no run of this app with that ID, or no such review\
> \- \`409\`: The review is no longer waiting\
> \- \`422\`: Malformed body or path ID\
> \- \`401\`: Missing or invalid API key or Bearer token\
> \- \`403\`: The API key is bound to a different organization than \`X-Organization-Id\`\
> \- \`429\`: Rate limit exceeded — retry after the \`Retry-After\` header

```json
{"openapi":"3.1.0","info":{"title":"Pletor - Public API","version":"2.0.0"},"tags":[{"name":"apps","description":"Discover apps, read their schema, and run them."}],"servers":[{"url":"/api/public"}],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"API key for authentication"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Cognito JWT Bearer token"}},"schemas":{"SubmitReviewRequest":{"properties":{"decision":{"$ref":"#/components/schemas/ReviewDecision","description":"`approve` resumes execution using the (optionally edited) output. `retry` re-runs the upstream node(s) to produce a new candidate output, then waits for another decision on the same review."},"output":{"anyOf":[{"additionalProperties":true,"type":"object"},{"items":{},"type":"array"},{"type":"string"},{"type":"null"}],"title":"Output","description":"Output to use when continuing execution. Required when decision is `approve` (missing → 400). Ignored when decision is `retry`. Must be an object matching the shape of `proposed_output` ({\"asset_ids\": [...]} for image/video/audio reviews, {\"prompts\": [...]} for text reviews — a text node's own key such as `texts` is also accepted); a mismatched shape is rejected with 400. To discard items, submit only the ones you want to keep; the rest are dropped. At least one item is required — an empty output is rejected with 400."}},"type":"object","required":["decision"],"title":"SubmitReviewRequest","description":"Request to resolve a pending human review."},"ReviewDecision":{"type":"string","enum":["approve","retry"],"title":"ReviewDecision","description":"Possible decisions when responding to a pending human review.\n\n`approve` resumes the run with the submitted output; `retry` re-runs the\nreviewed upstream node(s) and waits for a new decision on the same review."},"ApiErrorResponse":{"description":"Standard error response envelope. All API errors follow this format.","properties":{"error":{"$ref":"#/components/schemas/ApiErrorDetail","description":"Error details"}},"required":["error"],"title":"ApiErrorResponse","type":"object"},"ApiErrorDetail":{"description":"Structured details for an API error.","properties":{"code":{"$ref":"#/components/schemas/ApiErrorCode","description":"Machine-readable error code; branch on this, not on `message`"},"message":{"description":"Human-readable error description","title":"Message","type":"string"},"status":{"description":"HTTP status code of this response","title":"Status","type":"integer"},"details":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"number"},{"type":"boolean"},{"items":{"type":"string"},"type":"array"},{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}]},"description":"Additional context. Always holds `request_id` (the response's `X-Request-Id`); `validation_error` from request validation adds `errors`, a list of `location: message` strings, and `rate_limit_exceeded` adds `retry_after`, the seconds to wait.","title":"Details","type":"object"}},"required":["code","message","status","details"],"title":"ApiErrorDetail","type":"object"},"ApiErrorCode":{"description":"Machine-readable error code, with its usual HTTP status: `invalid_input`\n400, `authentication_required` 401, `forbidden` 403, `not_found` 404,\n`conflict` 409, `validation_error` 422, `rate_limit_exceeded` 429,\n`internal_error` 500, `run_failed` 502, `service_unavailable` 503, `timeout`\n504. A 4xx status without a code of its own is sent as `invalid_input`.","enum":["invalid_input","authentication_required","forbidden","not_found","conflict","validation_error","rate_limit_exceeded","internal_error","run_failed","service_unavailable","timeout"],"title":"ApiErrorCode","type":"string"}}},"paths":{"/v2/apps/{app}/runs/{run_id}/reviews/{review_id}":{"post":{"tags":["apps"],"summary":"Submit Review","description":"Resolve a pending human review and resume the run.\n\n- `approve` continues with `output`, in the shape of the review's\n  `proposed_output`. Keep only the items you want; at least one.\n- `retry` regenerates the reviewed step. The same review stays pending and\n  its `proposed_output` refreshes when the new one is ready.\n\n**Error Codes:**\n- `400`: Missing, empty or mismatched `output` on `approve`\n- `404`: App not accessible, no run of this app with that ID, or no such review\n- `409`: The review is no longer waiting\n- `422`: Malformed body or path ID\n- `401`: Missing or invalid API key or Bearer token\n- `403`: The API key is bound to a different organization than `X-Organization-Id`\n- `429`: Rate limit exceeded — retry after the `Retry-After` header","operationId":"v2_submit_review_apps__app__runs__run_id__reviews__review_id__post","parameters":[{"name":"app","in":"path","required":true,"schema":{"type":"string","description":"The app's ID, or its `slug` (an app of your own organization)","title":"App"},"description":"The app's ID, or its `slug` (an app of your own organization)"},{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"ID of the run","title":"Run Id"},"description":"ID of the run"},{"name":"review_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"`id` of an entry in the run's `pending_reviews`","title":"Review Id"},"description":"`id` of an entry in the run's `pending_reviews`"},{"name":"X-Organization-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Active organization ID for multi-org support","title":"X-Organization-Id"},"description":"Active organization ID for multi-org support"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitReviewRequest"}}}},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation error (`validation_error`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}}}}}}
```
