{
  "openapi": "3.1.0",
  "x-scopes": [
    {
      "scope": "sites:read",
      "operations": [
        "GET /v1/sites",
        "GET /v1/sites/{siteId}"
      ]
    },
    {
      "scope": "sites:write",
      "operations": [
        "POST /v1/sites",
        "PATCH /v1/sites/{siteId}",
        "POST /v1/sites/{siteId}/scan"
      ]
    },
    {
      "scope": "strings:write",
      "operations": [
        "POST /v1/sites/{siteId}/strings"
      ]
    },
    {
      "scope": "translations:read",
      "operations": [
        "GET /v1/sites/{siteId}/translations",
        "GET /v1/sites/{siteId}/glossary",
        "GET /v1/sites/{siteId}/export"
      ]
    },
    {
      "scope": "translations:write",
      "operations": [
        "PUT /v1/sites/{siteId}/translations/{lang}/{key}",
        "POST /v1/sites/{siteId}/translate",
        "PUT /v1/sites/{siteId}/glossary",
        "POST /v1/sites/{siteId}/import"
      ]
    },
    {
      "scope": "bundles:publish",
      "operations": [
        "POST /v1/sites/{siteId}/publish"
      ]
    },
    {
      "scope": "usage:read",
      "operations": [
        "GET /v1/sites/{siteId}/usage",
        "GET /v1/usage/quota"
      ]
    },
    {
      "scope": "keys:manage",
      "operations": [
        "GET /v1/keys",
        "POST /v1/keys",
        "POST /v1/keys/{id}/rotate",
        "DELETE /v1/keys/{id}"
      ]
    },
    {
      "scope": "webhooks:manage",
      "operations": []
    },
    {
      "scope": "audit:read",
      "operations": []
    },
    {
      "scope": "audit:manage",
      "operations": []
    },
    {
      "scope": "billing:read",
      "operations": []
    },
    {
      "scope": "billing:manage",
      "operations": []
    }
  ],
  "tags": [
    {
      "name": "System",
      "description": "Liveness and this document. No key needed."
    },
    {
      "name": "Sites",
      "description": "Create and configure sites (name, languages, allowed origins)."
    },
    {
      "name": "Strings",
      "description": "Submit extracted source strings; keys are computed by the server (BUNDLE_FORMAT.md)."
    },
    {
      "name": "Translations",
      "description": "List translations and set manual overrides, which machine runs never overwrite."
    },
    {
      "name": "Glossary",
      "description": "Do-not-translate terms and preferred translations."
    },
    {
      "name": "Jobs",
      "description": "Asynchronous work (scan, translate, publish) and its status."
    },
    {
      "name": "Interchange",
      "description": "Export and import translations as JSON, XLIFF, PO/POT, CSV or TMX (the ownership guarantee)."
    },
    {
      "name": "Usage",
      "description": "Strings, characters, engine cost and the plan allowance."
    },
    {
      "name": "Keys",
      "description": "Secret API keys: create, rotate, revoke. Secrets are shown once."
    },
    {
      "name": "Discovery",
      "description": "Optional browser-side reporting of untranslated strings (site key and allowed Origin, off by default)."
    }
  ],
  "x-problem-types": [
    {
      "type": "urn:resilientlang:problem:validation",
      "status": 400,
      "meaning": "One or more fields are invalid; see errors{field: [messages]}."
    },
    {
      "type": "urn:resilientlang:problem:bad-json",
      "status": 400,
      "meaning": "The body is not valid JSON for this endpoint."
    },
    {
      "type": "urn:resilientlang:problem:bad-request",
      "status": 400,
      "meaning": "Malformed request (missing body, malformed multipart, too many parts)."
    },
    {
      "type": "urn:resilientlang:problem:no-allowed-origins",
      "status": 400,
      "meaning": "The site has no allowed origins, so it cannot be scanned."
    },
    {
      "type": "urn:resilientlang:problem:import-format",
      "status": 400,
      "meaning": "The uploaded file could not be read (malformed, not UTF-8, DTD, over limits)."
    },
    {
      "type": "urn:resilientlang:problem:unauthorized",
      "status": 401,
      "meaning": "Missing, malformed, unknown, expired or revoked secret key."
    },
    {
      "type": "urn:resilientlang:problem:insufficient-scope",
      "status": 403,
      "meaning": "The key lacks the scope this endpoint requires."
    },
    {
      "type": "urn:resilientlang:problem:account-suspended",
      "status": 403,
      "meaning": "The account or site is suspended: writes are refused, reads and already-published translations keep working."
    },
    {
      "type": "urn:resilientlang:problem:site-restricted-key",
      "status": 403,
      "meaning": "The key is restricted to one site and cannot do this."
    },
    {
      "type": "urn:resilientlang:problem:origin-not-allowed",
      "status": 403,
      "meaning": "Discover: unknown site key or the request Origin is not allowed."
    },
    {
      "type": "urn:resilientlang:problem:test-key-cannot-issue",
      "status": 403,
      "meaning": "Test keys cannot create keys."
    },
    {
      "type": "urn:resilientlang:problem:not-found",
      "status": 404,
      "meaning": "The resource does not exist or is not accessible with this key."
    },
    {
      "type": "urn:resilientlang:problem:method-not-allowed",
      "status": 405,
      "meaning": "Wrong HTTP method for the path."
    },
    {
      "type": "urn:resilientlang:problem:quota-exhausted",
      "status": 402,
      "meaning": "The AI-word allowance for this period is used up. See Retry-After and resetsOn; add a word pack or bring your own provider key."
    },
    {
      "type": "urn:resilientlang:problem:machine-translation-disabled",
      "status": 402,
      "meaning": "Machine translation is not available (licence not active)."
    },
    {
      "type": "urn:resilientlang:problem:daily-ceiling",
      "status": 402,
      "meaning": "Daily translation limit reached; retry after Retry-After."
    },
    {
      "type": "urn:resilientlang:problem:strings-cap",
      "status": 402,
      "meaning": "The plan limit on submitted strings is reached."
    },
    {
      "type": "urn:resilientlang:problem:site-limit",
      "status": 402,
      "meaning": "The plan allows no more sites."
    },
    {
      "type": "urn:resilientlang:problem:language-limit",
      "status": 402,
      "meaning": "The plan allows no more target languages."
    },
    {
      "type": "urn:resilientlang:problem:ai-words-exhausted",
      "status": 402,
      "meaning": "The plan\u0027s AI-word allowance is exhausted."
    },
    {
      "type": "urn:resilientlang:problem:lapsed-publish-blocked",
      "status": 402,
      "meaning": "Publishing is blocked because the plan or licence has lapsed."
    },
    {
      "type": "urn:resilientlang:problem:feature-unavailable",
      "status": 402,
      "meaning": "The plan does not include this feature."
    },
    {
      "type": "urn:resilientlang:problem:licence-inactive",
      "status": 402,
      "meaning": "The licence does not allow this action (translate, add_language, publish); already-published bundles keep being served."
    },
    {
      "type": "urn:resilientlang:problem:idempotency-in-flight",
      "status": 409,
      "meaning": "A request with the same Idempotency-Key is still running; retry after Retry-After."
    },
    {
      "type": "urn:resilientlang:problem:payload-too-large",
      "status": 413,
      "meaning": "The body or file exceeds the limit."
    },
    {
      "type": "urn:resilientlang:problem:unsupported-media-type",
      "status": 415,
      "meaning": "Use application/json (or multipart/form-data for import)."
    },
    {
      "type": "urn:resilientlang:problem:idempotency-key-reused",
      "status": 422,
      "meaning": "The Idempotency-Key was used with a different request."
    },
    {
      "type": "urn:resilientlang:problem:import-rejected",
      "status": 422,
      "meaning": "The import has errors; nothing was written. See issues[]."
    },
    {
      "type": "urn:resilientlang:problem:rate-limited",
      "status": 429,
      "meaning": "Too many requests; wait Retry-After seconds."
    },
    {
      "type": "urn:resilientlang:problem:limit-reached",
      "status": 429,
      "meaning": "A hard abuse ceiling that does not depend on the plan was reached (for example the number of source strings one site may hold, ResilientLang:Limits:MaxStringsPerSite)."
    },
    {
      "type": "urn:resilientlang:problem:gate-unavailable",
      "status": 503,
      "meaning": "A plan, quota, licence or suspension check could not be completed (fails closed); retry after Retry-After."
    },
    {
      "type": "urn:resilientlang:problem:service-unavailable",
      "status": 503,
      "meaning": "The service could not reach its database (or it timed out); nothing was changed. Retry after Retry-After."
    },
    {
      "type": "urn:resilientlang:problem:internal",
      "status": 500,
      "meaning": "Unexpected error; quote the reference in detail when reporting it."
    }
  ],
  "info": {
    "title": "ResilientLang control-plane API",
    "version": "1.0.0",
    "description": "Source of truth: docs/architecture/API.md. Errors are RFC 9457 application/problem\u002Bjson. Pagination: ?cursor=\u0026limit= (max 100). Write endpoints accept Idempotency-Key."
  },
  "servers": [
    {
      "url": "/"
    }
  ],
  "paths": {
    "/v1/health": {
      "get": {
        "summary": "Liveness (no data)",
        "operationId": "getHealth",
        "tags": [
          "System"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      }
    },
    "/v1/openapi.json": {
      "get": {
        "summary": "This document",
        "operationId": "getOpenapi.json",
        "tags": [
          "System"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenApi"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      }
    },
    "/v1/sites": {
      "get": {
        "summary": "List sites",
        "operationId": "getSites",
        "tags": [
          "Sites"
        ],
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "sites:read",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SitePage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      },
      "post": {
        "summary": "Create a site",
        "operationId": "postSites",
        "tags": [
          "Sites"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "sites:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSiteRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Site"
                },
                "example": {
                  "id": "3f2b1c9e-6a4d-4c8e-9a53-0d2f7b1e8c11",
                  "siteKey": "rl_site_live_abcdefghijklmnopqrstuv",
                  "name": "My shop",
                  "sourceLanguage": "en",
                  "targetLanguages": [
                    "fr",
                    "de"
                  ],
                  "allowedOrigins": [
                    "https://shop.example.com"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "402": {
            "$ref": "#/components/responses/Problem402"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "409": {
            "$ref": "#/components/responses/Problem409"
          },
          "413": {
            "$ref": "#/components/responses/Problem413"
          },
          "415": {
            "$ref": "#/components/responses/Problem415"
          },
          "422": {
            "$ref": "#/components/responses/Problem422"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          },
          "503": {
            "$ref": "#/components/responses/Problem503"
          }
        }
      }
    },
    "/v1/sites/{siteId}": {
      "get": {
        "summary": "Get a site",
        "operationId": "getSitesSiteId",
        "tags": [
          "Sites"
        ],
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "sites:read",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Site"
                },
                "example": {
                  "id": "3f2b1c9e-6a4d-4c8e-9a53-0d2f7b1e8c11",
                  "siteKey": "rl_site_live_abcdefghijklmnopqrstuv",
                  "name": "My shop",
                  "sourceLanguage": "en",
                  "targetLanguages": [
                    "fr",
                    "de"
                  ],
                  "allowedOrigins": [
                    "https://shop.example.com"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      },
      "patch": {
        "summary": "Update site settings",
        "operationId": "patchSitesSiteId",
        "tags": [
          "Sites"
        ],
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "sites:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchSiteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Site"
                },
                "example": {
                  "id": "3f2b1c9e-6a4d-4c8e-9a53-0d2f7b1e8c11",
                  "siteKey": "rl_site_live_abcdefghijklmnopqrstuv",
                  "name": "My shop",
                  "sourceLanguage": "en",
                  "targetLanguages": [
                    "fr",
                    "de"
                  ],
                  "allowedOrigins": [
                    "https://shop.example.com"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "402": {
            "$ref": "#/components/responses/Problem402"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "409": {
            "$ref": "#/components/responses/Problem409"
          },
          "413": {
            "$ref": "#/components/responses/Problem413"
          },
          "415": {
            "$ref": "#/components/responses/Problem415"
          },
          "422": {
            "$ref": "#/components/responses/Problem422"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          },
          "503": {
            "$ref": "#/components/responses/Problem503"
          }
        }
      }
    },
    "/v1/sites/{siteId}/strings": {
      "post": {
        "summary": "Submit extracted strings (max 1000)",
        "operationId": "postSitesSiteIdStrings",
        "tags": [
          "Strings"
        ],
        "description": "Up to 1000 items. The response lists the key of every item in order; identical text (after normalisation) yields the same key and is stored once.",
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "strings:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmitStringsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StringKeys"
                },
                "example": {
                  "items": [
                    {
                      "key": "0123456789abcdef",
                      "kind": "text"
                    },
                    {
                      "key": "fedcba9876543210",
                      "kind": "attr"
                    }
                  ],
                  "stored": 2
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "402": {
            "$ref": "#/components/responses/Problem402"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "409": {
            "$ref": "#/components/responses/Problem409"
          },
          "413": {
            "$ref": "#/components/responses/Problem413"
          },
          "415": {
            "$ref": "#/components/responses/Problem415"
          },
          "422": {
            "$ref": "#/components/responses/Problem422"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          },
          "503": {
            "$ref": "#/components/responses/Problem503"
          }
        }
      }
    },
    "/v1/sites/{siteId}/scan": {
      "post": {
        "summary": "Start a crawl (async job)",
        "operationId": "postSitesSiteIdScan",
        "tags": [
          "Jobs"
        ],
        "description": "Every URL and the sitemap URL must be on one of the site\u0027s allowedOrigins and pass the SSRF check. Returns a job.",
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "sites:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScanRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                },
                "example": {
                  "id": "9b1e6c52-2f43-4a8e-8d0b-5c7a1f3e9a20",
                  "kind": "publish",
                  "siteId": "3f2b1c9e-6a4d-4c8e-9a53-0d2f7b1e8c11",
                  "state": "succeeded",
                  "attempts": 1,
                  "progressPercent": 100,
                  "createdAt": "2026-10-28T12:00:00Z",
                  "finishedAt": "2026-10-28T12:00:09Z",
                  "result": {
                    "version": 7,
                    "publishedAt": "2026-10-28T12:00:09Z",
                    "targets": [
                      {
                        "target": "r2-primary",
                        "ok": true
                      },
                      {
                        "target": "cdn-secondary",
                        "ok": true
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "409": {
            "$ref": "#/components/responses/Problem409"
          },
          "413": {
            "$ref": "#/components/responses/Problem413"
          },
          "415": {
            "$ref": "#/components/responses/Problem415"
          },
          "422": {
            "$ref": "#/components/responses/Problem422"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      }
    },
    "/v1/sites/{siteId}/translations": {
      "get": {
        "summary": "List translations",
        "operationId": "getSitesSiteIdTranslations",
        "tags": [
          "Translations"
        ],
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "translations:read",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TranslationPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      }
    },
    "/v1/sites/{siteId}/translations/{lang}/{key}": {
      "put": {
        "summary": "Manual override",
        "operationId": "putSitesSiteIdTranslationsLangKey",
        "tags": [
          "Translations"
        ],
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lang",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "translations:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PutTranslationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Translation"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "409": {
            "$ref": "#/components/responses/Problem409"
          },
          "413": {
            "$ref": "#/components/responses/Problem413"
          },
          "415": {
            "$ref": "#/components/responses/Problem415"
          },
          "422": {
            "$ref": "#/components/responses/Problem422"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      }
    },
    "/v1/sites/{siteId}/translate": {
      "post": {
        "summary": "Machine-translate missing strings (async job)",
        "operationId": "postSitesSiteIdTranslate",
        "tags": [
          "Jobs"
        ],
        "description": "Enqueues one job per target language (or per requested language). Never overwrites reviewed or manual translations.",
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "translations:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TranslateRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Jobs"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "402": {
            "$ref": "#/components/responses/Problem402"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "409": {
            "$ref": "#/components/responses/Problem409"
          },
          "413": {
            "$ref": "#/components/responses/Problem413"
          },
          "415": {
            "$ref": "#/components/responses/Problem415"
          },
          "422": {
            "$ref": "#/components/responses/Problem422"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          },
          "503": {
            "$ref": "#/components/responses/Problem503"
          }
        }
      }
    },
    "/v1/sites/{siteId}/glossary": {
      "get": {
        "summary": "Get glossary",
        "operationId": "getSitesSiteIdGlossary",
        "tags": [
          "Glossary"
        ],
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "translations:read",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Glossary"
                },
                "example": {
                  "doNotTranslate": [
                    "Acme"
                  ],
                  "preferred": [
                    {
                      "source": "cart",
                      "lang": "fr",
                      "target": "panier"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      },
      "put": {
        "summary": "Replace glossary",
        "operationId": "putSitesSiteIdGlossary",
        "tags": [
          "Glossary"
        ],
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "translations:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Glossary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Glossary"
                },
                "example": {
                  "doNotTranslate": [
                    "Acme"
                  ],
                  "preferred": [
                    {
                      "source": "cart",
                      "lang": "fr",
                      "target": "panier"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "409": {
            "$ref": "#/components/responses/Problem409"
          },
          "413": {
            "$ref": "#/components/responses/Problem413"
          },
          "415": {
            "$ref": "#/components/responses/Problem415"
          },
          "422": {
            "$ref": "#/components/responses/Problem422"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      }
    },
    "/v1/sites/{siteId}/publish": {
      "post": {
        "summary": "Build and publish bundles (async job)",
        "operationId": "postSitesSiteIdPublish",
        "tags": [
          "Jobs"
        ],
        "description": "Builds and publishes signed bundles to every storage target. Poll the job; a succeeded job\u0027s \u0060result.version\u0060 is the published version.",
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "bundles:publish",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PublishRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                },
                "example": {
                  "id": "9b1e6c52-2f43-4a8e-8d0b-5c7a1f3e9a20",
                  "kind": "publish",
                  "siteId": "3f2b1c9e-6a4d-4c8e-9a53-0d2f7b1e8c11",
                  "state": "succeeded",
                  "attempts": 1,
                  "progressPercent": 100,
                  "createdAt": "2026-10-28T12:00:00Z",
                  "finishedAt": "2026-10-28T12:00:09Z",
                  "result": {
                    "version": 7,
                    "publishedAt": "2026-10-28T12:00:09Z",
                    "targets": [
                      {
                        "target": "r2-primary",
                        "ok": true
                      },
                      {
                        "target": "cdn-secondary",
                        "ok": true
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "402": {
            "$ref": "#/components/responses/Problem402"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "409": {
            "$ref": "#/components/responses/Problem409"
          },
          "413": {
            "$ref": "#/components/responses/Problem413"
          },
          "415": {
            "$ref": "#/components/responses/Problem415"
          },
          "422": {
            "$ref": "#/components/responses/Problem422"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          },
          "503": {
            "$ref": "#/components/responses/Problem503"
          }
        }
      }
    },
    "/v1/jobs/{jobId}": {
      "get": {
        "summary": "Job status",
        "operationId": "getJobsJobId",
        "tags": [
          "Jobs"
        ],
        "description": "Status of a job created by scan, translate or publish. Succeeded publish jobs include result.version, publishedAt and per-target results.",
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                },
                "example": {
                  "id": "9b1e6c52-2f43-4a8e-8d0b-5c7a1f3e9a20",
                  "kind": "publish",
                  "siteId": "3f2b1c9e-6a4d-4c8e-9a53-0d2f7b1e8c11",
                  "state": "succeeded",
                  "attempts": 1,
                  "progressPercent": 100,
                  "createdAt": "2026-10-28T12:00:00Z",
                  "finishedAt": "2026-10-28T12:00:09Z",
                  "result": {
                    "version": 7,
                    "publishedAt": "2026-10-28T12:00:09Z",
                    "targets": [
                      {
                        "target": "r2-primary",
                        "ok": true
                      },
                      {
                        "target": "cdn-secondary",
                        "ok": true
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      }
    },
    "/v1/sites/{siteId}/export": {
      "get": {
        "summary": "Full export (format=json|xliff|xliff2|po|pot|csv|tmx, optional lang)",
        "operationId": "getSitesSiteIdExport",
        "tags": [
          "Interchange"
        ],
        "description": "Returns every translation of the site in the chosen format so a customer can keep or edit them elsewhere. Never cached. Formats that hold one language per file (po, xliff2) return a .zip when several languages are included.",
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "xliff",
                "xliff2",
                "po",
                "pot",
                "csv",
                "tmx"
              ],
              "default": "json"
            },
            "description": "Output format. xliff = XLIFF 1.2, xliff2 = XLIFF 2.x, pot = blank gettext template. Formats that hold one language (po, xliff2) return a .zip when several languages are exported."
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Limit to these target languages; repeat the parameter or separate with commas. Default: all.",
            "explode": true
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "translations:read",
        "responses": {
          "200": {
            "description": "The export file (a .zip when several one-language files are produced). Always Cache-Control: no-store.",
            "content": {
              "application/json": {},
              "application/xliff\u002Bxml": {},
              "application/x-xliff\u002Bxml": {},
              "text/x-gettext-translation": {},
              "text/csv": {},
              "application/x-tmx\u002Bxml": {},
              "application/zip": {}
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      }
    },
    "/v1/sites/{siteId}/import": {
      "post": {
        "summary": "Import translations from a file (multipart) or the JSON export (application/json)",
        "operationId": "postSitesSiteIdImport",
        "tags": [
          "Interchange"
        ],
        "description": "Send multipart/form-data with field \u0060file\u0060 (and \u0060reference\u0060 for key-based formats such as android, messages-json and keyvalue-json), plus optional fields \u0060format\u0060 (auto-detected when omitted), \u0060lang\u0060 (for single-language files that do not state one), \u0060status\u0060 (machine|reviewed|manual: overrides every entry\u0027s status; default: the file\u0027s own status, else manual) and \u0060dryRun\u0060 (true = report the plan, write nothing). Strings the site does not know are skipped and listed in \u0060issues\u0060. Entries whose inline tags or placeholders do not survive are rejected. Any error-level issue rejects the whole import (422).",
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "translations:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ImportForm"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportResult"
                },
                "example": {
                  "formatId": "xliff",
                  "dryRun": false,
                  "applied": true,
                  "planned": 1,
                  "written": 1,
                  "skippedProtected": 0,
                  "stats": {
                    "unitsRead": 2,
                    "matched": 1,
                    "skippedUntranslated": 0,
                    "skippedUnknown": 1,
                    "skippedInvalid": 0,
                    "conflicts": 0
                  },
                  "issues": [
                    {
                      "severity": "warning",
                      "code": "unknown-string",
                      "message": "String not found on this site; skipped.",
                      "where": "trans-unit b"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "409": {
            "$ref": "#/components/responses/Problem409"
          },
          "413": {
            "$ref": "#/components/responses/Problem413"
          },
          "415": {
            "$ref": "#/components/responses/Problem415"
          },
          "422": {
            "$ref": "#/components/responses/Problem422"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      }
    },
    "/v1/sites/{siteId}/usage": {
      "get": {
        "summary": "Strings, characters, engine cost",
        "operationId": "getSitesSiteIdUsage",
        "tags": [
          "Usage"
        ],
        "parameters": [
          {
            "name": "siteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "usage:read",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      }
    },
    "/v1/usage/quota": {
      "get": {
        "summary": "Plan allowance and period usage for the key\u0027s tenant",
        "operationId": "getUsageQuota",
        "tags": [
          "Usage"
        ],
        "description": "The plan allowance and where the tenant stands in the current period (404 when quotas are not enabled on the deployment).",
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "usage:read",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quota"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          },
          "503": {
            "$ref": "#/components/responses/Problem503"
          }
        }
      }
    },
    "/v1/keys": {
      "get": {
        "summary": "List secret keys (never returns secrets)",
        "operationId": "getKeys",
        "tags": [
          "Keys"
        ],
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "keys:manage",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      },
      "post": {
        "summary": "Create a secret key (secret shown once)",
        "operationId": "postKeys",
        "tags": [
          "Keys"
        ],
        "description": "The secret is returned once in this response and never again. The new key cannot hold scopes the calling key lacks. Test keys cannot create keys. Responses are never replayed from an Idempotency-Key.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "keys:manage",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateKeyRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyWithSecret"
                },
                "example": {
                  "id": "c2a4f0de-7b61-4e3a-9f55-1a8b2c3d4e5f",
                  "name": "CI",
                  "prefix": "rl_sec_live_",
                  "kind": "secret",
                  "scopes": [
                    "strings:write",
                    "bundles:publish"
                  ],
                  "secret": "rl_sec_live_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "409": {
            "$ref": "#/components/responses/Problem409"
          },
          "413": {
            "$ref": "#/components/responses/Problem413"
          },
          "415": {
            "$ref": "#/components/responses/Problem415"
          },
          "422": {
            "$ref": "#/components/responses/Problem422"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      }
    },
    "/v1/keys/{id}/rotate": {
      "post": {
        "summary": "Rotate a key (new valid now, old valid for a grace period)",
        "operationId": "postKeysIdRotate",
        "tags": [
          "Keys"
        ],
        "description": "The new key works immediately; the old key keeps working for gracePeriodSeconds (default 24 h, max 30 days).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "keys:manage",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RotateKeyRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyWithSecret"
                },
                "example": {
                  "id": "c2a4f0de-7b61-4e3a-9f55-1a8b2c3d4e5f",
                  "name": "CI",
                  "prefix": "rl_sec_live_",
                  "kind": "secret",
                  "scopes": [
                    "strings:write",
                    "bundles:publish"
                  ],
                  "secret": "rl_sec_live_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "409": {
            "$ref": "#/components/responses/Problem409"
          },
          "413": {
            "$ref": "#/components/responses/Problem413"
          },
          "415": {
            "$ref": "#/components/responses/Problem415"
          },
          "422": {
            "$ref": "#/components/responses/Problem422"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      }
    },
    "/v1/keys/{id}": {
      "delete": {
        "summary": "Revoke a key",
        "operationId": "deleteKeysId",
        "tags": [
          "Keys"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "x-required-scope": "keys:manage",
        "responses": {
          "204": {
            "description": "No content"
          },
          "401": {
            "$ref": "#/components/responses/Problem401"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "409": {
            "$ref": "#/components/responses/Problem409"
          },
          "422": {
            "$ref": "#/components/responses/Problem422"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          }
        }
      }
    },
    "/v1/discover": {
      "post": {
        "summary": "Browser snippet reports untranslated strings (site key \u002B allowed Origin; off by default)",
        "operationId": "postDiscover",
        "tags": [
          "Discovery"
        ],
        "description": "Disabled unless the deployment enables it (404). Authenticated by the public site key plus the browser\u0027s Origin header, which must be on the site\u0027s allowedOrigins.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DiscoverRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Accepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem400"
          },
          "402": {
            "$ref": "#/components/responses/Problem402"
          },
          "403": {
            "$ref": "#/components/responses/Problem403"
          },
          "404": {
            "$ref": "#/components/responses/Problem404"
          },
          "413": {
            "$ref": "#/components/responses/Problem413"
          },
          "415": {
            "$ref": "#/components/responses/Problem415"
          },
          "422": {
            "$ref": "#/components/responses/Problem422"
          },
          "429": {
            "$ref": "#/components/responses/Problem429"
          },
          "500": {
            "$ref": "#/components/responses/Problem500"
          },
          "503": {
            "$ref": "#/components/responses/Problem503"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "secretKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Secret API key rl_sec_{live|test}_.... Scopes: sites:read, sites:write, strings:write, translations:read, translations:write, bundles:publish, usage:read, keys:manage, webhooks:manage, audit:read, audit:manage, billing:read, billing:manage. See x-scopes for the operations each scope unlocks."
      }
    },
    "responses": {
      "Problem400": {
        "description": "Validation failed or malformed request",
        "content": {
          "application/problem\u002Bjson": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem401": {
        "description": "Missing or invalid API key",
        "content": {
          "application/problem\u002Bjson": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem403": {
        "description": "Insufficient scope, or origin not allowed",
        "content": {
          "application/problem\u002Bjson": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem404": {
        "description": "Not found (also returned for other tenants\u0027 resources)",
        "content": {
          "application/problem\u002Bjson": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem409": {
        "description": "Idempotent request still in progress",
        "content": {
          "application/problem\u002Bjson": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem413": {
        "description": "Body too large",
        "content": {
          "application/problem\u002Bjson": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem410": {
        "description": "Gone (expired)",
        "content": {
          "application/problem\u002Bjson": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem415": {
        "description": "Unsupported media type",
        "content": {
          "application/problem\u002Bjson": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem502": {
        "description": "The upstream provider failed",
        "content": {
          "application/problem\u002Bjson": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem422": {
        "description": "Idempotency-Key reused with a different request, or the import was rejected",
        "content": {
          "application/problem\u002Bjson": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem429": {
        "description": "Rate limited (see Retry-After)",
        "content": {
          "application/problem\u002Bjson": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem500": {
        "description": "Unexpected error (no details are exposed; quote the reference)",
        "content": {
          "application/problem\u002Bjson": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem402": {
        "description": "A plan, allowance or licence blocks this action (see the problem type; D41). Retry-After is set when the allowance resets.",
        "content": {
          "application/problem\u002Bjson": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem503": {
        "description": "A plan/quota/licence/suspension check was unavailable (fails closed); retry after Retry-After.",
        "content": {
          "application/problem\u002Bjson": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "schemas": {
      "Problem": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "urn:resilientlang:problem:validation",
              "urn:resilientlang:problem:bad-json",
              "urn:resilientlang:problem:bad-request",
              "urn:resilientlang:problem:no-allowed-origins",
              "urn:resilientlang:problem:import-format",
              "urn:resilientlang:problem:unauthorized",
              "urn:resilientlang:problem:insufficient-scope",
              "urn:resilientlang:problem:account-suspended",
              "urn:resilientlang:problem:site-restricted-key",
              "urn:resilientlang:problem:origin-not-allowed",
              "urn:resilientlang:problem:test-key-cannot-issue",
              "urn:resilientlang:problem:not-found",
              "urn:resilientlang:problem:method-not-allowed",
              "urn:resilientlang:problem:quota-exhausted",
              "urn:resilientlang:problem:machine-translation-disabled",
              "urn:resilientlang:problem:daily-ceiling",
              "urn:resilientlang:problem:strings-cap",
              "urn:resilientlang:problem:site-limit",
              "urn:resilientlang:problem:language-limit",
              "urn:resilientlang:problem:ai-words-exhausted",
              "urn:resilientlang:problem:lapsed-publish-blocked",
              "urn:resilientlang:problem:feature-unavailable",
              "urn:resilientlang:problem:licence-inactive",
              "urn:resilientlang:problem:idempotency-in-flight",
              "urn:resilientlang:problem:payload-too-large",
              "urn:resilientlang:problem:unsupported-media-type",
              "urn:resilientlang:problem:idempotency-key-reused",
              "urn:resilientlang:problem:import-rejected",
              "urn:resilientlang:problem:rate-limited",
              "urn:resilientlang:problem:limit-reached",
              "urn:resilientlang:problem:gate-unavailable",
              "urn:resilientlang:problem:service-unavailable",
              "urn:resilientlang:problem:internal"
            ]
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string"
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "issues": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ImportIssue"
            }
          }
        },
        "required": [
          "type",
          "title",
          "status"
        ]
      },
      "ImportIssue": {
        "type": "object",
        "properties": {
          "severity": {
            "type": "string",
            "enum": [
              "info",
              "warning",
              "error"
            ]
          },
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "where": {
            "type": "string"
          }
        },
        "required": [
          "severity",
          "code",
          "message"
        ]
      },
      "Health": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          }
        },
        "required": [
          "status"
        ]
      },
      "OpenApi": {
        "type": "object"
      },
      "Site": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "siteKey": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "sourceLanguage": {
            "type": "string"
          },
          "targetLanguages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "allowedOrigins": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "id",
          "siteKey",
          "name",
          "sourceLanguage",
          "targetLanguages",
          "allowedOrigins"
        ]
      },
      "SitePage": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Site"
            }
          },
          "nextCursor": {
            "type": "string"
          }
        },
        "required": [
          "items"
        ]
      },
      "CreateSiteRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "sourceLanguage": {
            "type": "string"
          },
          "targetLanguages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "allowedOrigins": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "name",
          "sourceLanguage",
          "targetLanguages"
        ]
      },
      "PatchSiteRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "targetLanguages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "allowedOrigins": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "StringItem": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "text",
              "attr"
            ]
          },
          "context": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        },
        "required": [
          "text"
        ]
      },
      "SubmitStringsRequest": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "maxItems": 1000,
            "items": {
              "$ref": "#/components/schemas/StringItem"
            }
          }
        },
        "required": [
          "items"
        ]
      },
      "StringKeys": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string"
                },
                "kind": {
                  "type": "string"
                }
              },
              "required": [
                "key",
                "kind"
              ]
            }
          },
          "stored": {
            "type": "integer"
          }
        },
        "required": [
          "items"
        ]
      },
      "Translation": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string"
          },
          "lang": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "machine",
              "reviewed",
              "manual"
            ]
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "flags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "key",
          "lang",
          "text",
          "status",
          "updatedAt"
        ]
      },
      "TranslationPage": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Translation"
            }
          },
          "nextCursor": {
            "type": "string"
          }
        },
        "required": [
          "items"
        ]
      },
      "PutTranslationRequest": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string"
          }
        },
        "required": [
          "text"
        ]
      },
      "ScanRequest": {
        "type": "object",
        "properties": {
          "urls": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sitemapUrl": {
            "type": "string"
          },
          "respectRobots": {
            "type": "boolean"
          }
        }
      },
      "TranslateRequest": {
        "type": "object",
        "properties": {
          "langs": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "keys": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "force": {
            "type": "boolean"
          }
        }
      },
      "PublishRequest": {
        "type": "object",
        "properties": {
          "onlyIfChanged": {
            "type": "boolean"
          }
        }
      },
      "Jobs": {
        "type": "object",
        "properties": {
          "jobs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Job"
            }
          }
        },
        "required": [
          "jobs"
        ]
      },
      "Job": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "scan",
              "translate",
              "publish"
            ]
          },
          "siteId": {
            "type": "string",
            "format": "uuid"
          },
          "state": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "succeeded",
              "failed",
              "canceled"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "progressPercent": {
            "type": "integer"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "finishedAt": {
            "type": "string",
            "format": "date-time"
          },
          "error": {
            "type": "string"
          },
          "result": {
            "description": "Present on succeeded publish jobs.",
            "type": "object",
            "properties": {
              "version": {
                "type": "integer"
              },
              "publishedAt": {
                "type": "string",
                "format": "date-time"
              },
              "targets": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "target": {
                      "type": "string"
                    },
                    "ok": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "target",
                    "ok"
                  ]
                }
              }
            },
            "required": [
              "version"
            ]
          }
        },
        "required": [
          "id",
          "kind",
          "siteId",
          "state",
          "attempts",
          "progressPercent",
          "createdAt"
        ]
      },
      "Glossary": {
        "type": "object",
        "properties": {
          "doNotTranslate": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "preferred": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "source": {
                  "type": "string"
                },
                "lang": {
                  "type": "string",
                  "description": "Omit to apply to every target language."
                },
                "target": {
                  "type": "string"
                }
              },
              "required": [
                "source",
                "target"
              ]
            }
          }
        }
      },
      "File": {},
      "ImportForm": {
        "type": "object",
        "properties": {
          "file": {
            "type": "string",
            "format": "binary",
            "description": "The translation file."
          },
          "reference": {
            "type": "string",
            "format": "binary",
            "description": "Source-language file; required for android, messages-json and keyvalue-json."
          },
          "format": {
            "type": "string",
            "enum": [
              "json",
              "xliff",
              "xliff2",
              "po",
              "csv",
              "tmx",
              "android",
              "messages-json",
              "keyvalue-json"
            ],
            "description": "Omit to detect from the file name and content."
          },
          "lang": {
            "type": "string",
            "description": "Target language for single-language files that do not state one."
          },
          "status": {
            "type": "string",
            "enum": [
              "machine",
              "reviewed",
              "manual"
            ],
            "description": "Overrides the status of every entry. Default: the file\u0027s own status, else manual."
          },
          "dryRun": {
            "type": "boolean",
            "description": "Report what would happen and write nothing."
          }
        },
        "required": [
          "file"
        ]
      },
      "ImportResult": {
        "type": "object",
        "properties": {
          "formatId": {
            "type": "string"
          },
          "dryRun": {
            "type": "boolean"
          },
          "applied": {
            "type": "boolean"
          },
          "planned": {
            "type": "integer"
          },
          "written": {
            "type": "integer"
          },
          "skippedProtected": {
            "type": "integer",
            "description": "Machine entries not written because a reviewed or manual translation exists."
          },
          "stats": {
            "type": "object",
            "properties": {
              "unitsRead": {
                "type": "integer"
              },
              "matched": {
                "type": "integer"
              },
              "skippedUntranslated": {
                "type": "integer"
              },
              "skippedUnknown": {
                "type": "integer"
              },
              "skippedInvalid": {
                "type": "integer"
              },
              "conflicts": {
                "type": "integer"
              }
            }
          },
          "issues": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ImportIssue"
            }
          }
        },
        "required": [
          "formatId",
          "dryRun",
          "applied",
          "planned",
          "written",
          "stats",
          "issues"
        ]
      },
      "Usage": {
        "type": "object",
        "properties": {
          "siteId": {
            "type": "string",
            "format": "uuid"
          },
          "from": {
            "type": "string",
            "format": "date"
          },
          "to": {
            "type": "string",
            "format": "date"
          },
          "strings": {
            "type": "integer"
          },
          "characters": {
            "type": "integer"
          },
          "engineCostUsd": {
            "type": "number"
          }
        },
        "required": [
          "siteId",
          "from",
          "to",
          "strings",
          "characters",
          "engineCostUsd"
        ]
      },
      "Quota": {
        "type": "object",
        "properties": {
          "planId": {
            "type": "string"
          },
          "periodStart": {
            "type": "string",
            "format": "date"
          },
          "periodEnd": {
            "type": "string",
            "format": "date"
          },
          "unlimited": {
            "type": "boolean"
          },
          "usedCharacters": {
            "type": "integer"
          },
          "reservedCharacters": {
            "type": "integer"
          },
          "allowanceCharacters": {
            "type": "integer"
          },
          "usedWords": {
            "type": "integer"
          },
          "allowanceWords": {
            "type": "integer"
          },
          "remainingWords": {
            "type": "integer"
          },
          "fraction": {
            "type": "number"
          },
          "state": {
            "type": "string",
            "enum": [
              "ok",
              "soft",
              "exhausted"
            ]
          },
          "byoCharacters": {
            "type": "integer"
          },
          "stringsDiscovered": {
            "type": "integer"
          },
          "engineCostUsd": {
            "type": "number"
          }
        },
        "required": [
          "planId",
          "periodStart",
          "periodEnd",
          "unlimited",
          "usedCharacters",
          "state"
        ]
      },
      "Key": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "siteId": {
            "type": "string",
            "format": "uuid"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastUsedAt": {
            "type": "string",
            "format": "date-time"
          },
          "revokedAt": {
            "type": "string",
            "format": "date-time"
          },
          "graceUntil": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "prefix",
          "scopes"
        ]
      },
      "KeyWithSecret": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "siteId": {
            "type": "string",
            "format": "uuid"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastUsedAt": {
            "type": "string",
            "format": "date-time"
          },
          "revokedAt": {
            "type": "string",
            "format": "date-time"
          },
          "graceUntil": {
            "type": "string",
            "format": "date-time"
          },
          "secret": {
            "type": "string",
            "description": "Shown once, never again."
          }
        },
        "required": [
          "id",
          "name",
          "prefix",
          "scopes"
        ]
      },
      "KeyPage": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Key"
            }
          },
          "nextCursor": {
            "type": "string"
          }
        },
        "required": [
          "items"
        ]
      },
      "CreateKeyRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "sites:read",
                "sites:write",
                "strings:write",
                "translations:read",
                "translations:write",
                "bundles:publish",
                "usage:read",
                "keys:manage",
                "webhooks:manage",
                "audit:read",
                "audit:manage",
                "billing:read",
                "billing:manage"
              ]
            }
          },
          "siteId": {
            "type": "string",
            "format": "uuid"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "name",
          "scopes"
        ]
      },
      "RotateKeyRequest": {
        "type": "object",
        "properties": {
          "gracePeriodSeconds": {
            "type": "integer"
          }
        }
      },
      "DiscoverRequest": {
        "type": "object",
        "properties": {
          "siteKey": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StringItem"
            }
          }
        },
        "required": [
          "siteKey",
          "items"
        ]
      },
      "Accepted": {
        "type": "object",
        "properties": {
          "accepted": {
            "type": "integer"
          }
        },
        "required": [
          "accepted"
        ]
      }
    }
  }
}
