{"openapi":"3.0.0","paths":{"/api/public/v1/me":{"get":{"description":"Echoes the account the API key belongs to and the key in use. The cheapest way to check a key works.","operationId":"MeController_me","parameters":[],"responses":{"200":{"description":"The account and the key in use.","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string","nullable":true}}},"api_key":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"key_prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}}}}}}}},"401":{"description":"Missing, revoked or expired API key."}},"security":[{"api_key":[]}],"summary":"Who am I","tags":["Identity"]}},"/api/public/v1/documents":{"post":{"description":"Parses `content` into blocks and, with `target_languages`, fans out one language version per target. Send an `Idempotency-Key` to make a retry safe.","operationId":"PublicDocumentsController_create","parameters":[{"name":"Idempotency-Key","in":"header","description":"Retry-safety token. A repeat of the same request replays the first response instead of creating a second document. Honored for 24h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicDocumentDto"}}}},"responses":{"201":{"description":"The document and its language group."},"409":{"description":"A request with this Idempotency-Key is still in flight."},"422":{"description":"This Idempotency-Key was used with a different body."},"429":{"description":"Rate limit exceeded — see `Retry-After`."}},"security":[{"api_key":[]}],"summary":"Create a document from inline content","tags":["Documents"]},"get":{"description":"Cursor-paged, newest first. One row per language version; siblings share `language_group_id`. Filter with `project_id` (the literal `none` = unfiled) and `search` (title contains, case-insensitive).","operationId":"PublicDocumentsController_listDocuments","parameters":[{"name":"limit","required":false,"in":"query","description":"Page size. Clamped to 100; defaults to 25.","schema":{"minimum":1,"maximum":100,"default":25,"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous response’s `next_cursor`. Omit for the first page.","schema":{"type":"string"}},{"name":"project_id","required":false,"in":"query","schema":{"type":"string"}},{"name":"search","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of documents."}},"security":[{"api_key":[]}],"summary":"List documents","tags":["Documents"]}},"/api/public/v1/documents/{id}/seed-translation":{"post":{"description":"Writes an already-translated document (markdown, HTML, or DOCX) onto a language sibling as active translations — no re-translation, no words billed. Send inline text (`content` + `content_type`), base64 (`content_base64` + `content_type: docx`), or a multipart `file`. Pass the SIBLING id, or a master id plus `target_language` to resolve the sibling. Blocks must align 1:1 with the source structure (else `422 alignment_mismatch`, with a per-index report). Dropped/added inline objects (images, footnotes, links) still seed but return `warnings`. Idempotent: a block that already has a translation is skipped unless `overwrite` is set.","operationId":"PublicDocumentsController_seedTranslationEndpoint","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeedTranslationDto"}},"multipart/form-data":{"schema":{"$ref":"#/components/schemas/SeedTranslationDto"}}}},"responses":{"201":{"description":"Seeding counts: `{ seeded_count, skipped_existing, overwritten_count, total_blocks, warnings }`."},"400":{"description":"No content source (need content, content_base64, or a file)."},"404":{"description":"No such document (or sibling) for this caller."},"422":{"description":"The document format is not seedable (`unsupported_source_format` / `strings_master_not_supported`), DOCX seeding is disabled (`docx_adapter_disabled`), or the content does not structurally align (`alignment_mismatch`, with a per-index report)."},"429":{"description":"Rate limit exceeded — see `Retry-After`."}},"security":[{"api_key":[]}],"summary":"Seed a translation from an already-translated file","tags":["Documents"]}},"/api/public/v1/documents/upload":{"post":{"description":"Returns a processing job id to poll at `GET /public/v1/document-jobs/{id}`. Multipart, so `Idempotency-Key` does not apply here — poll the job instead of retrying blind.","operationId":"PublicDocumentsController_upload","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"docx, pdf, txt, html, md — or a strings file: csv (translation matrix), po (gettext), xlf/xliff (XLIFF 1.2), tstrings.json"},"source_language":{"type":"string"},"target_language":{"type":"string"},"document_category":{"type":"string"},"project_id":{"type":"string"},"use_target_locales":{"type":"boolean","description":"Fan out to the locales the file itself declares (a CSV's locale columns, an XLIFF's target-language)."}}}}}},"responses":{"202":{"description":"The processing job id to poll."},"429":{"description":"Rate limit exceeded — see `Retry-After`."}},"security":[{"api_key":[]}],"summary":"Upload a file for asynchronous parsing","tags":["Documents"]}},"/api/public/v1/document-jobs/{id}":{"get":{"operationId":"PublicDocumentsController_processingJob","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Job status, progress and resulting document id."},"404":{"description":"No such job for this caller."}},"security":[{"api_key":[]}],"summary":"Poll a file-processing job","tags":["Documents"]}},"/api/public/v1/documents/{id}":{"get":{"operationId":"PublicDocumentsController_metadata","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Metadata and the document’s language group."},"404":{"description":"No such document for this caller."}},"security":[{"api_key":[]}],"summary":"Get document metadata","tags":["Documents"]},"patch":{"description":"Title, category, or project filing. Content changes go through resync-source (prose) or re-import (strings) — never a metadata PATCH.","operationId":"PublicDocumentsController_updateDocument","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicDocumentDto"}}}},"responses":{"200":{"description":"The updated metadata."},"404":{"description":"No such document for this caller."}},"security":[{"api_key":[]}],"summary":"Update document metadata","tags":["Documents"]},"delete":{"description":"Deletes ONE language version. Deleting the master does not delete its siblings.","operationId":"PublicDocumentsController_deleteDocument","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"`{deleted: true, id}`."},"404":{"description":"No such document for this caller."}},"security":[{"api_key":[]}],"summary":"Delete a document","tags":["Documents"]}},"/api/public/v1/documents/{id}/content":{"get":{"description":"`format=json` (default) returns the stable per-block shape: source text plus the active translation. Other formats stream the rendered file with its own content-type. `source` is the faithful export in whatever format the document was born; `html` requires an HTML-born document (`format_conversion_unavailable` otherwise — use docx/pdf for rendered files).","operationId":"PublicDocumentsController_getContent","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"format","required":false,"in":"query","description":"`json` returns the stable per-block shape; the rest stream the rendered file.","schema":{"default":"json","type":"string","enum":["json","markdown","html","docx","pdf","xliff","tmx","csv","strings","source"]}}],"responses":{"200":{"description":"The document content, in the requested format."},"404":{"description":"No such document for this caller."}},"security":[{"api_key":[]}],"summary":"Read a document’s content","tags":["Documents"]}},"/api/public/v1/documents/{id}/languages":{"post":{"description":"Adds target-language sibling versions to this document, linking them into its language group. Idempotent — languages already present in the group are skipped. Locale-aware formats (Transept Strings with ICU plurals) re-derive each sibling’s blocks for its locale.","operationId":"PublicDocumentsController_addLanguages","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddPublicLanguagesDto"}}}},"responses":{"201":{"description":"`{group_id, created: [{id, target_language}]}`."},"404":{"description":"No such document for this caller."}},"security":[{"api_key":[]}],"summary":"Add language versions","tags":["Documents"]}},"/api/public/v1/documents/{id}/resync-source":{"post":{"description":"For prose documents whose source file/artifact was replaced: re-aligns blocks against the stored source (content-keyed LCS), keeps translations on unchanged blocks, and reports what changed per language version. The strings counterpart is `POST /public/v1/documents/{id}/import-strings`.","operationId":"PublicDocumentsController_resyncSource","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The per-version delta of the re-sync."},"404":{"description":"No such document for this caller."},"429":{"description":"Rate limit exceeded — see `Retry-After`."}},"security":[{"api_key":[]}],"summary":"Re-sync a document from its updated source","tags":["Documents"]}},"/api/public/v1/documents/{id}/translation-settings":{"patch":{"description":"Body: `glossary_ids` and/or `styleguide_version_ids`. Omitted field = untouched; empty array = detach all of that kind. Foreign ids 404. The attached resources flow into every subsequent run on the document.","operationId":"PublicDocumentsController_updateTranslationSettings","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The resolved attachment lists."},"404":{"description":"Document, glossary, or version not found for this caller."}},"security":[{"api_key":[]}],"summary":"Attach glossaries / style-guide versions to a document","tags":["Documents"]}},"/api/public/v1/documents/{id}/tm-settings":{"patch":{"description":"Same rules as the app: edit access required; when the owning team enforces a TM policy, only the document owner may override, and the override marker is server-derived.","operationId":"PublicDocumentsController_updateTmSettings","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The stored settings."},"403":{"description":"Team policy enforced and you are not the document owner."}},"security":[{"api_key":[]}],"summary":"Update a document's translation-memory settings","tags":["Documents"]}},"/api/public/v1/blocks/{id}":{"put":{"description":"Body: `original_content` (Slate JSON), optional `block_type` / `hard_page_break`. Editing a source block marks its translations stale — run a scoped workflow (`block_ids`) to refresh them.","operationId":"PublicDocumentsController_updateBlock","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicBlockDto"}}}},"responses":{"200":{"description":"The updated block."},"404":{"description":"No such block for this caller."}},"security":[{"api_key":[]}],"summary":"Update a block's source content","tags":["Documents"]}},"/api/public/v1/block-translations/{id}":{"put":{"description":"Body: `translated_content` (Slate JSON). Edit-access-checked; the block stays active and the edit lands in translation memory like an in-app edit.","operationId":"PublicDocumentsController_updateBlockTranslation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicTranslationDto"}}}},"responses":{"200":{"description":"The updated translation."},"404":{"description":"No such translation for this caller."}},"security":[{"api_key":[]}],"summary":"Update a translation","tags":["Documents"]}},"/api/public/v1/projects":{"get":{"description":"Cursor-paged. Pass the previous response’s `next_cursor` as `cursor` to continue; `has_more: false` means you have them all.","operationId":"PublicProjectsController_list","parameters":[{"name":"limit","required":false,"in":"query","description":"Page size. Clamped to 100; defaults to 25.","schema":{"minimum":1,"maximum":100,"default":25,"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous response’s `next_cursor`. Omit for the first page.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of projects."}},"security":[{"api_key":[]}],"summary":"List projects","tags":["Projects"]},"post":{"description":"Personal by default; pass `team_id` (membership-checked) for a team project — documents filed into a team project become team-owned.","operationId":"PublicProjectsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicProjectDto"}}}},"responses":{"201":{"description":"The created project."},"403":{"description":"Not a member of the named team."}},"security":[{"api_key":[]}],"summary":"Create a project","tags":["Projects"]}},"/api/public/v1/projects/{id}":{"get":{"operationId":"PublicProjectsController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The project."},"404":{"description":"No such project for this caller."}},"security":[{"api_key":[]}],"summary":"Get a project","tags":["Projects"]},"patch":{"operationId":"PublicProjectsController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicProjectDto"}}}},"responses":{"200":{"description":"The updated project."},"404":{"description":"No such project for this caller."}},"security":[{"api_key":[]}],"summary":"Update a project","tags":["Projects"]},"delete":{"description":"Documents inside it survive — they become unfiled, exactly as in the app.","operationId":"PublicProjectsController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"`{deleted: true, id}`."},"404":{"description":"No such project for this caller."}},"security":[{"api_key":[]}],"summary":"Delete a project","tags":["Projects"]}},"/api/public/v1/workflow-templates":{"get":{"description":"The built-in catalog — ids here go into `template_id` on runs and group runs. Always one page (`has_more: false`): the catalog is a small static set.","operationId":"PublicWorkflowsController_listTemplates","parameters":[],"responses":{"200":{"description":"Every template, in the house envelope."}},"security":[{"api_key":[]}],"summary":"List workflow templates","tags":["Workflows"]}},"/api/public/v1/workflow-templates/{id}":{"get":{"operationId":"PublicWorkflowsController_getTemplate","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The template summary."},"404":{"description":"Unknown template id."}},"security":[{"api_key":[]}],"summary":"Get a workflow template","tags":["Workflows"]}},"/api/public/v1/workflows":{"get":{"description":"Your workflows plus your teams’ — ids here go into `workflow_id` on runs and group runs. Cursor-paged, most recently updated first within the page order.","operationId":"PublicWorkflowsController_list","parameters":[{"name":"limit","required":false,"in":"query","description":"Page size. Clamped to 100; defaults to 25.","schema":{"minimum":1,"maximum":100,"default":25,"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous response’s `next_cursor`. Omit for the first page.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of workflow summaries."}},"security":[{"api_key":[]}],"summary":"List saved workflows","tags":["Workflows"]}},"/api/public/v1/workflows/{id}":{"get":{"operationId":"PublicWorkflowsController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The workflow summary."},"404":{"description":"No such workflow for this caller."}},"security":[{"api_key":[]}],"summary":"Get a saved workflow","tags":["Workflows"]}},"/api/public/v1/glossaries":{"get":{"description":"Yours plus your teams’. Cursor-paged summaries; fetch one for its terms.","operationId":"PublicGlossariesController_list","parameters":[{"name":"limit","required":false,"in":"query","description":"Page size. Clamped to 100; defaults to 25.","schema":{"minimum":1,"maximum":100,"default":25,"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous response’s `next_cursor`. Omit for the first page.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of glossary summaries."}},"security":[{"api_key":[]}],"summary":"List glossaries","tags":["Glossaries"]},"post":{"description":"Optionally seed `terms` in the same call. Language columns: `source_language`, `primary_target_language`; more targets via term translations.","operationId":"PublicGlossariesController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicGlossaryDto"}}}},"responses":{"201":{"description":"The created glossary."}},"security":[{"api_key":[]}],"summary":"Create a glossary","tags":["Glossaries"]}},"/api/public/v1/glossaries/{id}":{"get":{"operationId":"PublicGlossariesController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The glossary with its terms."},"404":{"description":"No such glossary for this caller."}},"security":[{"api_key":[]}],"summary":"Get a glossary (terms included)","tags":["Glossaries"]},"patch":{"operationId":"PublicGlossariesController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicGlossaryDto"}}}},"responses":{"200":{"description":"The updated glossary."},"404":{"description":"No such glossary for this caller."}},"security":[{"api_key":[]}],"summary":"Update glossary metadata","tags":["Glossaries"]},"delete":{"operationId":"PublicGlossariesController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"`{deleted: true, id}`."},"404":{"description":"No such glossary for this caller."}},"security":[{"api_key":[]}],"summary":"Delete a glossary","tags":["Glossaries"]}},"/api/public/v1/glossaries/{id}/terms":{"post":{"operationId":"PublicGlossariesController_addTerm","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTermDto"}}}},"responses":{"201":{"description":"The created term."}},"security":[{"api_key":[]}],"summary":"Add a term","tags":["Glossaries"]}},"/api/public/v1/glossaries/{id}/terms/bulk":{"post":{"description":"The answer is counts, matching the in-app bulk importer.","operationId":"PublicGlossariesController_addTermsBulk","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTermsBulkDto"}}}},"responses":{"200":{"description":"Created/skipped counts."}},"security":[{"api_key":[]}],"summary":"Add many terms","tags":["Glossaries"]}},"/api/public/v1/glossary-terms/{termId}":{"put":{"description":"Source/target text, do-not-translate, variants, forbidden targets, metadata. The term’s glossary decides access.","operationId":"PublicGlossariesController_updateTerm","parameters":[{"name":"termId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTermDto"}}}},"responses":{"200":{"description":"The updated term."},"404":{"description":"No such term for this caller."}},"security":[{"api_key":[]}],"summary":"Update a term","tags":["Glossaries"]},"delete":{"operationId":"PublicGlossariesController_deleteTerm","parameters":[{"name":"termId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"`{deleted: true, id}`."}},"security":[{"api_key":[]}],"summary":"Delete a term","tags":["Glossaries"]}},"/api/public/v1/glossary-terms/{termId}/translations/{language}":{"put":{"description":"One cell of the language matrix — upsert on (term, language). The primary target language 400s: it lives on the term itself.","operationId":"PublicGlossariesController_upsertTermTranslation","parameters":[{"name":"termId","required":true,"in":"path","schema":{"type":"string"}},{"name":"language","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTermTranslationDto"}}}},"responses":{"200":{"description":"The upserted translation cell."}},"security":[{"api_key":[]}],"summary":"Set a term's translation for one language","tags":["Glossaries"]},"delete":{"operationId":"PublicGlossariesController_deleteTermTranslation","parameters":[{"name":"termId","required":true,"in":"path","schema":{"type":"string"}},{"name":"language","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The cell is cleared."}},"security":[{"api_key":[]}],"summary":"Clear a term's translation for one language","tags":["Glossaries"]}},"/api/public/v1/glossaries/{id}/export":{"get":{"description":"`format=csv` (default) or `format=tbx` (TBX-Basic; label langSets with `source_lang`/`target_lang`, defaulting to \"und\").","operationId":"PublicGlossariesController_exportGlossary","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"format","required":false,"in":"query","schema":{"enum":["csv","tbx"],"type":"string"}},{"name":"source_lang","required":false,"in":"query","schema":{"type":"string"}},{"name":"target_lang","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"The file, with its content-type."}},"security":[{"api_key":[]}],"summary":"Export a glossary","tags":["Glossaries"]}},"/api/public/v1/styleguide-presets":{"get":{"description":"The built-in preset library — clone one with POST /public/v1/styleguides/from-preset. Single page (static set).","operationId":"PublicStyleguidesController_listPresets","parameters":[],"responses":{"200":{"description":"Every preset."}},"security":[{"api_key":[]}],"summary":"List style-guide presets","tags":["Style guides"]}},"/api/public/v1/styleguides":{"get":{"description":"Yours plus your teams’, as summaries with the ACTIVE version id — the id runs and documents actually consume.","operationId":"PublicStyleguidesController_list","parameters":[{"name":"limit","required":false,"in":"query","description":"Page size. Clamped to 100; defaults to 25.","schema":{"minimum":1,"maximum":100,"default":25,"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous response’s `next_cursor`. Omit for the first page.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of style-guide summaries."}},"security":[{"api_key":[]}],"summary":"List style guides","tags":["Style guides"]},"post":{"description":"Optionally seed the first version: `initial_content` (Slate notes) and/or `initial_structured_data`.","operationId":"PublicStyleguidesController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicStyleguideDto"}}}},"responses":{"201":{"description":"The created style guide."}},"security":[{"api_key":[]}],"summary":"Create a style guide","tags":["Style guides"]}},"/api/public/v1/styleguides/from-preset":{"post":{"operationId":"PublicStyleguidesController_fromPreset","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicFromPresetDto"}}}},"responses":{"201":{"description":"The created style guide."},"400":{"description":"Unknown preset id."}},"security":[{"api_key":[]}],"summary":"Clone a preset into your own style guide","tags":["Style guides"]}},"/api/public/v1/styleguides/{id}":{"get":{"operationId":"PublicStyleguidesController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The style guide."},"404":{"description":"No such style guide for this caller."}},"security":[{"api_key":[]}],"summary":"Get a style guide (active version included)","tags":["Style guides"]},"patch":{"operationId":"PublicStyleguidesController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicStyleguideDto"}}}},"responses":{"200":{"description":"The updated style guide."},"404":{"description":"No such style guide for this caller."}},"security":[{"api_key":[]}],"summary":"Update style-guide metadata (name/description)","tags":["Style guides"]},"delete":{"operationId":"PublicStyleguidesController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"`{deleted: true, id}`."},"404":{"description":"No such style guide for this caller."}},"security":[{"api_key":[]}],"summary":"Delete a style guide","tags":["Style guides"]}},"/api/public/v1/styleguides/{id}/versions":{"get":{"description":"Newest first. The ACTIVE version is what documents referencing this guide use; its id goes into `styleguide_version_ids`.","operationId":"PublicStyleguidesController_versions","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The version history."}},"security":[{"api_key":[]}],"summary":"List versions","tags":["Style guides"]},"post":{"description":"Slate `content` (free-form notes) and/or `structured_data`. The new version becomes active.","operationId":"PublicStyleguidesController_createVersion","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicStyleguideVersionDto"}}}},"responses":{"201":{"description":"The created version."}},"security":[{"api_key":[]}],"summary":"Create a version","tags":["Style guides"]}},"/api/public/v1/styleguides/{id}/versions/{versionId}/activate":{"post":{"operationId":"PublicStyleguidesController_activateVersion","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"versionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The activation result."}},"security":[{"api_key":[]}],"summary":"Make a version the active one","tags":["Style guides"]}},"/api/public/v1/translation-memory/query":{"post":{"description":"Hybrid retrieval (lexical + fuzzy + semantic) over everything the key’s account can see. Body: `query_text`, `source_lang`, `target_lang`; optional `document_id` (scopes settings + access-checked), `top_k` (default 8), `cross_language`. Content-quality filters flow from the same settings hierarchy the app uses. Free.","operationId":"PublicTmController_query","parameters":[],"responses":{"200":{"description":"The retrieval hits."}},"security":[{"api_key":[]}],"summary":"Query translation memory","tags":["Translation memory"]}},"/api/public/v1/translation-memory/entries":{"get":{"description":"Newest first, id-cursor paged. Filter with `mode` (block, comment, chat, document), `source_lang`, `target_lang`.","operationId":"PublicTmController_entries","parameters":[{"name":"mode","required":false,"in":"query","schema":{"type":"string"}},{"name":"source_lang","required":false,"in":"query","schema":{"type":"string"}},{"name":"target_lang","required":false,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of entries."}},"security":[{"api_key":[]}],"summary":"Browse entries","tags":["Translation memory"]}},"/api/public/v1/documents/{id}/tm-status":{"get":{"description":"Entry counts by type plus pending embeddings.","operationId":"PublicTmController_status","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The index status."},"404":{"description":"No such document for this caller."}},"security":[{"api_key":[]}],"summary":"A document's TM index status","tags":["Translation memory"]}},"/api/public/v1/translation-memory/export-tmx":{"get":{"description":"Every active block pair visible to the key’s account (personal + team). Optionally filter with `source_lang` / `target_lang`. The exit-cost guarantee: what you brought in (or built up), you can take out.","operationId":"PublicTmController_exportTmx","parameters":[{"name":"source_lang","required":false,"in":"query","schema":{"type":"string"}},{"name":"target_lang","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"The TMX file."}},"security":[{"api_key":[]}],"summary":"Export your TM as TMX 1.4b","tags":["Translation memory"]}},"/api/public/v1/translation-memory/import":{"post":{"description":"Body: `format` (\"tmx\" | \"xliff\"), `content` (the file text — JSON caps at 50 MB), optional `project_id` scope. Pairs land immediately; embeddings fill asynchronously (watch `pendingEmbeddings` on tm-status). Free.","operationId":"PublicTmController_import","parameters":[],"responses":{"201":{"description":"Batch id + imported/skipped counts."},"400":{"description":"Unparseable file or bad format."},"403":{"description":"No access to that project."}},"security":[{"api_key":[]}],"summary":"Import a TMX or XLIFF file","tags":["Translation memory"]}},"/api/public/v1/glossaries/{id}/auto-build/estimate":{"post":{"description":"Free. Returns the floor–ceiling word range (`minCredits`/`estimatedCredits` — wire names say credits, they are words) plus wave/block counts.","operationId":"PublicGenerationController_estimateGlossaryAutoBuild","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The estimate."}},"security":[{"api_key":[]}],"summary":"Price a glossary auto-build","tags":["Generation"]}},"/api/public/v1/glossaries/{id}/auto-build":{"post":{"description":"Extracts terminology from the document into this glossary. REQUIRES `auto_apply: true`: over the API the result is committed on completion with no review step (in-app generation keeps the human review). Body: `document_id`, optional `scope` (default document), `block_ids`, `mode` (pairs | source-only), `target_language`. Bills words per the estimate; poll `GET /public/v1/generation-jobs/{id}`.","operationId":"PublicGenerationController_startGlossaryAutoBuild","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"202":{"description":"The generation job to poll."},"400":{"description":"auto_apply not confirmed, or missing fields."},"402":{"description":"Not enough words for the ceiling."},"404":{"description":"No such glossary/document for this caller."}},"security":[{"api_key":[]}],"summary":"Auto-build a glossary from a document","tags":["Generation"]}},"/api/public/v1/styleguides/generate/estimate":{"post":{"description":"Free. Floor–ceiling word range plus wave/block counts for generating from `document_id` (+ optional `block_ids`).","operationId":"PublicGenerationController_estimateStyleguideGenerate","parameters":[],"responses":{"200":{"description":"The estimate."}},"security":[{"api_key":[]}],"summary":"Price a style-guide generation","tags":["Generation"]}},"/api/public/v1/styleguides/generate":{"post":{"description":"Distills tone/register/formatting rules from the document. REQUIRES `auto_apply: true` (the result is applied on completion — a NEW style guide, or a new version when `styleguide_id` is passed). Body: `document_id`, `name`, optional `description`, `scope`, `block_ids`, `styleguide_id`. Bills words per the estimate; poll `GET /public/v1/generation-jobs/{id}`.","operationId":"PublicGenerationController_startStyleguideGenerate","parameters":[],"responses":{"202":{"description":"The generation job to poll."},"400":{"description":"auto_apply not confirmed, or missing fields."},"402":{"description":"Not enough words for the ceiling."}},"security":[{"api_key":[]}],"summary":"Generate a style guide from a document","tags":["Generation"]}},"/api/public/v1/generation-jobs/{id}":{"get":{"description":"Status running → completed | failed | cancelled. On completion `result` carries what was applied (terms added / the style-guide + version ids) — the write already happened, per your auto_apply consent.","operationId":"PublicGenerationController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The job."},"404":{"description":"No such job for this caller."}},"security":[{"api_key":[]}],"summary":"Poll a generation job","tags":["Generation"]}},"/api/public/v1/generation-jobs/{id}/cancel":{"post":{"description":"Idempotent; a terminal job is unchanged. Nothing is applied.","operationId":"PublicGenerationController_cancel","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The job after the cancel."},"404":{"description":"No such job for this caller."}},"security":[{"api_key":[]}],"summary":"Cancel a generation job","tags":["Generation"]}},"/api/public/v1/figma/file-link":{"get":{"description":"Pass the file key (from the figma.com URL); get the master document id and its language group. Then pull every locale keyed by node id with `GET /public/v1/documents/{master}/content?format=strings` — the shape the Transept Figma plugin applies back onto the design.","operationId":"PublicFigmaController_fileLink","parameters":[{"name":"file_key","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"The linked document + language group."},"404":{"description":"No imported document for this file (or not yours)."}},"security":[{"api_key":[]}],"summary":"Resolve a Figma file to its Transept document","tags":["Figma"]}},"/api/public/v1/runs":{"post":{"description":"Queues the run and returns immediately with a job id to poll. Consumes words — price it first with `/runs/estimate`, and send an `Idempotency-Key` so a retry cannot bill twice.","operationId":"PublicRunsController_run","parameters":[{"name":"Idempotency-Key","in":"header","description":"Retry-safety token. A repeat of the same request replays the first response instead of starting (and billing) a second run. Honored for 24h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRunDto"}}}},"responses":{"201":{"description":"The queued run’s job id and block count."},"402":{"description":"Not enough words to run this workflow."},"409":{"description":"A request with this Idempotency-Key is still in flight."},"422":{"description":"This Idempotency-Key was used with a different body."},"429":{"description":"Rate limit exceeded — see `Retry-After`."}},"security":[{"api_key":[]}],"summary":"Start a workflow run","tags":["Runs"]},"get":{"description":"Cursor-paged, newest first. Filter with `document_id` and/or `status` (queued, processing, completed, failed, cancelled, awaiting_review). Poll a single run for its full gate digest.","operationId":"PublicRunsController_listRuns","parameters":[{"name":"limit","required":false,"in":"query","description":"Page size. Clamped to 100; defaults to 25.","schema":{"minimum":1,"maximum":100,"default":25,"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous response’s `next_cursor`. Omit for the first page.","schema":{"type":"string"}},{"name":"document_id","required":false,"in":"query","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of runs."}},"security":[{"api_key":[]}],"summary":"List runs","tags":["Runs"]}},"/api/public/v1/runs/estimate":{"post":{"description":"No side effects: nothing is queued and nothing is billed. Some steps cannot know their exact cost until they run (a prescan only bills the blocks it flags), so the answer may be a range — `minCredits` is the guaranteed floor, `estimatedCredits` the ceiling that gets held and partially released. Deliberately NOT idempotency-keyed: it creates nothing to double-create.","operationId":"PublicRunsController_estimate","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRunDto"}}}},"responses":{"200":{"description":"The cost and scope of the run."},"429":{"description":"Rate limit exceeded — see `Retry-After`."}},"security":[{"api_key":[]}],"summary":"Price a run before starting it","tags":["Runs"]}},"/api/public/v1/runs/{jobId}":{"get":{"description":"A non-null `gate` with `pending: true` means the run stopped for review — resolve it with `/gate/approve` or `/gate/acknowledge` before the run can finish.","operationId":"PublicRunsController_status","parameters":[{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Status, progress and the review-gate digest."},"404":{"description":"No such run for this caller."}},"security":[{"api_key":[]}],"summary":"Poll a run","tags":["Runs"]}},"/api/public/v1/runs/{jobId}/cancel":{"post":{"description":"Idempotent by nature — cancelling an already-finished run leaves it untouched and returns its terminal status.","operationId":"PublicRunsController_cancel","parameters":[{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The run’s status after the cancel."},"404":{"description":"No such run for this caller."}},"security":[{"api_key":[]}],"summary":"Cancel a run","tags":["Runs"]}},"/api/public/v1/runs/{jobId}/gate/approve":{"post":{"description":"Continues the run on the approved blocks only. Returns the continuation run’s job id — poll that, not the original.","operationId":"PublicRunsController_approveGate","parameters":[{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GateApproveDto"}}}},"responses":{"200":{"description":"The continuation run’s job id."},"404":{"description":"No such run for this caller."}},"security":[{"api_key":[]}],"summary":"Approve a review gate and continue","tags":["Runs"]}},"/api/public/v1/runs/{jobId}/gate/acknowledge":{"post":{"description":"Clears the pending gate and leaves the run as it is. Use when you have read the findings and want no follow-up work.","operationId":"PublicRunsController_acknowledgeGate","parameters":[{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The gate is resolved."},"404":{"description":"No such run for this caller."}},"security":[{"api_key":[]}],"summary":"Dismiss a review gate without continuing","tags":["Runs"]}},"/api/public/v1/documents/{id}/pending-reviews":{"get":{"description":"The caller’s finished runs on this document whose review gate is still pending.","operationId":"PublicRunsController_pendingReviews","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Page size. Clamped to 100; defaults to 25.","schema":{"minimum":1,"maximum":100,"default":25,"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous response’s `next_cursor`. Omit for the first page.","schema":{"type":"string"}},{"name":"scope","required":false,"in":"query","description":"`group` widens the search to every language version of the document.","schema":{"enum":["group"],"type":"string"}}],"responses":{"200":{"description":"A page of pending reviews ({data, has_more, next_cursor} — the house cursor envelope)."},"404":{"description":"No such document for this caller."}},"security":[{"api_key":[]}],"summary":"List runs waiting on your review","tags":["Runs"]}},"/api/public/v1/group-runs":{"post":{"description":"Creates any missing language version, then queues one run per language and returns immediately with a group id to poll. Prices the WHOLE fan-out first: if the total does not fit, nothing is dispatched and nothing is billed (402). A language whose version has no eligible blocks is reported as a `skipped` lane, not a failure. Consumes words — send an `Idempotency-Key` so a retry cannot bill the fan-out twice.","operationId":"PublicGroupRunsController_create","parameters":[{"name":"Idempotency-Key","in":"header","description":"Retry-safety token. A repeat of the same request replays the first response instead of starting (and billing) a second group run. Honored for 24h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGroupRunDto"}}}},"responses":{"201":{"description":"The group id, the versions created, and one lane per language."},"402":{"description":"Not enough words for the whole fan-out. Nothing was dispatched."},"404":{"description":"No such document for this caller."},"409":{"description":"A request with this Idempotency-Key is still in flight."},"422":{"description":"This Idempotency-Key was used with a different body."},"429":{"description":"Rate limit exceeded — see `Retry-After`."}},"security":[{"api_key":[]}],"summary":"Run a workflow across many languages","tags":["Group runs"]},"get":{"description":"Newest first, cursor-paged. Pass the previous response’s `next_cursor` as `cursor` to continue; `has_more: false` means you have them all.","operationId":"PublicGroupRunsController_list","parameters":[{"name":"limit","required":false,"in":"query","description":"Page size. Clamped to 100; defaults to 25.","schema":{"minimum":1,"maximum":100,"default":25,"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous response’s `next_cursor`. Omit for the first page.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of group runs."}},"security":[{"api_key":[]}],"summary":"List group runs","tags":["Group runs"]}},"/api/public/v1/group-runs/estimate":{"post":{"description":"Prices every language lane and returns each one’s block count, word cost (floor and ceiling) and non-blocking resource warnings — a glossary with no column for that language, or a style guide contributing shared voice only. Creates nothing: a language with no version yet is priced against the master’s blocks and reported with `version_exists: false`. Never bills.","operationId":"PublicGroupRunsController_estimate","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGroupRunDto"}}}},"responses":{"200":{"description":"One entry per language, the fan-out totals, and the languages that would not run."},"404":{"description":"No such document for this caller."}},"security":[{"api_key":[]}],"summary":"Estimate a multi-language run","tags":["Group runs"]}},"/api/public/v1/group-runs/{id}/cancel":{"post":{"description":"Cancels every lane still in flight (queued, processing, or paused at a review gate). Idempotent — terminal lanes are untouched, and repeating the call cancels nothing further. Returns `cancelled_lanes` plus the same rollup shape as polling; the group’s own status settles asynchronously as the cancelled lanes land.","operationId":"PublicGroupRunsController_cancel","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"How many lanes were cancelled, plus the group rollup."},"404":{"description":"No such group run for this caller."}},"security":[{"api_key":[]}],"summary":"Cancel a group run","tags":["Group runs"]}},"/api/public/v1/group-runs/{id}":{"get":{"description":"The rollup plus one entry per language. `status` is `running` until every lane is terminal, then: `completed` (all lanes done), `partial` (some lanes did not produce a result), `failed` (none did), or `awaiting_review` (a lane stopped for a human — resolve it through `/runs/{job_id}/gate/*`). A lane with `job_id: null` and `status: \"skipped\"` had no eligible blocks and was never queued.","operationId":"PublicGroupRunsController_status","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The group’s rollup and its lanes."},"404":{"description":"No such group run for this caller."}},"security":[{"api_key":[]}],"summary":"Poll a group run","tags":["Group runs"]}},"/api/public/v1/webhooks":{"post":{"description":"Transept POSTs HMAC-signed event envelopes to the URL (events: run.completed, run.failed, run.gate_ready, group_run.completed, document.processed, document.processing_failed, ping). The response carries the signing `secret` — the ONLY time it is returned. Verify each delivery: `X-Transept-Signature: t=…,v1=…`, v1 = HMAC-SHA256(secret, \"{t}.{rawBody}\"), constant-time compare, reject a stale `t`. Failing endpoints retry 8× with exponential backoff, then auto-disable.","operationId":"PublicWebhooksController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookDto"}}}},"responses":{"201":{"description":"The endpoint, including its one-time `secret`."},"400":{"description":"URL rejected (scheme/host rules) or unknown event type."}},"security":[{"api_key":[]}],"summary":"Register a webhook endpoint","tags":["Webhooks"]},"get":{"operationId":"PublicWebhooksController_list","parameters":[{"name":"limit","required":false,"in":"query","description":"Page size. Clamped to 100; defaults to 25.","schema":{"minimum":1,"maximum":100,"default":25,"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous response’s `next_cursor`. Omit for the first page.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of endpoints (no secrets)."}},"security":[{"api_key":[]}],"summary":"List webhook endpoints","tags":["Webhooks"]}},"/api/public/v1/webhooks/{id}":{"get":{"operationId":"PublicWebhooksController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The endpoint (no secret)."},"404":{"description":"No such endpoint for this caller."}},"security":[{"api_key":[]}],"summary":"Get a webhook endpoint","tags":["Webhooks"]},"patch":{"description":"Patch url / description / events / enabled. A changed URL is re-validated (same SSRF rules as create). Re-enabling a tripped endpoint resets its failure counter.","operationId":"PublicWebhooksController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookDto"}}}},"responses":{"200":{"description":"The updated endpoint."},"404":{"description":"No such endpoint for this caller."}},"security":[{"api_key":[]}],"summary":"Update a webhook endpoint","tags":["Webhooks"]},"delete":{"operationId":"PublicWebhooksController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"`{deleted: true, id}`."},"404":{"description":"No such endpoint for this caller."}},"security":[{"api_key":[]}],"summary":"Delete a webhook endpoint","tags":["Webhooks"]}},"/api/public/v1/webhooks/{id}/rotate-secret":{"post":{"description":"Mints a new signing secret and returns it (the only time it is visible). The old secret stops verifying immediately — update your receiver first if you need continuity.","operationId":"PublicWebhooksController_rotateSecret","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"The endpoint with its new one-time `secret`."},"404":{"description":"No such endpoint for this caller."}},"security":[{"api_key":[]}],"summary":"Rotate the signing secret","tags":["Webhooks"]}},"/api/public/v1/webhooks/{id}/test":{"post":{"description":"Enqueues a `ping` event through the real delivery path — signed, retried, and visible in the delivery log — so you can prove the receiver verifies signatures correctly.","operationId":"PublicWebhooksController_test","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"202":{"description":"The queued delivery id."},"404":{"description":"No such endpoint for this caller."}},"security":[{"api_key":[]}],"summary":"Send a test ping","tags":["Webhooks"]}},"/api/public/v1/webhooks/{id}/deliveries":{"get":{"description":"What actually went out and what came back: per-delivery status (pending / success / failed / exhausted), attempts, and the receiver’s last response.","operationId":"PublicWebhooksController_deliveries","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Page size (1–100, default 25).","schema":{"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Delivery id to page after (a previous response's next_cursor).","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of deliveries, newest first."},"404":{"description":"No such endpoint for this caller."}},"security":[{"api_key":[]}],"summary":"Read the delivery log","tags":["Webhooks"]}},"/api/public/v1/documents/import-strings":{"post":{"description":"Submit a `.tstrings.json` document as the JSON body (`document`) and name the target locales (`use_target_locales` honors the file’s own list). One call creates a language group with a version per locale; translations the corpus already carries land as active, zero words billed. Validation is synchronous and itemized (400 with per-unit errors); parsing runs async — poll `GET /public/v1/document-jobs/{id}`.","operationId":"PublicStringsController_importStrings","parameters":[{"name":"Idempotency-Key","in":"header","description":"Retry-safety token. A repeat of the same request replays the first response instead of creating a second language group. Honored for 24h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportStringsDto"}}}},"responses":{"202":{"description":"The processing job id to poll."},"400":{"description":"Malformed corpus — itemized per-unit errors."},"404":{"description":"No such project for this caller."},"429":{"description":"Rate limit exceeded — see `Retry-After`."}},"security":[{"api_key":[]}],"summary":"Import a Transept Strings corpus","tags":["Strings"]}},"/api/public/v1/documents/{id}/import-strings":{"post":{"description":"Send the WHOLE current corpus against the master document; the server diffs by unit id and returns `units: {added, changed, unchanged, removed}` plus per-language `added_block_ids` / `stale_block_ids` — exactly the map a scoped group run takes. The diff is SOURCE-based: a changed `context` alone does not invalidate a unit. Optional inline `run` dispatches a group run over the delta in the same call. Synchronous. 409 while a run is active on the group.","operationId":"PublicStringsController_reimportStrings","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReimportStringsDto"}}}},"responses":{"200":{"description":"The per-unit delta and per-language block-id lists."},"400":{"description":"Malformed corpus, or run dispatch options invalid."},"404":{"description":"No such document for this caller."},"409":{"description":"A run is active on this language group — retry after it settles."},"429":{"description":"Rate limit exceeded — see `Retry-After`."}},"security":[{"api_key":[]}],"summary":"Re-import a corpus and get the delta","tags":["Strings"]}},"/api/public/v1/hooks/{secret}":{"post":{"description":"Dispatches the workflow bound to this secret. The secret is the whole auth — it lives in the URL, so no Authorization header is needed. Point a Notion database automation, a Zapier/n8n step, an ESP, or a CI job at this URL. What the BODY means depends on the trigger’s target mode: `document` ignores it; `notion_page` reads the Notion page id from the automation payload; `payload` takes the body AS THE CONTENT — raw `text/plain` / `text/markdown` / `text/html` (10 MB cap), or JSON `{\"content\": \"…\", \"content_type\": \"markdown\", \"title\"?, \"project_id\"?, \"target_languages\"?, \"external_id\"?}`. Payload fires either create a fresh document per delivery or content-key update a fixed one (the trigger decides), then run the workflow; repeats dedupe on `external_id` (or the content hash) for 24h. Returns 202 with the run or group-run id (+ `document_id`, `created`). An unknown secret is a 404 (the trigger’s existence is never revealed).","operationId":"PublicHooksController_fire","parameters":[{"name":"secret","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"202":{"description":"The run (job) or group-run id that was dispatched."},"400":{"description":"No resolvable target: missing Notion page id, or a payload trigger got a body with no usable content (`payload_content_missing`)."},"402":{"description":"Not enough words to run this workflow."},"404":{"description":"No enabled trigger for this secret."},"413":{"description":"A raw text body over the 10 MB cap."},"415":{"description":"A payload trigger got a Content-Type it cannot read."},"429":{"description":"Rate limit exceeded — see `Retry-After`."}},"security":[{"api_key":[]}],"summary":"Fire a workflow webhook trigger","tags":["Triggers"]}}},"info":{"title":"Transept API","description":"The Transept HTTP API: submit content, run translation and review workflows,\nand read the results back — the whole editor loop, headless.\n\nAuthenticate with an API key from Settings → Developer:\n`Authorization: Bearer tsk_live_…`. Keys are the ONLY credential here —\nheader-only, scoped to one account, and never a browser session token.\nA key carries scopes (`resource:read` / `resource:write`, picked when it is\nminted; the default is full access) and a `403 forbidden` names any scope\nthe key is missing. Keys are minted, scoped, and revoked by a human in the\napp — a key can never create more keys.\n\nAI work is billed in **words** — the source-word count of what you run,\nmultiplied by the mode (standard ×1, premium ×3). `POST /public/v1/runs/estimate`\nprices a run before you spend anything. Some actions cannot know their exact\ncost until they run (a prescan only bills the blocks it flags), so an estimate\nmay come back as a floor–ceiling range: the ceiling is held, and whatever never\nconverts is released back to your balance. Note that response fields still name\nthis balance `credits` — that is the wire name for the same words.\n\nErrors share one envelope: `{\"error\":{\"code\",\"message\",\"request_id\"}}`. Branch\non `code`; quote `request_id` in support requests.\n\nRetry safely: send an `Idempotency-Key` header on `POST /public/v1/documents`,\n`POST /public/v1/runs`, and `POST /public/v1/group-runs` and a repeat of the\nsame request replays the first response instead of creating (and billing) a\nsecond job.\n\nRate limits are per API key: 120 requests/minute across the API, and 20/minute\non the job-creating routes. Over the limit you get `429 rate_limited` with a\n`Retry-After` header.","version":"1.0.0","contact":{}},"tags":[],"servers":[{"url":"https://app.transept.ai/api"}],"components":{"securitySchemes":{"api_key":{"scheme":"bearer","bearerFormat":"tsk_live_…","type":"http","description":"A personal API key from Settings → Developer, sent as `Authorization: Bearer tsk_live_…`. Header only — never a query parameter."}},"schemas":{"CreatePublicDocumentDto":{"type":"object","properties":{"title":{"type":"string","description":"Document title. Defaults to a title derived from the content.","example":"Q3 release notes"},"content":{"type":"string","description":"The source text to import, in `content_type` format.","example":"# Release notes\n\nWe shipped the public API."},"content_type":{"type":"string","description":"How to parse `content` into blocks.","enum":["text","html","markdown"]},"source_language":{"type":"string","description":"BCP-47 language of the source. Detected when omitted.","example":"en"},"target_language":{"type":"string","description":"BCP-47 language to translate into (the master version).","example":"de"},"target_languages":{"description":"Additional target languages. Each becomes a sibling language version of the document.","maxItems":20,"example":["fr","es"],"type":"array","items":{"type":"string"}},"project_id":{"type":"string","description":"File the document into a project, inheriting its owner and defaults. See `GET /public/v1/projects`."},"document_category":{"type":"string","description":"Content category hint used when building prompts."},"glossary_ids":{"description":"Glossaries to apply, overriding any project defaults. Must belong to the caller.","type":"array","items":{"type":"string"}},"styleguide_version_ids":{"description":"Style guide versions to apply, overriding any project defaults. Must belong to the caller.","type":"array","items":{"type":"string"}}},"required":["content","content_type"]},"SeedTranslationDto":{"type":"object","properties":{}},"UpdatePublicDocumentDto":{"type":"object","properties":{"title":{"type":"string","description":"New title.","maxLength":300},"document_category":{"type":"string","description":"New category tag."},"project_id":{"type":"string","description":"Move into a project (ownership-checked). Filing into a TEAM project changes who can see the document."}}},"AddPublicLanguagesDto":{"type":"object","properties":{"target_languages":{"description":"Target languages to add as sibling versions of this document. Idempotent — languages already present in the group are skipped.","type":"array","items":{"type":"string"}}},"required":["target_languages"]},"UpdatePublicBlockDto":{"type":"object","properties":{"original_content":{"type":"array","description":"The new source content as Slate JSON (same shape `content?format=json` returns per block).","items":{"type":"object","additionalProperties":true}},"block_type":{"type":"string","description":"Change the block type (p, h1, li…)."},"hard_page_break":{"type":"boolean","description":"Force a page break before this block."}},"required":["original_content"]},"UpdatePublicTranslationDto":{"type":"object","properties":{"translated_content":{"type":"array","description":"The new translation as Slate JSON.","items":{"type":"object","additionalProperties":true}}},"required":["translated_content"]},"CreatePublicProjectDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name.","maxLength":120},"description":{"type":"string","description":"Free-text description.","maxLength":500},"emoji":{"type":"string","description":"Emoji shown on the project card."},"color":{"type":"string","description":"Accent color (hex or token)."},"team_id":{"type":"string","description":"Create as a TEAM project (documents filed into it become team-owned and bill the team pool on seated teams). Omit for a personal project. Membership-checked."},"parent_id":{"type":"string","description":"Create as a sub-project of an existing top-level project (one level of nesting). The parent must have the same owner: pass the same team_id for a team sub-project."}},"required":["name"]},"UpdatePublicProjectDto":{"type":"object","properties":{"name":{"type":"string","description":"New display name.","maxLength":120},"description":{"type":"string","description":"New description.","maxLength":500},"emoji":{"type":"string","description":"New emoji."},"color":{"type":"string","description":"New accent color."}}},"PublicTermDto":{"type":"object","properties":{"source_term":{"type":"string","description":"The source-language term."},"target_term":{"type":"string","description":"The translation in the glossary’s primary target language."},"notes":{"type":"string","description":"Usage/meaning hint for translators and the model (folds into term metadata)."},"do_not_translate":{"type":"boolean","description":"Keep the source term verbatim in every translation."},"variants":{"description":"Source-side inflections/spellings that count as this term.","type":"array","items":{"type":"string"}},"forbidden_targets":{"description":"Translations the model must NOT produce.","type":"array","items":{"type":"string"}},"term_metadata":{"type":"object","description":"Structured term metadata (context_hint, part_of_speech, …).","additionalProperties":true}}},"CreatePublicGlossaryDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name.","maxLength":200},"description":{"type":"string","description":"Free-text description.","maxLength":1000},"source_language":{"type":"string","description":"Source-language column (BCP-47)."},"primary_target_language":{"type":"string","description":"Primary target-language column (BCP-47)."},"terms":{"description":"Seed terms in the same call.","type":"array","items":{"$ref":"#/components/schemas/PublicTermDto"}}}},"UpdatePublicGlossaryDto":{"type":"object","properties":{"name":{"type":"string","description":"New display name.","maxLength":200},"description":{"type":"string","description":"New description.","maxLength":1000},"source_language":{"type":"string","description":"New source-language column."},"primary_target_language":{"type":"string","description":"New primary target-language column."}}},"PublicTermsBulkDto":{"type":"object","properties":{"terms":{"description":"Terms to add.","type":"array","items":{"$ref":"#/components/schemas/PublicTermDto"}}},"required":["terms"]},"PublicTermTranslationDto":{"type":"object","properties":{"target_term":{"type":"string","description":"The translation for this language."},"forbidden_targets":{"description":"Translations the model must NOT produce in this language.","type":"array","items":{"type":"string"}}}},"CreatePublicStyleguideDto":{"type":"object","properties":{"name":{"type":"string","description":"Display name.","maxLength":200},"description":{"type":"string","description":"Free-text description.","maxLength":1000},"initial_content":{"type":"array","description":"First version’s free-form notes as Slate JSON.","items":{"type":"object","additionalProperties":true}},"initial_structured_data":{"type":"object","description":"First version’s structured fields (tone, register, formatting rules…).","additionalProperties":true},"initial_commit_message":{"type":"string","description":"Commit message for the first version."}}},"PublicFromPresetDto":{"type":"object","properties":{"preset_id":{"type":"string","description":"Preset id from GET /public/v1/styleguide-presets."},"name":{"type":"string","description":"Name for the cloned guide."}},"required":["preset_id"]},"UpdatePublicStyleguideDto":{"type":"object","properties":{"name":{"type":"string","description":"New display name.","maxLength":200},"description":{"type":"string","description":"New description.","maxLength":1000}}},"CreatePublicStyleguideVersionDto":{"type":"object","properties":{"content":{"type":"array","description":"Free-form notes as Slate JSON.","items":{"type":"object","additionalProperties":true}},"structured_data":{"type":"object","description":"Structured fields (either field omitted = column untouched).","additionalProperties":true},"commit_message":{"type":"string","description":"Commit message shown in the history."}}},"CreateRunDto":{"type":"object","properties":{"document_id":{"type":"string","description":"The document to run the workflow over."},"workflow_id":{"type":"string","description":"A saved workflow of the caller's. Provide exactly one of `workflow_id` or `template_id`."},"template_id":{"type":"string","description":"A catalog workflow template. Provide exactly one of `workflow_id` or `template_id`."},"block_ids":{"description":"Restrict the run to these blocks. Omit to run the whole document.","type":"array","items":{"type":"string"}},"parallel_mode":{"type":"boolean","description":"Run in large-parallel mode (shared glossary/style-guide canon passes).","default":false},"updated_only":{"type":"boolean","description":"Run only on what changed. Content-generating steps (translate/rewrite) act ONLY on blocks whose source changed since their current translation; review steps (proofread) keep full scope but focus on the recently-changed blocks. The cost estimate reflects the reduced scope.","default":false}},"required":["document_id"]},"GateApproveDto":{"type":"object","properties":{"approved_block_ids":{"description":"The blocks whose findings you accept. The run continues on this subset only; omit for none.","type":"array","items":{"type":"string"}}}},"GroupRunOptionsDto":{"type":"object","properties":{"parallel_mode":{"type":"boolean","description":"Run every lane in large-parallel mode (shared glossary/style-guide canon passes).","default":false},"include_untranslated":{"type":"boolean","description":"Include language versions that have no translations yet. Keep the default `true` for a run that GENERATES content (translate/rewrite fills an empty version); set `false` for a review-only run, where a proofread on an untranslated version is a doomed zero-block lane.","default":true},"updated_only":{"type":"boolean","description":"Run only on what changed, per lane. Content-generating steps (translate/rewrite) act ONLY on each version’s blocks whose source changed since their current translation; review steps (proofread) keep full scope but focus on the recently-changed blocks. The pairs naturally with a strings re-import, where each sibling version’s delta is exactly its updated blocks.","default":false}}},"CreateGroupRunDto":{"type":"object","properties":{"document_id":{"type":"string","description":"Any document in the language group. Its master (and therefore the group) is resolved from it."},"workflow_id":{"type":"string","description":"A saved workflow of the caller’s. Provide exactly one of `workflow_id` or `template_id`."},"template_id":{"type":"string","description":"A catalog workflow template. Provide exactly one of `workflow_id` or `template_id`."},"target_languages":{"description":"BCP-47 codes to run across — a version is created for any that does not exist yet — or the string \"all\" for every language already in the group. At most 20.","oneOf":[{"type":"array","items":{"type":"string"},"maxItems":20},{"type":"string","enum":["all"]}],"example":["de","fr","es"]},"options":{"$ref":"#/components/schemas/GroupRunOptionsDto"},"block_ids":{"description":"Restrict the run to specific blocks. Two forms. A plain array scopes only `document_id`’s own lane (block ids belong to ONE document — a language version’s blocks are copies with their own ids); every other language runs whole-document. A map of `{document_id: [block_ids]}` scopes EACH listed lane to its own blocks — the shape a strings re-import delta hands back (`versions[].added_block_ids` + `stale_block_ids`), so \"translate only what changed, in every language\" is one call. Omit to run the whole document everywhere.","oneOf":[{"type":"array","items":{"type":"string"}},{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}]},"draft":{"type":"object","description":"An unsaved workflow to run on every lane (the builder’s \"run this draft\"). Provide exactly one run selector.","additionalProperties":true},"smart_action_id":{"type":"string","description":"A free inline check, by id. The server owns its definition, so these lanes are not billed."}},"required":["document_id","target_languages"]},"CreateWebhookDto":{"type":"object","properties":{"url":{"type":"string","description":"HTTPS URL Transept will POST signed event envelopes to.","example":"https://example.com/hooks/transept"},"description":{"type":"string","description":"Human note shown in listings."},"events":{"description":"Subscribed event types, or [\"*\"] for all. Omit to default to all.","example":["run.completed","group_run.completed"],"type":"array","items":{"type":"string"}}},"required":["url"]},"UpdateWebhookDto":{"type":"object","properties":{"url":{"type":"string","description":"New delivery URL (re-validated)."},"description":{"type":"string","description":"Human note shown in listings."},"events":{"description":"Replacement event-type list, or [\"*\"] for all.","type":"array","items":{"type":"string"}},"enabled":{"type":"boolean","description":"Enable/disable deliveries. Re-enabling a tripped endpoint resets its failure counter."}}},"ImportStringsDto":{"type":"object","properties":{"document":{"type":"object","description":"The .tstrings.json document inline (format: \"transept-strings\", version 1, source_locale, units[]). Units may carry `targets` — those land as active translations, zero words billed.","additionalProperties":true},"project_id":{"type":"string","description":"Project to file the language group into. Ownership-checked."},"target_language":{"type":"string","description":"The master version's target language. Defaults to the first resolved locale."},"target_locales":{"description":"Locales to produce (capped at 20 including the master). Wins over the document's own target_locales hint.","type":"array","items":{"type":"string"}},"use_target_locales":{"type":"boolean","description":"Fan out to the document's own target_locales list when no explicit target_locales is passed."},"document_category":{"type":"string","description":"Document category tag."}},"required":["document"]},"ReimportRunDto":{"type":"object","properties":{"workflow_id":{"type":"string","description":"A saved workflow to run over the changed blocks. One of workflow_id or template_id."},"template_id":{"type":"string","description":"A catalog template to run over the changed blocks.","example":"help_article"}}},"ReimportStringsDto":{"type":"object","properties":{"document":{"type":"object","description":"The COMPLETE current Transept Strings document — not a patch. The server diffs it against what it already holds; untouched units keep their translations.","additionalProperties":true},"run":{"description":"Translate the delta immediately: dispatches a group run scoped to the blocks this reimport added or invalidated, across every language version. Omit to get the delta back and dispatch it yourself.","allOf":[{"$ref":"#/components/schemas/ReimportRunDto"}]},"overwrite_targets":{"type":"boolean","description":"Let the file's `targets` overwrite translations Transept already holds for units that changed. Off by default: the file owns sources, Transept owns translations once a unit is managed here.","default":false}},"required":["document"]}}}}