{
  "openapi": "3.1.0",
  "info": {
    "title": "HE Engine API",
    "description": "A self-hosted engine for computing on encrypted data. Clients keep their TFHE client key; the server evaluates on ciphertexts with the server key they upload. Every error is `{\"error\": {\"code\", \"message\"}}`.",
    "contact": {
      "name": "Yog-Sotho",
      "url": "https://github.com/Yog-Sotho/Homomorphic-Encryption-Service"
    },
    "license": {
      "name": "MIT",
      "identifier": "MIT"
    },
    "version": "0.5.0"
  },
  "servers": [
    {
      "url": "/api/v1"
    }
  ],
  "paths": {
    "/auth/exchange": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "`POST /api/v1/auth/exchange` — redeems a one-time login code (once).",
        "operationId": "exchange",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExchangeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Signed in",
            "headers": {
              "Set-Cookie": {
                "schema": {
                  "type": "string"
                },
                "description": "The `HttpOnly` refresh cookie (path `/api/v1/auth`)"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unknown, used or expired code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/auth/forgot-password": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "`POST /api/v1/auth/forgot-password`",
        "operationId": "forgot_password",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmailRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Sent if the account exists (the answer is the same either way)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "429": {
            "description": "Too many requests; see `Retry-After`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/auth/forgot-password/redirect": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "`GET /api/v1/auth/forgot-password/redirect?token=` — the same for reset links.",
        "operationId": "reset_link",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to the frontend page, token in the fragment. Consumes nothing."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/auth/github": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "Starts GitHub sign-in.",
        "operationId": "github_start",
        "responses": {
          "302": {
            "description": "Redirect to GitHub, with a state cookie"
          },
          "404": {
            "description": "GitHub sign-in is not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/auth/github/callback": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "Where GitHub returns the user after sign-in.",
        "operationId": "github_callback",
        "parameters": [
          {
            "name": "code",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "error",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to the frontend with `#code=` (exchange it at `/auth/exchange`) or `#error=`"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/auth/google": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "Starts Google sign-in.",
        "operationId": "google_start",
        "responses": {
          "302": {
            "description": "Redirect to Google, with a state cookie"
          },
          "404": {
            "description": "Google sign-in is not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/auth/google/callback": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "Where Google returns the user after sign-in.",
        "operationId": "google_callback",
        "parameters": [
          {
            "name": "code",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "error",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to the frontend with `#code=` (exchange it at `/auth/exchange`) or `#error=`"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/auth/login": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "`POST /api/v1/auth/login`",
        "operationId": "login",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Credentials"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Signed in",
            "headers": {
              "Set-Cookie": {
                "schema": {
                  "type": "string"
                },
                "description": "The `HttpOnly` refresh cookie (path `/api/v1/auth`)"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              }
            }
          },
          "401": {
            "description": "Wrong email or password, or the email is not verified",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "429": {
            "description": "Too many requests; see `Retry-After`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/auth/logout": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "`POST /api/v1/auth/logout` — ends this browser's session (its whole family).",
        "operationId": "logout",
        "responses": {
          "200": {
            "description": "Signed out; the cookie is cleared",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "refresh_cookie": []
          }
        ]
      }
    },
    "/auth/providers": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "`GET /api/v1/auth/providers` — which sign-in buttons the frontend should show.",
        "operationId": "providers",
        "responses": {
          "200": {
            "description": "Which OAuth providers are configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Providers"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/auth/refresh": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "`POST /api/v1/auth/refresh` — exchanges the refresh cookie for a new access\ntoken and a new refresh cookie.",
        "operationId": "refresh",
        "responses": {
          "200": {
            "description": "A new access token; the refresh cookie is rotated",
            "headers": {
              "Set-Cookie": {
                "schema": {
                  "type": "string"
                },
                "description": "The `HttpOnly` refresh cookie (path `/api/v1/auth`)"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              }
            }
          },
          "401": {
            "description": "No valid session (the cookie is cleared)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "refresh_cookie": []
          }
        ]
      }
    },
    "/auth/register": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "`POST /api/v1/auth/register`",
        "operationId": "register",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Credentials"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Verification email sent. The same answer is given when the address already has an account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid email or weak password",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "429": {
            "description": "Too many requests; see `Retry-After`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/auth/resend-verification": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "`POST /api/v1/auth/resend-verification`",
        "operationId": "resend_verification",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmailRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Sent if an unverified account exists (the answer is the same either way)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "429": {
            "description": "Too many requests; see `Retry-After`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/auth/reset-password": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "`POST /api/v1/auth/reset-password` — sets a new password (once per link) and\nsigns out every session.",
        "operationId": "reset_password",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResetPasswordRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Password set; every session is signed out",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or expired link, or weak password",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/auth/verify": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "`GET /api/v1/auth/verify?token=` — links in emails sent before verification\nmoved to the frontend. Redirects without consuming the token.",
        "operationId": "verify_link",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to the frontend page, token in the fragment. Consumes nothing."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      },
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "`POST /api/v1/auth/verify` — confirms the email address and signs the user in.",
        "operationId": "verify_email",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Email verified and signed in",
            "headers": {
              "Set-Cookie": {
                "schema": {
                  "type": "string"
                },
                "description": "The `HttpOnly` refresh cookie (path `/api/v1/auth`)"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or expired verification link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/compute/jobs": {
      "get": {
        "tags": [
          "jobs"
        ],
        "summary": "`GET /api/v1/compute/jobs?cursor=&limit=` — the caller's jobs, newest first.",
        "operationId": "list_jobs",
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "`next_cursor` of the previous page.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Jobs per page (default 20).",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32",
              "maximum": 100,
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The caller's jobs, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobPage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid cursor or limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "post": {
        "tags": [
          "jobs"
        ],
        "summary": "`POST /api/v1/compute/jobs` — queue `operation` on the encrypted inputs.",
        "description": "Honours an `Idempotency-Key` header: resending the same request with the same\nkey returns the original job (with `Idempotent-Replayed: true`) instead of\nqueuing and charging a second one.",
        "operationId": "submit_job",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "1-255 visible ASCII characters. A retry with the same key and body returns the original job instead of queuing (and charging) another.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateJobRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Queued (or, for a replayed idempotency key, the original job)",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "boolean"
                },
                "description": "`true` when this is the original job of a reused key"
              },
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "The job"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobSummary"
                }
              }
            }
          },
          "400": {
            "description": "Wrong number or shape of inputs, bad base64, a malformed proven list, a scalar out of range, an invalid program, or a bad Idempotency-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No such server key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "413": {
            "description": "Body too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "Unknown operation or type; proven inputs for a key without proof support or public key; or the Idempotency-Key was used for a different request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "Daily quota used up, or too many jobs waiting; see `Retry-After`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The queue is full; see `Retry-After`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/compute/jobs/batch": {
      "post": {
        "tags": [
          "jobs"
        ],
        "summary": "`POST /api/v1/compute/jobs/batch` — queue several jobs at once, all or none.",
        "description": "With an `Idempotency-Key`, job *i* is keyed `<key>/<i>`: resending the batch\nreturns the original jobs.",
        "operationId": "submit_batch",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "1-255 visible ASCII characters, for the whole batch. A retry with the same key and body returns the original jobs.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchJobsRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Every job queued (or, for a replayed idempotency key, the original jobs), in request order",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "boolean"
                },
                "description": "`true` when these are the original jobs of a reused key"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchJobsResponse"
                }
              }
            }
          },
          "400": {
            "description": "An empty or oversized batch, or any job that would be refused on its own (the message names it)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "A job names no server key of yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "413": {
            "description": "Body too large: all inputs together are limited like one job's",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "As for one job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "Not enough daily quota or active-job room for every job; nothing is queued",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The queue is full; see `Retry-After`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/compute/jobs/{id}": {
      "get": {
        "tags": [
          "jobs"
        ],
        "summary": "`GET /api/v1/compute/jobs/{id}`",
        "operationId": "get_job_status",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Job id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobSummary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No such job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "delete": {
        "tags": [
          "jobs"
        ],
        "summary": "`DELETE /api/v1/compute/jobs/{id}` — removes a job that is not running, with\nits stored ciphertexts.",
        "operationId": "delete_job",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Job id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted with its ciphertexts"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No such job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "409": {
            "description": "The job is running",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/compute/jobs/{id}/result": {
      "get": {
        "tags": [
          "jobs"
        ],
        "summary": "`GET /api/v1/compute/jobs/{id}/result` — an encrypted result as\n`application/octet-stream`: a `safe_serialize`d ciphertext of the type in\n`result_types[output]` (`FheBool` for comparisons), or, for a job with\n`compressed_results`, one `CompressedCiphertextList` of every result.",
        "operationId": "get_job_result",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Job id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "output",
            "in": "query",
            "description": "Which result (0-based; default 0), for a program with several outputs.\nA compressed job has one list holding them all: output 0.",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The encrypted result: a `safe_serialize`d ciphertext of the type `result_types[output]` names (e.g. `FheUint16`, or `FheBool` for comparisons), or a `CompressedCiphertextList` of every result if the job has `compressed_results`",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No such job, or no such output",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "409": {
            "description": "The job has not completed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/he/crs": {
      "get": {
        "tags": [
          "he"
        ],
        "summary": "The CRS proven inputs are made against, as `application/octet-stream`.",
        "operationId": "get_crs",
        "responses": {
          "200": {
            "description": "A `safe_serialize`d (compressed) TFHE-rs `CompactPkeCrs`; its SHA-256 is `proofs.crs_sha256` of `/he/params`",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "The quoted SHA-256"
              }
            },
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "304": {
            "description": "Unchanged since the `If-None-Match` ETag"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/he/params": {
      "get": {
        "tags": [
          "he"
        ],
        "summary": "What a client needs to produce compatible keys and inputs.",
        "operationId": "get_params",
        "responses": {
          "200": {
            "description": "Scheme, TFHE-rs version and limits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HeParams"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/health": {
      "get": {
        "tags": [
          "meta"
        ],
        "summary": "Liveness: the process is up and answering.",
        "operationId": "health",
        "responses": {
          "200": {
            "description": "The server is up",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/health/ready": {
      "get": {
        "tags": [
          "meta"
        ],
        "summary": "Readiness: the database answers, both stores accept new files and every\njob worker is running. Only `200` means the instance should get traffic.",
        "operationId": "ready",
        "responses": {
          "200": {
            "description": "Ready",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ready"
                }
              }
            }
          },
          "503": {
            "description": "Not ready; `checks` says what failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotReady"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {}
        ]
      }
    },
    "/keys": {
      "get": {
        "tags": [
          "keys"
        ],
        "summary": "`GET /api/v1/keys` — the caller's keys, newest first.",
        "operationId": "list_keys",
        "responses": {
          "200": {
            "description": "The caller's keys, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ServerKeyInfo"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "post": {
        "tags": [
          "keys"
        ],
        "summary": "`POST /api/v1/keys` — body is a `safe_serialize`d TFHE `CompressedServerKey`.",
        "operationId": "upload_key",
        "requestBody": {
          "description": "A `safe_serialize`d TFHE-rs `CompressedServerKey` for the parameters of `/he/params`",
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Registered",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "The new key"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServerKeyInfo"
                }
              }
            }
          },
          "400": {
            "description": "Not a compressed server key for these parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "The account already has its maximum number of keys",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "413": {
            "description": "Larger than `max_server_key_bytes`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/keys/{id}": {
      "delete": {
        "tags": [
          "keys"
        ],
        "summary": "`DELETE /api/v1/keys/{id}` — removes the key; its jobs stay but lose the reference.",
        "operationId": "delete_key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Key id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No such key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/keys/{id}/public-key": {
      "put": {
        "tags": [
          "keys"
        ],
        "summary": "`PUT /api/v1/keys/{id}/public-key` — body is a `safe_serialize`d TFHE\n`CompactPublicKey` made from the same client key as server key `id`.",
        "operationId": "upload_public_key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Key id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "A `safe_serialize`d TFHE-rs `CompactPublicKey` (at most `proofs.max_public_key_bytes`)",
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Stored; proven inputs can now be sent for this key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServerKeyInfo"
                }
              }
            }
          },
          "400": {
            "description": "Not a compact public key for these parameters",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No such key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "413": {
            "description": "Larger than `proofs.max_public_key_bytes`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "The server key was made without proof support",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/user/account": {
      "delete": {
        "tags": [
          "user"
        ],
        "summary": "`DELETE /api/v1/user/account` — deletes the account and everything it owns.",
        "operationId": "delete_account",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeleteAccountRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The account, its keys and jobs are deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Wrong password, or (without a password) no sign-in in the last 5 minutes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/user/me": {
      "get": {
        "tags": [
          "user"
        ],
        "summary": "`GET /api/v1/user/me` — the signed-in user, their sign-in methods and today's usage.",
        "operationId": "get_me",
        "responses": {
          "200": {
            "description": "The signed-in user",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MeResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid access token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/user/password": {
      "put": {
        "tags": [
          "user"
        ],
        "summary": "`PUT /api/v1/user/password` — changes the password and signs out every session.",
        "operationId": "change_password",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangePasswordRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Changed; every session is signed out",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "400": {
            "description": "Weak password, or the account has no password",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "401": {
            "description": "Current password is incorrect",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "409": {
            "description": "The password changed meanwhile",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "Arg": {
        "oneOf": [
          {
            "type": "object",
            "description": "The job's encrypted input with this index (0-based).",
            "required": [
              "input"
            ],
            "properties": {
              "input": {
                "type": "integer",
                "description": "The job's encrypted input with this index (0-based).",
                "minimum": 0
              }
            }
          },
          {
            "type": "object",
            "description": "The result of an earlier step (0-based).",
            "required": [
              "step"
            ],
            "properties": {
              "step": {
                "type": "integer",
                "description": "The result of an earlier step (0-based).",
                "minimum": 0
              }
            }
          },
          {
            "type": "object",
            "description": "A plaintext constant in decimal, e.g. `\"-3\"`. The server sees it.",
            "required": [
              "const"
            ],
            "properties": {
              "const": {
                "type": "string",
                "description": "A plaintext constant in decimal, e.g. `\"-3\"`. The server sees it."
              }
            }
          }
        ],
        "description": "An argument of a step."
      },
      "AuthResponse": {
        "type": "object",
        "description": "A signed-in session. The refresh token is not here: it is set as the\n`HttpOnly` cookie `heaas_refresh` (path `/api/v1/auth`).",
        "required": [
          "token",
          "expires_in",
          "user"
        ],
        "properties": {
          "expires_in": {
            "type": "integer",
            "format": "int64",
            "description": "Seconds until `token` expires."
          },
          "token": {
            "type": "string",
            "description": "Bearer token for API calls."
          },
          "user": {
            "$ref": "#/components/schemas/UserPublic"
          }
        }
      },
      "BatchJobsRequest": {
        "type": "object",
        "description": "`POST /api/v1/compute/jobs/batch`: several jobs, queued together or not at all.",
        "required": [
          "jobs"
        ],
        "properties": {
          "jobs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreateJobRequest"
            },
            "description": "1 to `max_batch_jobs` jobs, each as for `POST /api/v1/compute/jobs`."
          }
        }
      },
      "BatchJobsResponse": {
        "type": "object",
        "description": "The queued jobs of a batch, in request order.",
        "required": [
          "jobs"
        ],
        "properties": {
          "jobs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/JobSummary"
            }
          }
        }
      },
      "ChangePasswordRequest": {
        "type": "object",
        "required": [
          "current_password",
          "new_password"
        ],
        "properties": {
          "current_password": {
            "type": "string"
          },
          "new_password": {
            "type": "string"
          }
        }
      },
      "Checks": {
        "type": "object",
        "description": "Each check: `\"ok\"`, or why it failed.",
        "required": [
          "database",
          "key_store",
          "job_store",
          "workers"
        ],
        "properties": {
          "database": {
            "type": "string",
            "description": "The database answers a query.",
            "example": "ok"
          },
          "job_store": {
            "type": "string",
            "description": "Files can be created in the job store.",
            "example": "ok"
          },
          "key_store": {
            "type": "string",
            "description": "Files can be created in the key store.",
            "example": "ok"
          },
          "workers": {
            "type": "string",
            "description": "Running job workers out of those started, e.g. `2/2`.",
            "example": "2/2"
          }
        }
      },
      "CreateJobRequest": {
        "type": "object",
        "description": "`POST /api/v1/compute/jobs`: compute `operation` on encrypted inputs.\n\nThe inputs are either `inputs` (one ciphertext each) or `proven_inputs` (one\nzero-knowledge-proven list holding all of them), never both. Binary operations\ntake two inputs, or one input and a `scalar`; `sum` takes 2 to `max_inputs`.",
        "required": [
          "key_id",
          "operation"
        ],
        "properties": {
          "compress_results": {
            "type": "boolean",
            "description": "Return the results as one TFHE-rs `CompressedCiphertextList` (~2 KB)\ninstead of one ciphertext each. Needs a key that `supports_compression`."
          },
          "inputs": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Standard base64 of `safe_serialize`d ciphertexts of `integer_type` (e.g.\n`FheUint16`), encrypted with the client key that belongs to `key_id`."
          },
          "integer_type": {
            "$ref": "#/components/schemas/IntegerType",
            "description": "The inputs' type; `u16` if absent."
          },
          "key_id": {
            "type": "string",
            "description": "One of the caller's registered server keys."
          },
          "operation": {
            "$ref": "#/components/schemas/Operation"
          },
          "program": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Program",
                "description": "The steps to run, for `operation: \"program\"` (and only then)."
              },
              {
                "type": "null"
              }
            ]
          },
          "proven_inputs": {
            "type": [
              "string",
              "null"
            ],
            "description": "Standard base64 of one `safe_serialize`d `ProvenCompactCiphertextList`\nencrypted under the key's public key, proven against the server's CRS with\nmetadata `he-engine/proof/v1/<key_id>`."
          },
          "scalar": {
            "type": [
              "string",
              "null"
            ],
            "description": "A plaintext right-hand operand in decimal, e.g. `\"-7\"`. The server sees it."
          }
        }
      },
      "Credentials": {
        "type": "object",
        "required": [
          "email",
          "password"
        ],
        "properties": {
          "email": {
            "type": "string"
          },
          "password": {
            "type": "string"
          }
        }
      },
      "DailyUsage": {
        "type": "object",
        "required": [
          "count",
          "quota",
          "date"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "format": "int64"
          },
          "date": {
            "type": "string"
          },
          "quota": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "DeleteAccountRequest": {
        "type": "object",
        "required": [
          "password"
        ],
        "properties": {
          "password": {
            "type": "string"
          }
        }
      },
      "EmailRequest": {
        "type": "object",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string"
          }
        }
      },
      "ErrorBody": {
        "type": "object",
        "description": "Every error response: `{\"error\": {\"code\": \"...\", \"message\": \"...\"}}`.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorDetail"
          }
        }
      },
      "ErrorDetail": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable, machine-readable reason; one of [`error_codes`]. New codes may be\nadded, so clients should fall back on the HTTP status for unknown ones.",
            "example": "not_found"
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation, safe to show to the user."
          }
        }
      },
      "ExchangeRequest": {
        "type": "object",
        "required": [
          "code"
        ],
        "properties": {
          "code": {
            "type": "string"
          }
        }
      },
      "HeParams": {
        "type": "object",
        "description": "`GET /api/v1/he/params`: what a client needs to make compatible keys and inputs.",
        "required": [
          "scheme",
          "library",
          "tfhe_version",
          "config",
          "integer_type",
          "plaintext_modulus",
          "operations",
          "max_ciphertext_bytes",
          "max_server_key_bytes",
          "max_keys_per_user"
        ],
        "properties": {
          "config": {
            "type": "string",
            "example": "ConfigBuilder::default() + PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128"
          },
          "integer_type": {
            "type": "string",
            "description": "The TFHE-rs type of the default `integer_type` (`u16`).",
            "example": "FheUint16"
          },
          "integer_types": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IntegerType"
            },
            "description": "Every type a job can compute on (absent before HE Engine 0.5: `u16` only)."
          },
          "library": {
            "type": "string",
            "example": "TFHE-rs"
          },
          "max_batch_jobs": {
            "type": "integer",
            "description": "Most jobs in one batch (`POST /api/v1/compute/jobs/batch`); a batch's\ninputs together are limited like one job's.",
            "minimum": 0
          },
          "max_ciphertext_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "max_inputs": {
            "type": "integer",
            "description": "Most encrypted inputs one job takes (a `sum`).",
            "minimum": 0
          },
          "max_job_input_bytes": {
            "type": "integer",
            "format": "int64",
            "description": "Most input bytes one job takes, all inputs together.",
            "minimum": 0
          },
          "max_keys_per_user": {
            "type": "integer",
            "format": "int64"
          },
          "max_program_outputs": {
            "type": "integer",
            "description": "Most results one program returns.",
            "minimum": 0
          },
          "max_program_steps": {
            "type": "integer",
            "description": "Most steps in one program.",
            "minimum": 0
          },
          "max_server_key_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "max_table_entries": {
            "type": "integer",
            "description": "Most entries in one lookup table.",
            "minimum": 0
          },
          "operations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Operation"
            }
          },
          "plaintext_modulus": {
            "type": "integer",
            "format": "int64",
            "description": "The default type's plaintext modulus (2^16).",
            "minimum": 0
          },
          "proofs": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ProofParams",
                "description": "Absent on servers before HE Engine 0.5, which take no proven inputs."
              },
              {
                "type": "null"
              }
            ]
          },
          "scheme": {
            "type": "string",
            "example": "TFHE (CGGI)"
          },
          "tfhe_version": {
            "type": "string",
            "description": "Clients must use exactly this TFHE-rs release.",
            "example": "1.8.2"
          }
        }
      },
      "Health": {
        "type": "object",
        "required": [
          "status",
          "version"
        ],
        "properties": {
          "status": {
            "type": "string",
            "example": "ok"
          },
          "version": {
            "type": "string",
            "description": "The server's version."
          }
        }
      },
      "IntegerType": {
        "type": "string",
        "description": "The encrypted integer types jobs can compute on. Arithmetic wraps modulo\n2^bits; signed types use two's complement.",
        "enum": [
          "u8",
          "u16",
          "u32",
          "u64",
          "i8",
          "i16",
          "i32",
          "i64"
        ]
      },
      "JobPage": {
        "type": "object",
        "description": "One page of `GET /api/v1/compute/jobs`, newest first.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/JobSummary"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next (older) page; `None` on the last page."
          }
        }
      },
      "JobStatus": {
        "type": "string",
        "enum": [
          "pending",
          "processing",
          "completed",
          "failed"
        ]
      },
      "JobSummary": {
        "type": "object",
        "description": "A job as its owner sees it: metadata only, never ciphertexts.",
        "required": [
          "id",
          "status",
          "operation",
          "attempts",
          "created_at"
        ],
        "properties": {
          "attempts": {
            "type": "integer",
            "format": "int64",
            "description": "Attempts started so far (internal errors are retried)."
          },
          "compressed_results": {
            "type": "boolean",
            "description": "The results are one compressed list (`GET …/result` returns it whole)."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "error_message": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the job failed, if it did."
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "id": {
            "type": "string"
          },
          "input_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Total size of the submitted ciphertexts."
          },
          "input_count": {
            "type": "integer",
            "format": "int64",
            "description": "Number of encrypted inputs."
          },
          "integer_type": {
            "$ref": "#/components/schemas/IntegerType"
          },
          "key_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The server key the job is evaluated with; `None` once that key is deleted."
          },
          "operation": {
            "$ref": "#/components/schemas/Operation"
          },
          "program": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Program",
                "description": "The program, for `operation: \"program\"`."
              },
              {
                "type": "null"
              }
            ]
          },
          "proven": {
            "type": "boolean",
            "description": "The inputs were a zero-knowledge-proven list."
          },
          "result_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Size of the encrypted results together\n(`GET /api/v1/compute/jobs/{id}/result?output=N`)."
          },
          "result_type": {
            "$ref": "#/components/schemas/ValueType",
            "description": "The type to decrypt the (first) result as: `bool` for comparisons."
          },
          "result_types": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ValueType"
            },
            "description": "The type of every result, in order: one for operations, one per output\nfor programs. Empty from servers before HE Engine 0.6 (read `result_type`)."
          },
          "scalar": {
            "type": [
              "string",
              "null"
            ],
            "description": "The plaintext right-hand operand, if the job has one."
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "status": {
            "$ref": "#/components/schemas/JobStatus"
          }
        }
      },
      "MeResponse": {
        "type": "object",
        "required": [
          "id",
          "email",
          "email_verified",
          "has_password",
          "created_at",
          "oauth_providers",
          "daily_usage"
        ],
        "properties": {
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "daily_usage": {
            "$ref": "#/components/schemas/DailyUsage"
          },
          "email": {
            "type": "string"
          },
          "email_verified": {
            "type": "boolean"
          },
          "has_password": {
            "type": "boolean"
          },
          "id": {
            "type": "string"
          },
          "oauth_providers": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "MessageResponse": {
        "type": "object",
        "description": "A plain confirmation, e.g. after registration.",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string"
          }
        }
      },
      "NotReady": {
        "type": "object",
        "description": "The error envelope, plus what failed.",
        "required": [
          "error",
          "checks"
        ],
        "properties": {
          "checks": {
            "$ref": "#/components/schemas/Checks"
          },
          "error": {
            "$ref": "#/components/schemas/ErrorDetail"
          }
        }
      },
      "Operation": {
        "type": "string",
        "description": "Homomorphic operations. Arithmetic wraps modulo 2^bits of the input type;\ncomparisons return an encrypted boolean.\n\nEvery operation except [`Sum`](Operation::Sum) and\n[`Program`](Operation::Program) takes two operands: two ciphertexts, or one\nciphertext and a plaintext `scalar` as the right-hand side. `Sum` adds 2 to\n[`MAX_INPUTS`] ciphertexts. `Program` runs the job's\n[`Program`](crate::program::Program) on 1 to [`MAX_INPUTS`] ciphertexts.",
        "enum": [
          "add",
          "subtract",
          "multiply",
          "min",
          "max",
          "eq",
          "ne",
          "lt",
          "le",
          "gt",
          "ge",
          "sum",
          "program"
        ]
      },
      "Program": {
        "type": "object",
        "description": "Steps evaluated in order.",
        "required": [
          "steps"
        ],
        "properties": {
          "outputs": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "integer",
              "minimum": 0
            },
            "description": "The steps (0-based) whose values are the job's results, in order: 1 to\n`max_program_outputs` of them. Absent: the last step alone."
          },
          "steps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Step"
            }
          }
        }
      },
      "ProgramOp": {
        "type": "string",
        "description": "What a step computes. Arithmetic wraps modulo 2^bits of the program's type.",
        "enum": [
          "add",
          "subtract",
          "multiply",
          "divide",
          "remainder",
          "min",
          "max",
          "eq",
          "ne",
          "lt",
          "le",
          "gt",
          "ge",
          "and",
          "or",
          "xor",
          "not",
          "neg",
          "abs",
          "shl",
          "shr",
          "rotl",
          "rotr",
          "select",
          "lookup"
        ]
      },
      "ProofParams": {
        "type": "object",
        "description": "How to send zero-knowledge-proven inputs.",
        "required": [
          "crs_sha256",
          "max_bits",
          "max_list_bytes",
          "max_public_key_bytes",
          "metadata_prefix"
        ],
        "properties": {
          "crs_sha256": {
            "type": "string",
            "description": "Lower-case hex SHA-256 of the CRS served at `GET /api/v1/he/crs`."
          },
          "max_bits": {
            "type": "integer",
            "description": "Most plaintext bits one proven list may hold.",
            "minimum": 0
          },
          "max_list_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "max_public_key_bytes": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "metadata_prefix": {
            "type": "string",
            "description": "Proofs are made with metadata `<metadata_prefix><key id>`.",
            "example": "he-engine/proof/v1/"
          }
        }
      },
      "Providers": {
        "type": "object",
        "required": [
          "google",
          "github"
        ],
        "properties": {
          "github": {
            "type": "boolean"
          },
          "google": {
            "type": "boolean"
          }
        }
      },
      "Ready": {
        "type": "object",
        "required": [
          "status",
          "checks"
        ],
        "properties": {
          "checks": {
            "$ref": "#/components/schemas/Checks"
          },
          "status": {
            "type": "string",
            "example": "ready"
          }
        }
      },
      "ResetPasswordRequest": {
        "type": "object",
        "required": [
          "token",
          "new_password"
        ],
        "properties": {
          "new_password": {
            "type": "string"
          },
          "token": {
            "type": "string"
          }
        }
      },
      "ServerKeyInfo": {
        "type": "object",
        "description": "A registered (compressed) server key.",
        "required": [
          "id",
          "sha256",
          "size_bytes",
          "created_at"
        ],
        "properties": {
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "has_public_key": {
            "type": "boolean",
            "description": "The matching compact public key has been uploaded\n(`PUT /api/v1/keys/{id}/public-key`)."
          },
          "id": {
            "type": "string"
          },
          "sha256": {
            "type": "string",
            "description": "Lower-case hex SHA-256 of the uploaded bytes."
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64"
          },
          "supports_compression": {
            "type": "boolean",
            "description": "The key holds compression keys (it was made with the SDK's compressing\nconfiguration), so jobs can ask for `compress_results`."
          },
          "supports_proofs": {
            "type": "boolean",
            "description": "The key was made with the compact-public-key parameters, so it can take\nproven inputs once its public key is uploaded. Keys from before HE Engine\n0.5 cannot."
          }
        }
      },
      "Step": {
        "type": "object",
        "required": [
          "op",
          "args"
        ],
        "properties": {
          "args": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Arg"
            }
          },
          "op": {
            "$ref": "#/components/schemas/ProgramOp"
          },
          "table": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/TableEntry"
            },
            "description": "For `lookup` only: 1 to `max_table_entries` entries with distinct keys.\nThe server sees the table."
          }
        }
      },
      "TableEntry": {
        "type": "object",
        "description": "One entry of a lookup table: `key` maps to `value` (decimal constants).",
        "required": [
          "key",
          "value"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        }
      },
      "TokenRequest": {
        "type": "object",
        "required": [
          "token"
        ],
        "properties": {
          "token": {
            "type": "string"
          }
        }
      },
      "UserPublic": {
        "type": "object",
        "required": [
          "id",
          "email",
          "created_at"
        ],
        "properties": {
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "email": {
            "type": "string"
          },
          "id": {
            "type": "string"
          }
        }
      },
      "ValueType": {
        "type": "string",
        "description": "The type of a job's encrypted result: the input type, or `bool` for comparisons.",
        "enum": [
          "bool",
          "u8",
          "u16",
          "u32",
          "u64",
          "i8",
          "i16",
          "i32",
          "i64"
        ]
      }
    },
    "responses": {
      "CrossOrigin": {
        "description": "A browser request from an origin that is not allowed",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorBody"
            }
          }
        }
      },
      "Error": {
        "description": "Any other error, in the error envelope",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorBody"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests from this client or for this account",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds to wait before retrying"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorBody"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, invalid or expired access token",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorBody"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "The access token from sign-in or `/auth/refresh` (15 minutes by default)"
      },
      "refresh_cookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "heaas_refresh",
        "description": "`HttpOnly` cookie set at sign-in; only sent to `/api/v1/auth`"
      }
    }
  },
  "tags": [
    {
      "name": "meta",
      "description": "Service status"
    },
    {
      "name": "he",
      "description": "Encryption parameters"
    },
    {
      "name": "auth",
      "description": "Accounts and sessions. Browser calls must come from an allowed `Origin`."
    },
    {
      "name": "user",
      "description": "The signed-in account"
    },
    {
      "name": "keys",
      "description": "Your TFHE server keys"
    },
    {
      "name": "jobs",
      "description": "Homomorphic computations"
    }
  ]
}
