{
  "openapi": "3.1.0",
  "info": {
    "title": "Curaword Integration API",
    "version": "1.0.0",
    "summary": "Send documents for certified translation, pay, approve and collect, from your own software.",
    "description": "For integrators (legal-tech products sending documents for the offices using them) and for offices with their own systems.\n\n- Authenticate with `Authorization: Bearer cw_live_…` (a server-side secret).\n- Money is in the currency's smallest unit (`5000` = $50.00).\n- Every response carries `X-Request-Id`; quote it to us for any `5xx`.\n- Statuses are the eight in `Status`; treat an unknown one as \"wait\".\n\nRead the Integration Guide for the full story, webhook signatures included.",
    "contact": {
      "name": "Your Curaword contact"
    }
  },
  "servers": [
    {
      "url": "https://curaword.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Orders",
      "description": "Send a document, follow it, cancel it."
    },
    {
      "name": "Payment",
      "description": "Payment links, for one order or several."
    },
    {
      "name": "Approval",
      "description": "Preview, approve or reject."
    },
    {
      "name": "Delivery",
      "description": "The certified PDF and its certificate."
    },
    {
      "name": "Events",
      "description": "The event feed and period figures."
    },
    {
      "name": "Public",
      "description": "No key needed."
    }
  ],
  "paths": {
    "/v1/api/orders": {
      "post": {
        "operationId": "createOrder",
        "tags": [
          "Orders"
        ],
        "summary": "Send a document and get its price",
        "description": "Creates one order for one document and prices it. The order starts `awaiting_payment`; the quote stands for 7 days.\n\n**Idempotency.** Every create carries an `Idempotency-Key`. A retry with the same key and the same request returns the first order with `200` and `Idempotent-Replayed: true`; the same key with a different request is `422`. Keys are remembered for 24 hours.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CreateOrder"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created and priced.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "200": {
            "description": "A replay: this `Idempotency-Key` was already used for the same request (same file bytes and fields). Returns that order as it is now. No new order.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            },
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ApiOff"
          },
          "413": {
            "description": "The file is larger than 100 MiB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A field is wrong (`detail` says which), or the `Idempotency-Key` was used for a different request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many unpaid quotes open (200 for an office, 2000 for an integrator). Pay for or cancel some.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listOrders",
        "tags": [
          "Orders"
        ],
        "summary": "List your orders",
        "description": "Orders your system sent, newest first.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/Status"
            }
          },
          {
            "name": "external_reference",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "description": "Exactly as sent."
          },
          {
            "name": "case_reference",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "description": "Exactly as sent."
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/After"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderPage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ApiOff"
          },
          "400": {
            "description": "`after` is not a cursor of ours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A query value is invalid, e.g. `limit` outside 1–200 or an unknown `status`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/api/orders/{order_id}": {
      "get": {
        "operationId": "getOrder",
        "tags": [
          "Orders"
        ],
        "summary": "Read one order",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ApiOff"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Invalid"
          }
        }
      }
    },
    "/v1/api/orders/{order_id}/cancel": {
      "post": {
        "operationId": "cancelOrder",
        "tags": [
          "Orders"
        ],
        "summary": "Withdraw an unpaid order",
        "description": "Only while `awaiting_payment`. The document is deleted. A payment link the order is on is closed first, for every order on it.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderId"
          }
        ],
        "responses": {
          "200": {
            "description": "Now `cancelled`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ApiOff"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Already paid for (including a link paid a moment ago), or otherwise past cancelling.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/Invalid"
          },
          "502": {
            "$ref": "#/components/responses/ProviderDown"
          }
        }
      }
    },
    "/v1/api/orders/{order_id}/checkout": {
      "post": {
        "operationId": "createPaymentLink",
        "tags": [
          "Payment"
        ],
        "summary": "Get a payment link for one order",
        "description": "Returns a payment page; whoever opens it pays. Asking again returns the same link while it lives, with the first request's return addresses; shortly before it expires you get a new one. Being sent to `success_url` does not prove payment: watch `status` or the `order.processing` event.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentLinkRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLink"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ApiOff"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Not `awaiting_payment`; on a shared link that is still live; a link it was on has just been paid; or the total is under the provider's minimum charge.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A return address is not an acceptable `https` address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/ProviderDown"
          }
        }
      }
    },
    "/v1/api/checkouts": {
      "post": {
        "operationId": "createGroupPaymentLink",
        "tags": [
          "Payment"
        ],
        "summary": "Get one payment link for several orders",
        "description": "Give **either** `order_ids` **or** `case_reference`. Each order is a line on the payment page under its `external_reference`, and stays its own order afterwards. Asking again for the same set returns the same link; a different set closes the old links and makes a new one.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GroupPaymentLinkRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GroupPaymentLink"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ApiOff"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "An order is no longer `awaiting_payment`, or a link being closed was paid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Fewer than two or more than twenty orders, both or neither selector, or a bad return address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/ProviderDown"
          }
        }
      }
    },
    "/v1/api/orders/{order_id}/preview": {
      "get": {
        "operationId": "getPreview",
        "tags": [
          "Approval"
        ],
        "summary": "Download the watermarked preview",
        "description": "Available while `awaiting_approval`, `in_revision` and `completed`, until the files are deleted. Watermarked: for looking at, not for using.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderId"
          }
        ],
        "responses": {
          "200": {
            "description": "The PDF.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "contentMediaType": "application/pdf"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ApiOff"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "No preview yet, or the files have been deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/Invalid"
          }
        }
      }
    },
    "/v1/api/orders/{order_id}/approve": {
      "post": {
        "operationId": "approveOrder",
        "tags": [
          "Approval"
        ],
        "summary": "Approve the translation",
        "description": "When `awaiting_approval`. The order becomes `completed` and the certified document is released. **Cannot be undone.**",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderId"
          }
        ],
        "responses": {
          "200": {
            "description": "Now `completed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ApiOff"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Not `awaiting_approval`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/Invalid"
          },
          "500": {
            "description": "Our fault; read the order before retrying, and quote `X-Request-Id`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/api/orders/{order_id}/reject": {
      "post": {
        "operationId": "rejectOrder",
        "tags": [
          "Approval"
        ],
        "summary": "Reject the translation with a reason",
        "description": "When `awaiting_approval`. The order becomes `in_revision`. The translator either corrects it (`in_review`, then `awaiting_approval` again) or disagrees, and Curaword decides: the work stands (`completed`), it is corrected, or you are refunded (`refunded`).",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RejectRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Now `in_revision`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ApiOff"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Not `awaiting_approval`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "No reason, one longer than 2000 characters, or an order id that is not a UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/api/orders/{order_id}/rejections": {
      "get": {
        "operationId": "listRejections",
        "tags": [
          "Approval"
        ],
        "summary": "List an order's rejections and their outcome",
        "description": "Oldest first.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderId"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/After"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RejectionPage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ApiOff"
          },
          "400": {
            "description": "`after` is not a cursor of ours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "The order id is not a UUID, or `limit` is outside 1–200.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/api/orders/{order_id}/deliverable": {
      "get": {
        "operationId": "getDeliverable",
        "tags": [
          "Delivery"
        ],
        "summary": "Download the certified translation",
        "description": "When `completed`. A PDF whose last page is the Certificate of Translation. Kept for 30 days after the order ends.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderId"
          }
        ],
        "responses": {
          "200": {
            "description": "The PDF.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "contentMediaType": "application/pdf"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ApiOff"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Not `completed`, or the files have been deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/Invalid"
          }
        }
      }
    },
    "/v1/api/orders/{order_id}/certificate": {
      "get": {
        "operationId": "getCertificate",
        "tags": [
          "Delivery"
        ],
        "summary": "Read the certificate's record",
        "description": "When `completed`. Compare `sha256` with the file you downloaded.",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrderId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Certificate"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ApiOff"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Not `completed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/Invalid"
          }
        }
      }
    },
    "/v1/api/events": {
      "get": {
        "operationId": "listEvents",
        "tags": [
          "Events"
        ],
        "summary": "Read the event feed",
        "description": "Everything that happened to your orders, oldest first, in the shape a webhook delivers. An event appears here 30 seconds after it happens and is kept 30 days. While `more` is `true`, ask again at once with the new `cursor`.",
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "The `cursor` you were last given. Without it, you start from the beginning."
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Events"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ApiOff"
          },
          "400": {
            "description": "`after` is not a cursor of ours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`limit` is outside 1–200.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/api/report": {
      "get": {
        "operationId": "getReport",
        "tags": [
          "Events"
        ],
        "summary": "Figures for a period",
        "description": "Orders your system **created** in a period of up to 366 days that were paid and not refunded, and what you earned on them (from our ledger, so only completed orders count).",
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Inclusive.",
            "example": "2026-10-01T00:00:00Z"
          },
          {
            "name": "until",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Exclusive.",
            "example": "2026-11-01T00:00:00Z"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Report"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ApiOff"
          },
          "422": {
            "description": "A bad or reversed period, or one longer than a year.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/languages": {
      "get": {
        "operationId": "listLanguages",
        "tags": [
          "Public"
        ],
        "summary": "Language pairs we translate",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LanguagePair"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/verify/{code}": {
      "get": {
        "operationId": "verifyCertificate",
        "tags": [
          "Public"
        ],
        "summary": "Check a certificate by its printed code",
        "description": "For anyone holding the paper; no key needed; the code is not case-sensitive. Says what was certified, never the document itself.",
        "security": [],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "8F3A1C7D9E24"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Verification"
                }
              }
            }
          },
          "404": {
            "description": "No certificate with that code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "orderEvent": {
      "post": {
        "summary": "An order of yours entered a new status",
        "description": "Sent to each address you added on your Curaword page, when the order's public status changes. Usually within seconds; it can wait a few minutes while our engine is busy with a document. Answer any `2xx` within 10 seconds; otherwise we retry after 1, 5 and 30 minutes, 2, 6 and 24 hours, then give up (the event stays in the feed).\n\n**Verify** `Curaword-Signature: t=<unix>,v1=<hex>`: HMAC-SHA256 of `t + \".\" + raw body`, keyed with your signing secret; compare in constant time; refuse a `t` more than 5 minutes from your clock.",
        "parameters": [
          {
            "name": "Curaword-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "order.awaiting_approval"
          },
          {
            "name": "Curaword-Delivery",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Quote it to us about a delivery."
          },
          {
            "name": "Curaword-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "t=1790977211,v1=5257a869e7ec…"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Do your work after answering."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "cw_live_…",
        "description": "Your API key, from your Curaword page. Never in a query string, a browser or a mobile app."
      }
    },
    "parameters": {
      "OrderId": {
        "name": "order_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      },
      "After": {
        "name": "after",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "The `next` of the page before."
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255
        },
        "description": "Unique per order you intend to create; reuse it on a retry. A UUID is fine."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "No key, or a key that does not work (mistyped, stopped, or the account is switched off).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ApiOff": {
        "description": "The key is real but the API is not turned on for your account.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "No such order, or not one your system sent.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Invalid": {
        "description": "The order id is not a UUID.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ProviderDown": {
        "description": "Our payment provider could not be reached. Try again shortly.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Order": {
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The order's id. Use it in every later call.",
            "examples": [
              "614a3ca6-8505-4fbc-ae79-c6cc72a9ace4"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/Status",
            "description": "Where the order is. Treat a value you do not know as \"wait\"."
          },
          "external_reference": {
            "type": "string",
            "description": "Your own id for this document, as you sent it.",
            "examples": [
              "case-4711/passport"
            ]
          },
          "case_reference": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Your own id for the case, as you sent it.",
            "examples": [
              "case-4711"
            ]
          },
          "client_name": {
            "type": "string",
            "description": "Who the translation is for, as you sent it.",
            "examples": [
              "Ayşe Yılmaz"
            ]
          },
          "source_filename": {
            "type": "string",
            "description": "The file name you uploaded.",
            "examples": [
              "passport.pdf"
            ]
          },
          "source_language": {
            "type": "string",
            "description": "Language of the document (e.g. `tr`).",
            "examples": [
              "tr"
            ]
          },
          "target_language": {
            "type": "string",
            "description": "Language it is translated into (e.g. `en`).",
            "examples": [
              "en"
            ]
          },
          "page_count": {
            "type": "integer",
            "description": "Pages we counted. The price is per page.",
            "examples": [
              2
            ]
          },
          "unit_price_minor": {
            "type": "integer",
            "description": "Price of one page, in the currency's smallest unit.",
            "examples": [
              2500
            ]
          },
          "total_minor": {
            "type": "integer",
            "description": "What the order costs, in the currency's smallest unit (`5000` = $50.00).",
            "examples": [
              5000
            ]
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 code, e.g. `USD`.",
            "examples": [
              "USD"
            ]
          },
          "promotion_code": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Offices: the code the order was placed on. `null` on an integrator's order."
          },
          "metadata": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object",
            "description": "Your key–value pairs, as sent.",
            "examples": [
              {
                "branch": "Ankara"
              }
            ]
          },
          "office_reference": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Integrators: your id for the office, as sent with that office's first order. `null` on an office's own order.",
            "examples": [
              "office-17"
            ]
          },
          "office_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Integrators: the office's name, as sent with that office's first order.",
            "examples": [
              "Yılmaz Law Office"
            ]
          },
          "name_spellings": {
            "items": {
              "$ref": "#/components/schemas/NameSpelling"
            },
            "type": "array",
            "description": "How names are to be spelled in the translation, as sent."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When you sent it."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When it last changed."
          },
          "quote_expires_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "While `awaiting_payment`: when the quote lapses and the order is withdrawn."
          },
          "documents_expire_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Once the order has ended: when its files are deleted."
          },
          "documents_purged_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Set once its files have been deleted. Preview and deliverable answer 409 from then on."
          }
        },
        "type": "object",
        "required": [
          "id",
          "status",
          "external_reference",
          "case_reference",
          "client_name",
          "source_filename",
          "source_language",
          "target_language",
          "page_count",
          "unit_price_minor",
          "total_minor",
          "currency",
          "promotion_code",
          "metadata",
          "name_spellings",
          "created_at",
          "updated_at",
          "quote_expires_at",
          "documents_expire_at",
          "documents_purged_at"
        ]
      },
      "Status": {
        "type": "string",
        "enum": [
          "awaiting_payment",
          "processing",
          "in_review",
          "awaiting_approval",
          "in_revision",
          "completed",
          "refunded",
          "cancelled"
        ],
        "description": "| value | meaning |\n|---|---|\n| `awaiting_payment` | Priced; nothing has started. Get a payment link or cancel. |\n| `processing` | Paid; the translation is being prepared. |\n| `in_review` | The draft is ready and a sworn translator reviews it. |\n| `awaiting_approval` | Finished; fetch the preview, then approve or reject. |\n| `in_revision` | You rejected it; the translator is answering, or Curaword is deciding a disagreement. |\n| `completed` | Approved. Collect the deliverable and the certificate. |\n| `refunded` | We could not complete it, or a dispute was decided in your favour; the payment went back. |\n| `cancelled` | Withdrawn before payment (by you, or the quote lapsed). |"
      },
      "NameSpelling": {
        "properties": {
          "as_written": {
            "type": "string",
            "maxLength": 120,
            "minLength": 1,
            "description": "The name as it appears in the document."
          },
          "spell_as": {
            "type": "string",
            "maxLength": 120,
            "minLength": 1,
            "description": "How it is to appear in the translation."
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "as_written",
          "spell_as"
        ]
      },
      "CreateOrder": {
        "properties": {
          "source_language": {
            "type": "string",
            "maxLength": 16,
            "minLength": 2,
            "description": "Language of the document, e.g. `tr` (2–16 characters). Must be a pair listed by `GET /v1/languages`.",
            "examples": [
              "tr"
            ]
          },
          "target_language": {
            "type": "string",
            "maxLength": 16,
            "minLength": 2,
            "description": "Language to translate into, e.g. `en`.",
            "examples": [
              "en"
            ]
          },
          "client_name": {
            "type": "string",
            "maxLength": 120,
            "minLength": 1,
            "description": "Who the translation is for. Up to 120 characters.",
            "examples": [
              "Ayşe Yılmaz"
            ]
          },
          "external_reference": {
            "type": "string",
            "maxLength": 128,
            "minLength": 1,
            "description": "Your own id for this document, up to 128 characters. Need not be unique. Shown to the payer (first 80 characters) on a shared payment link.",
            "examples": [
              "case-4711/passport"
            ]
          },
          "promotion_code": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 64
              },
              {
                "type": "null"
              }
            ],
            "description": "**Offices only**, optional: one of your own open codes. An integrator that sends one is refused (422)."
          },
          "case_reference": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 128
              },
              {
                "type": "null"
              }
            ],
            "description": "Your own id for the case, up to 128 characters. Groups documents and lets one link pay for them.",
            "examples": [
              "case-4711"
            ]
          },
          "metadata": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 32768
              },
              {
                "type": "null"
              }
            ],
            "description": "A JSON object of strings, sent as text. Up to 20 keys; keys up to 40 characters, values up to 500.",
            "examples": [
              "{\"branch\":\"Ankara\"}"
            ]
          },
          "name_spellings": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 32768
              },
              {
                "type": "null"
              }
            ],
            "description": "A JSON list of `{\"as_written\", \"spell_as\"}` pairs, sent as text. Up to 20 pairs, 120 characters a side.",
            "examples": [
              "[{\"as_written\":\"Ayşe Yılmaz\",\"spell_as\":\"AYSE YILMAZ\"}]"
            ]
          },
          "office_reference": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 128
              },
              {
                "type": "null"
              }
            ],
            "description": "**Integrators: required.** Your id for the office the document is for. An office's own system must leave it out (422).",
            "examples": [
              "office-17"
            ]
          },
          "office_name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200
              },
              {
                "type": "null"
              }
            ],
            "description": "**Integrators: required on every order.** The office's name, up to 200 characters. The name sent with the office's first order is kept.",
            "examples": [
              "Yılmaz Law Office"
            ]
          },
          "office_email": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 320
              },
              {
                "type": "null"
              }
            ],
            "description": "Integrators, optional: the office's e-mail address, kept from the office's first order (where a non-address is refused with 422)."
          },
          "file": {
            "type": "string",
            "contentMediaType": "application/octet-stream",
            "description": "The document, up to 100 MiB: PDF, Word (`.docx`), or an image (`.png .jpg .jpeg .tif .tiff .bmp .webp`; an image is one page). The type is read from the file name's extension only, so send the real name."
          }
        },
        "type": "object",
        "required": [
          "source_language",
          "target_language",
          "client_name",
          "external_reference",
          "file"
        ]
      },
      "PaymentLinkRequest": {
        "properties": {
          "success_url": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 2000
              },
              {
                "type": "null"
              }
            ],
            "description": "Where the payer is sent after paying. `https` with a host, no credentials or spaces, up to 2000 characters. Arriving here does not prove payment."
          },
          "cancel_url": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 2000
              },
              {
                "type": "null"
              }
            ],
            "description": "Where the payer is sent if they leave without paying. `https`, up to 2000 characters."
          }
        },
        "type": "object"
      },
      "PaymentLink": {
        "properties": {
          "checkout_url": {
            "type": "string",
            "description": "The payment page. Whoever opens it pays."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the link stops working (about 24 hours)."
          }
        },
        "type": "object",
        "required": [
          "checkout_url",
          "expires_at"
        ]
      },
      "GroupPaymentLinkRequest": {
        "properties": {
          "order_ids": {
            "anyOf": [
              {
                "items": {
                  "type": "string",
                  "format": "uuid"
                },
                "type": "array",
                "maxItems": 100,
                "minItems": 2
              },
              {
                "type": "null"
              }
            ],
            "description": "Two to twenty orders, all `awaiting_payment`. Give this **or** `case_reference`.",
            "maxItems": 20
          },
          "case_reference": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 128,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "description": "Every order of the case that is `awaiting_payment` and has no live link of its own (2 to 20)."
          },
          "success_url": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 2000
              },
              {
                "type": "null"
              }
            ],
            "description": "As for a single order."
          },
          "cancel_url": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 2000
              },
              {
                "type": "null"
              }
            ],
            "description": "As for a single order."
          }
        },
        "type": "object"
      },
      "GroupPaymentLink": {
        "properties": {
          "checkout_url": {
            "type": "string",
            "description": "The payment page for all the orders listed."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the link stops working."
          },
          "total_minor": {
            "type": "integer",
            "description": "Sum of the orders on the link."
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 code."
          },
          "orders": {
            "items": {
              "$ref": "#/components/schemas/GroupPaymentLine"
            },
            "type": "array",
            "description": "Each order on the link, under its `external_reference`."
          }
        },
        "type": "object",
        "required": [
          "checkout_url",
          "expires_at",
          "total_minor",
          "currency",
          "orders"
        ]
      },
      "GroupPaymentLine": {
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Order id."
          },
          "external_reference": {
            "type": "string",
            "description": "Your reference; its first 80 characters are shown to the payer."
          },
          "total_minor": {
            "type": "integer",
            "description": "This order's total."
          }
        },
        "type": "object",
        "required": [
          "id",
          "external_reference",
          "total_minor"
        ]
      },
      "RejectRequest": {
        "properties": {
          "reason": {
            "type": "string",
            "maxLength": 2000,
            "minLength": 1,
            "description": "What is wrong and where. Goes to the translator as written. 1–2000 characters.",
            "examples": [
              "The surname is spelled Yilmaz in the passport, not Yılmaz."
            ]
          }
        },
        "type": "object",
        "required": [
          "reason"
        ]
      },
      "Certificate": {
        "properties": {
          "version": {
            "type": "integer",
            "description": "Goes up each time the translation is delivered again after a rejection; each version has its own code."
          },
          "verification_code": {
            "type": "string",
            "description": "Printed on the certificate. Anyone can check it at `GET /v1/verify/{code}`.",
            "examples": [
              "8F3A1C7D9E24"
            ]
          },
          "sha256": {
            "type": "string",
            "description": "Digest of the deliverable PDF. A download that does not match is not the certified document."
          },
          "page_count": {
            "type": "integer",
            "description": "Translated pages, not counting the certificate page."
          },
          "certificate_name": {
            "type": "string",
            "description": "The translator who signed it.",
            "examples": [
              "Mehmet Demir, sworn translator (TR–EN)"
            ]
          },
          "issued_on": {
            "type": "string",
            "format": "date-time",
            "description": "When it was issued."
          }
        },
        "type": "object",
        "required": [
          "version",
          "verification_code",
          "sha256",
          "page_count",
          "certificate_name",
          "issued_on"
        ]
      },
      "Rejection": {
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The rejection's id."
          },
          "order_id": {
            "type": "string",
            "format": "uuid",
            "description": "The order rejected."
          },
          "reason": {
            "type": "string",
            "description": "Your reason, as sent."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When you rejected."
          },
          "accepted_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Set if the translator accepted the reason and is correcting the work."
          },
          "contested_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "Set if the translator disagreed; Curaword then decides."
          },
          "contest_note": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The translator's answer, when contested."
          },
          "resolved_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "description": "When Curaword decided a contested rejection."
          },
          "resolution": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Resolution"
              },
              {
                "type": "null"
              }
            ],
            "description": "Curaword's decision on a contested rejection."
          },
          "resolution_note": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Curaword's note on its decision."
          }
        },
        "type": "object",
        "required": [
          "id",
          "order_id",
          "reason",
          "created_at",
          "accepted_at",
          "contested_at",
          "contest_note",
          "resolved_at",
          "resolution",
          "resolution_note"
        ]
      },
      "Resolution": {
        "type": "string",
        "enum": [
          "approved_work",
          "returned_for_correction",
          "refunded_customer"
        ],
        "description": "`approved_work`: the work stands, the order is `completed`. `returned_for_correction`: it is corrected and comes back for approval. `refunded_customer`: the payment is returned, the order is `refunded`."
      },
      "Event": {
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The event's id; the same id a webhook carries. Use it to ignore duplicates."
          },
          "type": {
            "type": "string",
            "description": "`order.<status>` — see the webhook `orderEvent`."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When it happened."
          },
          "payload": {
            "additionalProperties": true,
            "type": "object",
            "description": "Exactly the body a webhook delivers for this event."
          }
        },
        "type": "object",
        "required": [
          "id",
          "type",
          "created_at",
          "payload"
        ]
      },
      "Events": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/Event"
            },
            "type": "array",
            "description": "Events, oldest first."
          },
          "cursor": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Keep it and send it as `after` next time. `null` only before your first event."
          },
          "more": {
            "type": "boolean",
            "description": "`true`: ask again at once with the new cursor. `false`: you are up to date."
          }
        },
        "type": "object",
        "required": [
          "items",
          "cursor",
          "more"
        ]
      },
      "Report": {
        "properties": {
          "since": {
            "type": "string",
            "format": "date-time",
            "description": "Start of the period, inclusive."
          },
          "until": {
            "type": "string",
            "format": "date-time",
            "description": "End of the period, exclusive."
          },
          "orders": {
            "type": "integer",
            "description": "Orders created in the period, leaving out unpaid, cancelled and refunded ones."
          },
          "pages": {
            "type": "integer",
            "description": "Their pages."
          },
          "total_minor": {
            "type": "integer",
            "description": "What they cost."
          },
          "earned_minor": {
            "type": "integer",
            "description": "What you earned on the ones completed, from our ledger."
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 code."
          }
        },
        "type": "object",
        "required": [
          "since",
          "until",
          "orders",
          "pages",
          "total_minor",
          "earned_minor",
          "currency"
        ]
      },
      "Verification": {
        "properties": {
          "order_reference": {
            "type": "string",
            "format": "uuid",
            "description": "The order the certificate belongs to."
          },
          "source_language": {
            "type": "string",
            "description": "Language of the original."
          },
          "target_language": {
            "type": "string",
            "description": "Language of the translation."
          },
          "page_count": {
            "type": "integer",
            "description": "Translated pages, not counting the certificate page."
          },
          "certificate_name": {
            "type": "string",
            "description": "The translator who signed it."
          },
          "issued_on": {
            "type": "string",
            "format": "date-time",
            "description": "When it was issued."
          },
          "sha256": {
            "type": "string",
            "description": "Digest of the certified PDF; compare it with the file in hand."
          }
        },
        "type": "object",
        "required": [
          "order_reference",
          "source_language",
          "target_language",
          "page_count",
          "certificate_name",
          "issued_on",
          "sha256"
        ]
      },
      "LanguagePair": {
        "properties": {
          "source_language": {
            "type": "string",
            "description": "From."
          },
          "target_language": {
            "type": "string",
            "description": "Into."
          }
        },
        "type": "object",
        "required": [
          "source_language",
          "target_language"
        ]
      },
      "OrderPage": {
        "type": "object",
        "required": [
          "items",
          "next"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Order"
            }
          },
          "next": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `after` to read the next page. `null` on the last page."
          }
        }
      },
      "RejectionPage": {
        "type": "object",
        "required": [
          "items",
          "next"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Rejection"
            }
          },
          "next": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `after` to read the next page. `null` on the last page."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "detail"
        ],
        "properties": {
          "detail": {
            "description": "A sentence, or for a field that failed validation a list naming the field.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "object"
                }
              }
            ]
          }
        },
        "examples": [
          {
            "detail": "only an order that is awaiting approval can be approved"
          }
        ]
      },
      "WebhookEvent": {
        "type": "object",
        "required": [
          "id",
          "type",
          "created_at",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Event id, the same in the feed. The same event may arrive twice; ignore an id you have handled."
          },
          "type": {
            "type": "string",
            "enum": [
              "order.awaiting_payment",
              "order.processing",
              "order.in_review",
              "order.awaiting_approval",
              "order.in_revision",
              "order.completed",
              "order.refunded",
              "order.cancelled",
              "ping"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "properties": {
              "order": {
                "$ref": "#/components/schemas/Order"
              }
            },
            "description": "`order` is the order as `GET /v1/api/orders/{id}` returned it at that moment. Empty for `ping`."
          }
        }
      }
    }
  }
}
