{"openapi":"3.0.0","info":{"title":"AI Pipeline API","version":"1.0.0","description":"Crowdin integration for AI-powered translation pipeline","contact":{"name":"Crowdin Team"},"license":{"name":"MIT","url":"https://opensource.org/licenses/MIT"}},"servers":[{"url":"https://q7jzzl34byh3bkcwuutbwpigym0ddlxv.lambda-url.us-east-1.on.aws","description":"AI Pipeline API Server"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token from Crowdin API"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"Error message"},"errorCode":{"type":"string","description":"Error code"},"timestamp":{"type":"string","format":"date-time","description":"Error timestamp"}},"required":["error","errorCode"]},"Success":{"type":"object","properties":{"success":{"type":"boolean","description":"Operation success status"},"message":{"type":"string","description":"Success message"}},"required":["success"]},"PipelineLog":{"type":"object","description":"One pipeline run: one file and one target language of a pre-translation","properties":{"logId":{"type":"string","format":"uuid"},"projectId":{"type":"integer"},"filePath":{"type":"string"},"sourceLanguageId":{"type":"string","nullable":true,"description":"A Crowdin language id, in Crowdin's case","example":"en"},"sourceLanguageName":{"type":"string","description":"The display name the log was written with (the id when Crowdin sent no name)"},"targetLanguageId":{"type":"string","nullable":true,"description":"A Crowdin language id, in Crowdin's case","example":"pt-BR"},"targetLanguageName":{"type":"string"},"status":{"type":"string","enum":["pending","translating","completed","error"],"description":"A log still `translating` long after the 15-minute execution limit belongs to a run that was cut off"},"progress":{"type":"number","description":"0 to 100"},"totalBatches":{"type":"integer"},"totalSteps":{"type":"integer","description":"Steps in the pipeline config, including any a batch skipped"},"startDate":{"type":"string","format":"date-time","nullable":true},"endDate":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time","nullable":true},"errorMessage":{"type":"string","nullable":true},"preTranslationId":{"type":"string","nullable":true,"description":"The pre-translation the run belonged to. null for runs before 2026-04-17, which did not record it"},"isQaCheckRetry":{"type":"boolean","description":"The run re-translates strings a previous run left with QA issues"},"aiPromptId":{"type":"integer","nullable":true},"promptName":{"type":"string","nullable":true,"description":"The prompt's current name; null when it was deleted or could not be read"}}},"PipelineLogConfigStep":{"type":"object","description":"A step of the pipeline config the run was started with","properties":{"stepNumber":{"type":"integer","description":"Position in the config, 1-based: what `stepNumber` on a step row refers to"},"displayStepNumber":{"type":"integer","description":"The step's number in the FlowBuilder diagram: the variants of one step (consecutive nodes sharing a variantGroup) share it"},"id":{"type":"string"},"type":{"type":"string","example":"translation"},"name":{"type":"string"},"builtinId":{"type":"string"},"variantGroup":{"type":"string"},"languages":{"type":"array","items":{"type":"string"}},"filePathPatterns":{"type":"array","items":{"type":"string"}},"model":{"type":"string","description":"\"providerId::modelId\"","example":"12::gpt-5"},"onAmbiguous":{"type":"string","enum":["hold","translate-unapproved"],"description":"Only on an `unambiguous-filter` step: the setting for the strings it flags, in the config's own words, not what the run did with them. \"hold\" keeps them back with no translation. \"translate-unapproved\" translates them and leaves them unapproved, but only when log.autoApprove is true and a Translate step runs for the batch, before or after the filter; otherwise the run holds them back. Any other value in the config, or none, reads \"hold\". For what a step actually did, read that step's ambiguity.mode"},"prompt":{"type":"string","description":"Only with `include=prompt`"}}},"PipelineLogStep":{"type":"object","description":"One execution of a step on a batch. A QA step that retries runs again under the same `stepNumber`. A configured step has no row for a batch it did not run on: its language or file scope excluded the batch, or it is an ambiguity filter and every string was shorter than its minimum length.","properties":{"stepId":{"type":"string","description":"Pass as `stepId` to get this step on its own, with everything a page had to leave out"},"stepNumber":{"type":"integer","description":"The node's position in the recorded pipeline"},"displayStepNumber":{"type":"integer","description":"The step's number in the FlowBuilder diagram; absent when the config could not be read"},"name":{"type":"string"},"type":{"type":"string"},"builtinId":{"type":"string"},"variantGroup":{"type":"string"},"model":{"type":"string"},"status":{"type":"string","enum":["pending","translating","completed","error"]},"startDate":{"type":"string","format":"date-time","nullable":true},"endDate":{"type":"string","format":"date-time","nullable":true},"note":{"type":"string","description":"Present when the row did not call the model: why it was skipped, or that the QA checks passed","example":"skipped: another variant in this group already ran"},"aiResponse":{"type":"string","nullable":true,"description":"The model's raw output, usually a JSON document with the strings by id; the error message when the step failed"},"aiResponseOmitted":{"type":"boolean","description":"The reply was left out, because its batch alone is over what one response can carry: get it with `stepId`"},"explanation":{"description":"A filter step's reasons by string key (\"id\" or \"id:pluralForm\"). A single string when the model gave one reason for the whole batch","oneOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"string"}]},"explanationStringKeys":{"type":"array","items":{"type":"string"},"description":"For a single-string explanation: the strings it is about, when the log recorded them"},"explanationOmitted":{"type":"boolean","description":"The reasons were left out, as with `aiResponseOmitted`: get them with `stepId`"},"ambiguity":{"type":"object","description":"An ambiguity filter step's verdict: which strings it flagged and what the run did with them. Absent when the step recorded none, which is the case for every log written before the verdict was recorded","required":["mode","stringKeys"],"properties":{"mode":{"type":"string","enum":["skip","translate"],"description":"\"skip\": the flagged strings were held back with no translation (the step was set to \"hold\", or its \"translate-unapproved\" could not apply). \"translate\": they were translated and left unapproved (the step was set to \"translate-unapproved\"). Any other recorded behaviour reads \"skip\""},"stringKeys":{"type":"array","items":{"type":"string"},"description":"The flagged strings, as \"id\" or \"id:pluralForm\"; never empty"}},"example":{"mode":"translate","stringKeys":["131","132:one"]}},"ambiguityOmitted":{"type":"boolean","description":"The verdict was left out, as with `explanationOmitted`: get it with `stepId`"},"input":{"oneOf":[{"type":"object"},{"type":"string"}],"nullable":true,"description":"Only with `include=input`: everything the step's prompt was built from (the batch strings with their glossary terms and TM matches, file context, and so on). null when the step stored none, the raw text when what it stored is not JSON. The style guide is stored once per log, in the request (`include=request`, `payload.styleGuide`), so a step that rendered it whole carries `styleGuideFromRequest: true` instead of a copy; `styleGuide` is present when the model saw something else: the guide cut to fit, or 'N/A' when the request has none. A log written before then carries `styleGuide` whenever the step's prompt rendered one."},"inputOmitted":{"type":"boolean","description":"The input was asked for but left out, because its batch alone is over the page budget: get it with `stepId`"}}},"PipelineLogBatch":{"type":"object","properties":{"batchNumber":{"type":"integer"},"status":{"type":"string","enum":["pending","translating","completed","error"]},"startDate":{"type":"string","format":"date-time","nullable":true},"endDate":{"type":"string","format":"date-time","nullable":true},"steps":{"type":"array","items":{"$ref":"#/components/schemas/PipelineLogStep"}}}}}},"security":[{"bearerAuth":[]}],"paths":{"/pre-translation-summary":{"get":{"summary":"Get pre-translation summary (all, ambiguous, translated and approvable string IDs)","description":"Pipeline logs are kept for 30 days (LOG_RETENTION_DAYS), and a pre-translation is deleted as a whole once all of its runs are older than that: afterwards the answer is 404.","tags":["Logs"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"query","name":"preTranslationId","required":true,"schema":{"type":"string"},"description":"Pre-translation identifier (UUID)"}],"responses":{"200":{"description":"Pre-translation summary with stringKeys, ambiguityStrings with their mode, translatedStrings and approvableStrings. Keys are not per target language: the summary merges the logs of every file and target language of the pre-translation, so a key flagged in any one log counts as flagged, even when the log of another language had it clean","content":{"application/json":{"schema":{"type":"object","properties":{"stringKeys":{"type":"array","items":{"type":"string"},"description":"String keys \"id\" or \"id:pluralForm\"","example":["131","132:one","145:other","200:none","201:few"]},"filteredStrings":{"type":"object","deprecated":true,"additionalProperties":{"type":"string"},"description":"Deprecated, read ambiguityStrings together with mode instead: this map only ever lists the strings that were held back, never the ones translated and left unapproved. Maps a held back string key (\"id\" or \"id:pluralForm\") to the reason the string was filtered, or an empty string when no reason was recorded. When the model gave one reason for a whole batch, that reason is under the key `batch`, after the keys of the strings it covers (each of which points there); several such reasons are separated by a blank line. These strings received no translation at all, so they are absent from translatedStrings","example":{"131":"Context mismatch","132:one":"Another explanation","145:other":""}},"ambiguityStrings":{"type":"array","description":"Every string the ambiguity filter flagged, sorted by key, with what the run did with it and the explanation recorded for it when there is one. A key appears once: when logs flagged it both ways, its mode is \"skip\"","items":{"type":"object","required":["key","mode"],"properties":{"key":{"type":"string","description":"String key \"id\" or \"id:pluralForm\""},"mode":{"type":"string","enum":["skip","translate"],"description":"\"skip\": held back with no translation (the filter step was set to hold). \"translate\": translated and left unapproved for a human to confirm (the filter step was set to translate unapproved). A step that recorded any other behaviour, or none, reads \"skip\""},"explanation":{"type":"string","description":"Why the string was flagged; the property is absent when no reason was recorded for this string alone. A reason the model gave for a whole batch is not copied here: it is in ambiguityBatchReasons"}}},"example":[{"key":"131","mode":"skip","explanation":"Context mismatch"},{"key":"132:one","mode":"skip","explanation":"Another explanation"},{"key":"145:other","mode":"skip"},{"key":"200:none","mode":"translate"}]},"ambiguityBatchReasons":{"type":"array","description":"Reasons the model gave for a whole batch rather than per string, in either mode, including those of logs written before ambiguityStrings had a mode. One entry per filter step row that recorded such a reason. Sorted by the first of its keys (an entry with no keys comes first), then by explanation, then by the whole key list","items":{"type":"object","required":["keys","explanation"],"properties":{"keys":{"type":"array","items":{"type":"string"},"description":"The strings the reason is about, as \"id\" or \"id:pluralForm\". Empty for an old log that did not record them"},"explanation":{"type":"string","description":"The reason, as the model gave it"}}},"example":[{"keys":["200:none"],"explanation":"The batch mixes button labels and error messages without context"}]},"mode":{"type":"string","nullable":true,"enum":["skip","translate",null],"description":"One answer for the whole run, derived from the mode of each ambiguityStrings entry: \"skip\" when any entry is \"skip\", \"translate\" when every entry is \"translate\", null when ambiguityStrings is empty. Under \"skip\" some keys may still have been translated: the mode of each entry, or translatedStrings, says which","example":"skip"},"translatedStrings":{"type":"array","items":{"type":"string"},"description":"stringKeys for which a translation was delivered, that is stringKeys minus the filteredStrings keys. It includes every ambiguityStrings key whose mode is \"translate\", on purpose","example":["200:none","201:few"]},"autoApprove":{"type":"boolean","description":"true when at least one log of this pre-translation ran with auto-approve on (`pipelineConfig.autoApprove` in its request). A log whose request cannot be read counts as off","example":true},"approvableStrings":{"type":"array","items":{"type":"string"},"description":"Sorted keys that would be approved under the pipeline's rules: the keys that every log carrying them ran with auto-approve and did not end in error, minus every ambiguityStrings key (either mode) and every filteredStrings key, so always a subset of translatedStrings and never an ambiguityStrings key. Empty when autoApprove is false. It says what would be approved, not what was: the app does not know what Crowdin actually approved, and Crowdin can still decline an approval (for example a closed gate, or a user below the proofreading step). Keys are not per target language, so a key flagged in the log of any language, or carried by the log of a language that ran without auto-approve or ended in error, is left out for all of them. It is derived from what the logs recorded, so it does not account for a batch that failed or timed out while other batches of its log completed, a log that is still running, or a string a step could not translate: read it as not flagged, and carried only by logs that ran with auto-approve and did not end in error","example":["201:few"]}}}}}},"400":{"description":"Bad request"},"401":{"description":"Unauthorized"},"403":{"description":"The AI Pipeline app is not installed in this organization (APP_NOT_INSTALLED), or it has lost its access to it (APP_NOT_AUTHORIZED): reinstall the app"},"404":{"description":"Pre-translation not found, or caller has no access to the owning project"},"429":{"description":"Crowdin is rate limiting this app; retry later"},"500":{"description":"Internal server error"},"502":{"description":"Crowdin did not answer the access check; retry later"}}}},"/step-prompt-catalog":{"get":{"summary":"Get the prompts the built-in pipeline steps ship with","description":"Without `id`, an index: the step kinds, their byte length and the placeholders each uses, but no prompt bodies. With `id`, the full body of the requested entries, verbatim — a step whose `settings.prompt` is byte-identical to its built-in default keeps receiving improvements to that default, while any edit is stored as a custom override.","tags":["Pipeline"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"query","name":"id","required":false,"schema":{"type":"string"},"description":"Comma-separated catalog ids or builtinIds (e.g. `translate,qa_check`). Omit for the index."}],"responses":{"200":{"description":"Catalog entries plus the config contract, step types and valid placeholders","content":{"application/json":{"schema":{"type":"object","properties":{"meta":{"type":"object","description":"Constants a config has to get right, the FlowNode type of every built-in, and every placeholder a prompt can use","properties":{"config":{"type":"object","description":"The values a prompt payload must carry to reach this app: `action`, `mode`, `identifier`, `key`, `retryOnQaIssues`, `aiModelId`, where the steps live (`flowNodesPath`), and a note on resolving `aiProviderId` (no constant — it is this organization's \"AI Pipeline\" provider id)"},"nodeTypes":{"type":"array","items":{"type":"string"},"description":"Every FlowNode `type` the catalog covers"},"nodeTypeByBuiltinId":{"type":"object","additionalProperties":{"type":"string"},"description":"Map from `builtinId` to the FlowNode `type` that built-in serializes to"},"placeholders":{"type":"object","description":"Everything a prompt body can reference: `dotted` (the `input.…` paths), `legacy` (flat aliases mapped to their dotted forms), `snippet` (the `{{custom:…}}` shape)"}}},"prompts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"translate"},"builtinId":{"type":"string","nullable":true,"description":"The built-in this prompt is the default for, or null when it is not a built-in default"},"nodeType":{"type":"string","example":"translation"},"title":{"type":"string"},"use":{"type":"string"},"bytes":{"type":"integer"},"placeholders":{"type":"array","items":{"type":"string"}},"prompt":{"type":"string","description":"Present only when the entry was requested by id"}}}}}}}}},"400":{"description":"Bad request"},"401":{"description":"Unauthorized"},"404":{"description":"No catalog entry matches the requested id"},"500":{"description":"Internal server error"}}}},"/pipeline-inventory":{"get":{"summary":"List the AI Pipeline prompts of the caller organization, without their prompt bodies","description":"Answers \"what pipelines exist here and what is in them\" in a few KB. The Crowdin prompt list cannot: `config` is a default field of that resource, so every row carries its full step bodies — about 6 KB per prompt, past what one tool result can hold for a dozen pipelines. Each step is described instead: type, name, builtinId, scope, model, its prompt's byte length, and whether that prompt is still the one this app ships. The organization and user come from the JWT, never from the request; `projectId` only narrows. On Crowdin Enterprise the caller's entitlement is checked first: an organization admin gets the organization-wide list, anyone else must pass a `projectId` of a project they are a member of.","tags":["Pipeline"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"query","name":"projectId","required":false,"schema":{"type":"integer","minimum":1},"description":"Only prompts available to this project. On Enterprise, required for callers who are not organization admins."}],"responses":{"200":{"description":"The pipelines, described without prompt bodies","content":{"application/json":{"schema":{"type":"object","properties":{"prompts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"isEnabled":{"type":"boolean"},"enabledProjectIds":{"type":"array","nullable":true,"items":{"type":"integer"}},"aiProviderId":{"type":"integer"},"aiModelId":{"type":"string","description":"Always the literal \"Crowdin\" for this provider; the real models are per step"},"retryOnQaIssues":{"type":"boolean"},"stepCount":{"type":"integer"},"steps":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"id":{"type":"string"},"type":{"type":"string"},"name":{"type":"string"},"builtinId":{"type":"string"},"languages":{"type":"array","items":{"type":"string"}},"filePathPatterns":{"type":"array","items":{"type":"string"}},"variantGroup":{"type":"string"},"model":{"type":"string"},"promptBytes":{"type":"integer"},"matchesDefault":{"type":"boolean","description":"True when the prompt body is byte-identical to a prompt this app ships (see matchesDefaultOf) — the organization has not tuned this step. A statement about the text, not about updates: entries without a builtinId (artifact_correction) are stored by the FlowBuilder as frozen overrides even when untouched."},"matchesDefaultOf":{"type":"string","description":"The catalog entry id whose prompt this body matches"}}}}}}},"scanned":{"type":"object","description":"How many prompts were read (the whole list is paged through, not one page) and how many of them are pipelines","properties":{"total":{"type":"integer"},"pipelines":{"type":"integer"}}}}}}}},"400":{"description":"Bad request"},"401":{"description":"Unauthorized"},"403":{"description":"Enterprise only: no projectId was passed and the caller could not be verified as an organization admin. On installations that have not yet granted the `user` scope (app update pending) verification always fails, so every caller must pass projectId. Also when the AI Pipeline app is not installed in this organization (APP_NOT_INSTALLED), or has lost its access to it (APP_NOT_AUTHORIZED): reinstall the app"},"404":{"description":"Enterprise only: the caller has no access to the requested project"},"429":{"description":"Enterprise only: Crowdin is rate limiting this app; retry later"},"500":{"description":"Internal server error"},"502":{"description":"Enterprise only: Crowdin did not answer the access check; retry later"}}}},"/validate-pipeline":{"post":{"summary":"Check an AI Pipeline prompt config before writing it to Crowdin","description":"Structural validation only — no project or organization state is consulted, so this works before the prompt exists. `errors` are configurations that are broken (several are accepted silently by the Crowdin API and only fail during a real pre-translation); `warnings` are judgement calls; `unchecked` lists what this endpoint cannot see and the caller still has to verify.","tags":["Pipeline"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"config":{"type":"object","description":"The prompt `config` object, exactly as it would be sent to Crowdin"},"flowNodes":{"type":"array","items":{"type":"object"},"description":"The step array on its own, when there is no envelope to check"},"action":{"type":"string","description":"Optional: the prompt-level `action`. Send it when validating a whole prompt payload — the create API requires it, and only \"pre_translate\" reaches this app"}}}}}},"responses":{"200":{"description":"Validation result. A 200 means the config was inspected, not that it is valid — read `valid`.","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean","description":"True when `errors` is empty. Warnings do not make a config invalid."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","example":"flow-nodes-misplaced"},"path":{"type":"string","example":"config.options.flowNodes[2].settings.model"},"message":{"type":"string"},"fix":{"type":"string"}}}},"warnings":{"type":"array","items":{"type":"object"}},"summary":{"type":"object","description":"What the validator read: step count, and each step's type, name and scope","properties":{"nodeCount":{"type":"integer"},"steps":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","description":"Position in the submitted array — malformed entries keep their slot"},"id":{"type":"string"},"type":{"type":"string"},"builtinId":{"type":"string"},"name":{"type":"string"},"languages":{"type":"array","items":{"type":"string"}},"filePathPatterns":{"type":"array","items":{"type":"string"}},"variantGroup":{"type":"string"}}}}}},"unchecked":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"Bad request"},"401":{"description":"Unauthorized"},"500":{"description":"Internal server error"}}}},"/pipeline-logs":{"get":{"summary":"List the AI Pipeline logs of a project, newest first","description":"One log per pipeline run: each run covers one file and one target language, and a QA-check retry is a run of its own. Every filter narrows the project's logs. To read them all, pass each response's `nextCursor` back as `cursor` until it is null: the cursor neither repeats nor skips a log while runs start or change status, and a page deep in a big project answers as fast as the first one. There is no offset and no total count, since both would have to read every log they cover. The organization comes from the JWT, and the caller has to be able to see the project (a member, or an organization admin on Enterprise; not a user blocked in it): otherwise the answer is 404. Runs of AI Translate requests, which have no project, are not listed. Logs are kept for 30 days (LOG_RETENTION_DAYS); a run older than that is deleted and no longer listed.","tags":["Logs"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"query","name":"projectId","required":true,"schema":{"type":"integer","minimum":1}},{"in":"query","name":"preTranslationId","required":false,"schema":{"type":"string"},"description":"Only the runs of this pre-translation (its `identifier` in GET /projects/{projectId}/pre-translations). Runs before 2026-04-17 did not record it and are not matched"},{"in":"query","name":"status","required":false,"schema":{"type":"string","enum":["pending","translating","completed","error"]}},{"in":"query","name":"targetLanguageId","required":false,"schema":{"type":"string"},"description":"Matched in any case","example":"pt-BR"},{"in":"query","name":"createdAfter","required":false,"schema":{"type":"string","format":"date-time"},"description":"Inclusive. An ISO 8601 date (2026-09-01, read as midnight UTC) or a date-time with a zone (2026-09-01T10:00:00Z; send a + offset as %2B03:00)"},{"in":"query","name":"createdBefore","required":false,"schema":{"type":"string","format":"date-time"},"description":"Exclusive, so back-to-back windows never return a log twice. Same formats as createdAfter"},{"in":"query","name":"limit","required":false,"schema":{"type":"integer","minimum":1,"maximum":500,"default":25}},{"in":"query","name":"cursor","required":false,"schema":{"type":"string"},"description":"The `nextCursor` of the previous page: continue below its last log"}],"responses":{"200":{"description":"A page of logs","content":{"application/json":{"schema":{"type":"object","properties":{"logs":{"type":"array","items":{"$ref":"#/components/schemas/PipelineLog"}},"limit":{"type":"integer"},"nextCursor":{"type":"string","nullable":true,"description":"Pass as `cursor` for the next page; null after the last one"}}}}}},"400":{"description":"Bad request: a parameter is missing or malformed"},"401":{"description":"Unauthorized"},"403":{"description":"The AI Pipeline app is not installed in this organization (APP_NOT_INSTALLED), or it has lost its access to it (APP_NOT_AUTHORIZED): reinstall the app"},"404":{"description":"The project does not exist, or the caller cannot see it"},"429":{"description":"Crowdin is rate limiting this app; retry later"},"500":{"description":"Internal server error"},"502":{"description":"Crowdin did not answer the access check; retry later"}}}},"/pipeline-log-details":{"get":{"summary":"Get one AI Pipeline log with a page of its batches and steps","description":"Paged by batch number, because a log grows with its batches. By default each step carries what it produced: the model response, a filter's reasons, why it was skipped. `include` adds what it was given (`input`), the prompt bodies of the pipeline (`prompt`) and the request the run started with (`request`). A page is also capped at about 3 MB, so pass `pagination.nextFromBatch` back as `fromBatch` until it is null instead of counting on `limit` batches per page. Every page holds at least one batch. The request goes out only when it fits alongside the page's first batch; otherwise `log.requestOmitted` is set, and `limit=0` gets it alone (up to 5 MB). When a batch is over the cap on its own, its steps leave out their inputs, largest first (`inputOmitted`), and when even that is more than one response can carry (about 5 MB), their replies, reasons and ambiguity verdicts too (`aiResponseOmitted`, `explanationOmitted`, `ambiguityOmitted`): `stepId` gets such a step whole. The log must belong to the caller's organization and to a project the caller can see; otherwise the answer is 404.","tags":["Logs"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"query","name":"logId","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"include","required":false,"schema":{"type":"string"},"description":"Comma-separated, any of `input`, `prompt`, `request`: `input` (each step's input), `prompt` (prompt bodies in `log.pipeline`), `request` (the run's request as `log.request`)","example":"input,prompt"},{"in":"query","name":"limit","required":false,"schema":{"type":"integer","minimum":0,"maximum":50,"default":10},"description":"Batches per page. 0 gives the log without batches, to get a request too large to come with them"},{"in":"query","name":"fromBatch","required":false,"schema":{"type":"integer","minimum":0},"description":"The first batch number the page may hold: `pagination.nextFromBatch` of the previous page. Omit for the first page"},{"in":"query","name":"stepId","required":false,"schema":{"type":"string"},"description":"One step on its own, as the only step of its batch, with everything `include` asks for. Not combined with `limit` or `fromBatch`"}],"responses":{"200":{"description":"The log and one page of its batches","content":{"application/json":{"schema":{"type":"object","properties":{"log":{"allOf":[{"$ref":"#/components/schemas/PipelineLog"},{"type":"object","properties":{"pipeline":{"type":"array","description":"The pipeline config the run was started with","items":{"$ref":"#/components/schemas/PipelineLogConfigStep"}},"autoApprove":{"type":"boolean","description":"The run had auto-approve on (`pipelineConfig.autoApprove` in its request). false when the log's request body could not be read or does not set it. It says the pipeline would approve what it is confident about, not what Crowdin actually approved"},"request":{"type":"object","nullable":true,"description":"Only with `include=request`; null when the log's request body could not be read"},"requestOmitted":{"type":"boolean","description":"The request was asked for but does not fit alongside the page's first batch: ask with `limit=0` to get it alone"}}}]},"batches":{"type":"array","items":{"$ref":"#/components/schemas/PipelineLogBatch"}},"pagination":{"type":"object","properties":{"limit":{"type":"integer"},"total":{"type":"integer","description":"Batches in the log"},"nextFromBatch":{"type":"integer","nullable":true,"description":"Pass as `fromBatch` for the next page; null after the last one"}}}}}}}},"400":{"description":"Bad request: a parameter is missing or malformed"},"401":{"description":"Unauthorized"},"403":{"description":"The AI Pipeline app is not installed in this organization (APP_NOT_INSTALLED), or it has lost its access to it (APP_NOT_AUTHORIZED): reinstall the app"},"404":{"description":"The log does not exist, the caller cannot see it, or it has no step with that stepId"},"429":{"description":"Crowdin is rate limiting this app; retry later"},"500":{"description":"Internal server error"},"502":{"description":"Crowdin did not answer the access check; retry later"}}}}}}