{
  "openapi": "3.0.0",
  "info": {
    "title": "MultiLocale API",
    "version": "1.0.0",
    "description": "Version 1 uses the /api/v1 path prefix. Breaking changes receive a new major prefix; additive changes remain in v1. The unversioned /api prefix remains a compatibility alias. The deprecation policy documents how Deprecation and Sunset response headers announce an endpoint lifecycle."
  },
  "externalDocs": {
    "description": "API versioning and deprecation policy",
    "url": "https://www.multilocale.com/docs/api/versioning/"
  },
  "servers": [
    {
      "url": "https://api.multilocale.com"
    }
  ],
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Uniform JSON envelope returned for API errors.",
        "required": [
          "name",
          "message",
          "status"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Machine-readable error class.",
            "example": "ForbiddenError"
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation."
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code."
          },
          "details": {
            "type": "object",
            "additionalProperties": true,
            "description": "Optional structured context for the error."
          }
        }
      },
      "Phrase": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "example": "welcome_message"
          },
          "value": {
            "type": "string",
            "example": "Welcome"
          },
          "language": {
            "type": "string",
            "example": "en"
          },
          "projects": {
            "type": "array",
            "description": "Project names this phrase belongs to",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "PhrasePage": {
        "type": "object",
        "required": [
          "items",
          "nextCursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Phrase"
            }
          },
          "nextCursor": {
            "nullable": true,
            "type": "string",
            "description": "Opaque cursor for the next page, or null after the final page"
          }
        }
      },
      "Project": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "example": "website"
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "basic",
        "description": "HTTP Basic auth carrying only the API key secret: Authorization: Basic base64(<key secret>)."
      },
      "accessToken": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Operator session token issued by the MultiLocale dashboard: Authorization: Token <access token>."
      },
      "oauth2": {
        "type": "oauth2",
        "description": "Authrice authorization-code flow with PKCE for delegated access.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://app.multilocale.com/oauth/authorize",
            "tokenUrl": "https://api.authrice.com/api/oauth/token",
            "scopes": {
              "projects:read": "Read projects and their locales.",
              "projects:write": "Create, rename, and delete projects.",
              "phrases:read": "Read phrases and translations.",
              "phrases:write": "Create, update, and delete phrases.",
              "translations:write": "Machine-translate phrases using organization credits."
            }
          }
        }
      }
    }
  },
  "paths": {
    "/api/v1/phrases": {
      "get": {
        "tags": [
          "Phrases"
        ],
        "summary": "List phrases",
        "description": "Lists the phrases of your organization. A phrase is one `{key, value, language}` triple belonging to one or more projects. Filter with `project`, `key` and `language`. Requires the `phrases:read` scope. A key scoped to a project only ever sees that project's phrases. For stable traversal, set `pagination=cursor`; the response becomes `{items, nextCursor}` and is ordered by `_id`. Pass `nextCursor` back as `cursor` until it is null. Existing array responses and offset pagination remain available when cursor pagination is not requested.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          },
          {
            "oauth2": [
              "phrases:read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "project",
            "description": "Project name",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "key",
            "description": "Phrase key (URL-encoded)",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "language",
            "schema": {
              "type": "string",
              "example": "fr"
            }
          },
          {
            "in": "query",
            "name": "fields",
            "description": "Comma-separated projection of fields to return",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "pagination",
            "description": "Set to `cursor` for a stable page envelope",
            "schema": {
              "type": "string",
              "enum": [
                "cursor"
              ]
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "description": "Opaque `nextCursor` from the preceding cursor page",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "description": "Optional bounded page size (maximum 2001; defaults to 100 in cursor mode)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 2001
            }
          },
          {
            "in": "query",
            "name": "skip",
            "description": "Optional page offset (maximum 10000)",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 10000
            }
          },
          {
            "in": "query",
            "name": "sortField",
            "schema": {
              "type": "string",
              "enum": [
                "_id",
                "key",
                "language"
              ]
            }
          },
          {
            "in": "query",
            "name": "sortDirection",
            "schema": {
              "type": "string",
              "enum": [
                "ASC",
                "DESC"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of phrases, or `{items, nextCursor}` in cursor mode",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Phrase"
                      }
                    },
                    {
                      "$ref": "#/components/schemas/PhrasePage"
                    }
                  ]
                }
              }
            },
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `phrases:read` scope",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds until the request may be retried.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getPhrases"
      },
      "post": {
        "tags": [
          "Phrases"
        ],
        "summary": "Create or update phrases",
        "description": "Upserts one phrase or an array of phrases. Emits `phrase.created` for rows that did not exist and `phrase.updated` for rows that did — one event per row, not per request. Requires the `phrases:write` scope. Send `Idempotency-Key` to make retries safe for 24 hours.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          },
          {
            "oauth2": [
              "phrases:write"
            ]
          }
        ],
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "description": "Unique retry key retained for 24 hours (maximum 255 characters)",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/Phrase"
                  },
                  {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Phrase"
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The upserted phrase, or the array of upserted phrases",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Phrase"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Phrase"
                      }
                    }
                  ]
                }
              }
            },
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `phrases:write` scope",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency key is in flight or was reused with different input",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds until the request may be retried.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postPhrases"
      },
      "delete": {
        "tags": [
          "Phrases"
        ],
        "summary": "Delete phrases",
        "description": "Deletes every translation of one `key` in one `project` — all languages at once — and emits one `phrase.deleted` per deleted row. Requires the `phrases:write` scope. Send `Idempotency-Key` to make retries safe for 24 hours.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          },
          {
            "oauth2": [
              "phrases:write"
            ]
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "key",
            "required": true,
            "description": "Phrase key (URL-encoded)",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "description": "Unique retry key retained for 24 hours (maximum 255 characters)",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          },
          {
            "in": "query",
            "name": "project",
            "required": true,
            "description": "Project name",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The deleted phrases",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Phrase"
                  }
                }
              }
            },
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `phrases:write` scope",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Nothing matched",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency key is in flight or was reused with different input",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds until the request may be retried.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deletePhrases"
      }
    },
    "/api/v1/phrases/batch": {
      "post": {
        "tags": [
          "Phrases"
        ],
        "operationId": "batchUpsertPhrases",
        "summary": "Create or update a batch of phrases",
        "description": "Upserts an array of phrases in one authenticated request and returns the resulting array in input order. Each row emits its own created or updated webhook event. Requires the `phrases:write` scope. Send `Idempotency-Key` to make retries safe for 24 hours.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          },
          {
            "oauth2": [
              "phrases:write"
            ]
          }
        ],
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "description": "Unique retry key retained for 24 hours (maximum 255 characters)",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Phrase"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The upserted phrases in input order",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Phrase"
                  }
                }
              }
            },
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `phrases:write` scope",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency key is in flight or was reused with different input",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds until the request may be retried.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/phrases/{phraseId}": {
      "put": {
        "tags": [
          "Phrases"
        ],
        "summary": "Update a phrase",
        "description": "Replaces a phrase you own and emits `phrase.updated`. Answers 404 — not 403 — for a phrase belonging to another organization, so ids cannot be probed. Requires the `phrases:write` scope. Send `Idempotency-Key` to make retries safe for 24 hours.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          },
          {
            "oauth2": [
              "phrases:write"
            ]
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "phraseId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "description": "Unique retry key retained for 24 hours (maximum 255 characters)",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Phrase"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated phrase",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Phrase"
                }
              }
            },
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `phrases:write` scope",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such phrase in your organization",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency key is in flight or was reused with different input",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds until the request may be retried.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "putPhrasesByPhraseId"
      }
    },
    "/api/v1/projects": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "List projects",
        "description": "Lists the projects of your organization. Requires the `projects:read` scope. A key scoped to a project sees only that project.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          },
          {
            "oauth2": [
              "projects:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Array of projects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Project"
                  }
                }
              }
            },
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `projects:read` scope",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds until the request may be retried.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getProjects"
      },
      "post": {
        "tags": [
          "Projects"
        ],
        "summary": "Create a project",
        "description": "Creates a project in your organization and emits `project.created`. Requires the `projects:write` scope. Send `Idempotency-Key` to make retries safe for 24 hours.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          },
          {
            "oauth2": [
              "projects:write"
            ]
          }
        ],
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "description": "Unique retry key retained for 24 hours (maximum 255 characters)",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Project"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created project",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            },
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `projects:write` scope",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency key is in flight or was reused with different input",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds until the request may be retried.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postProjects"
      }
    },
    "/api/v1/projects/{projectId}": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Get a project by id",
        "description": "Answers 404 — not 403 — for a project belonging to another organization, so ids cannot be probed. Requires the `projects:read` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          },
          {
            "oauth2": [
              "projects:read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The project",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            },
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `projects:read` scope",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such project in your organization",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds until the request may be retried.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getProjectsByProjectId"
      },
      "put": {
        "tags": [
          "Projects"
        ],
        "summary": "Update a project",
        "description": "Updates a project you own and emits `project.updated`. Answers 404 — not 403 — for a project belonging to another organization. Requires the `projects:write` scope. Send `Idempotency-Key` to make retries safe for 24 hours.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          },
          {
            "oauth2": [
              "projects:write"
            ]
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "description": "Unique retry key retained for 24 hours (maximum 255 characters)",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Project"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated project",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            },
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `projects:write` scope",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such project in your organization",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency key is in flight or was reused with different input",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds until the request may be retried.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "putProjectsByProjectId"
      }
    },
    "/api/v1/projects/{projectName}": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Get a project by name",
        "description": "Project names are unique per organization, not globally, so the lookup is always scoped to your organization. Requires the `projects:read` scope.\n",
        "security": [
          {
            "apiKey": []
          },
          {
            "accessToken": []
          },
          {
            "oauth2": [
              "projects:read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "projectName",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The project",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            },
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "API key is missing the `projects:read` scope",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such project in your organization",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "API key rate limit exceeded",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds until the request may be retried.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getProjectsByProjectName"
      }
    },
    "/api/v1/sandbox/phrases": {
      "get": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Read sandbox phrases without authentication",
        "description": "Returns a fixed set of demonstration phrases in the same `{key, value, language}` shape as authenticated phrase reads. No account, credential, or key is required, so agents can evaluate the API before onboarding. Responses carry the standard `RateLimit` response headers and the anonymous pacing limit applies.\n",
        "security": [],
        "responses": {
          "200": {
            "description": "Demonstration phrases.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "_id": {
                        "type": "string"
                      },
                      "key": {
                        "type": "string"
                      },
                      "value": {
                        "type": "string"
                      },
                      "language": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "description": "Anonymous rate limit exceeded; retry after `Retry-After` seconds.",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds until the request may be retried.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getSandboxPhrases"
      }
    },
    "/api/v1/sandbox/translationjobs": {
      "post": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Start a demonstration asynchronous translation job",
        "description": "Demonstrates the API's asynchronous-job pattern without authentication. The response is `202 Accepted` with a `Location` header pointing at the job resource and a body carrying the job `_id` and `status`. Poll the `Location` URL until the status is `succeeded`, then read the translations from the job body. The job is deterministic fixture work that completes about two seconds after creation.\n",
        "security": [],
        "responses": {
          "202": {
            "description": "Job accepted; poll the `Location` URL for the result.",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              },
              "Location": {
                "description": "URL of the created job resource.",
                "schema": {
                  "type": "string",
                  "example": "/api/v1/sandbox/translationjobs/c2FuZGJveGpvYnwxNzU2NTAwMDAwMDAw"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "running",
                        "succeeded"
                      ]
                    },
                    "creationTime": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Anonymous rate limit exceeded; retry after `Retry-After` seconds.",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds until the request may be retried.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "postSandboxTranslationjobs"
      }
    },
    "/api/v1/sandbox/translationjobs/{jobId}": {
      "get": {
        "tags": [
          "Sandbox"
        ],
        "summary": "Poll a demonstration translation job",
        "description": "Reads the status of a demonstration job started at `POST /api/v1/sandbox/translationjobs`. While the job runs the status is `running`; afterwards it is `succeeded` and the body carries the translated phrases. No authentication is required.\n",
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "jobId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current job state.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "running",
                        "succeeded"
                      ]
                    },
                    "creationTime": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "completionTime": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "translations": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          },
                          "language": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            },
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Unknown job id.",
            "headers": {
              "RateLimit": {
                "description": "Current quota, remaining requests, and reset time.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "Quota policy and window applied to the API key.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getSandboxTranslationjobsByJobId"
      }
    }
  },
  "tags": []
}
