{
 "openapi": "3.1.0",
 "info": {
  "title": "Jepela API",
  "version": "2026-10-15",
  "summary": "A hosted decision API with memory.",
  "description": "Jepela is a hosted decision API with memory. Send a state (text, a JSON object or a list of turns) and typed questions: choice (pick one option), score (a level on an ordered scale) or noul (yes or no). Every answer comes back with probabilities and a confidence; nothing is generated. Name a subject and its memory is read first: facts, rules checked in code, pinned lines and options removed in code, and `compare` shows what the memory changed.\n\nPrepaid: $0.042 per million input tokens (what the engine read: the state, your questions and options, and recalled memory lines; `usage.input_tokens` is its own count). Output is free, and so are memory writes. Prices exclude VAT. When the credit is used up, priced requests answer 402. For business customers.\n\nPOST bodies are `application/json`, at most 1 MB (16 MB for /v1/batches and /v1/golden); a duplicate JSON key is refused, and a Content-Length that is not a number, or JSON nested more than 100 levels deep, is a 400. Errors are JSON: {\"error\": {\"type\", \"message\"}}. Retry 429 after Retry-After, and 502 or 503 with backoff; a request that failed is not charged.",
  "termsOfService": "https://jepela.com/legal/terms.html",
  "contact": {
   "name": "Jepela",
   "email": "info@jepela.com",
   "url": "https://jepela.com"
  }
 },
 "externalDocs": {
  "description": "Jepela documentation",
  "url": "https://jepela.com/docs/index.html"
 },
 "servers": [
  {
   "url": "https://api.jepela.com",
   "description": "The United States region (us; also https://us.api.jepela.com). Keys belong to a region: another region's key is answered 421 wrong_region."
  }
 ],
 "security": [
  {
   "bearer": []
  }
 ],
 "tags": [
  {
   "name": "Decisions"
  },
  {
   "name": "Memory",
   "description": "Lines and rules per subject."
  },
  {
   "name": "Quality",
   "description": "Labels, calibration, golden sets, deleting records."
  },
  {
   "name": "Fine-tuning"
  },
  {
   "name": "Batches"
  },
  {
   "name": "Account"
  },
  {
   "name": "Service",
   "description": "No key needed."
  },
  {
   "name": "Pages",
   "description": "No key needed."
  }
 ],
 "paths": {
  "/v1/systemone": {
   "post": {
    "operationId": "systemOne",
    "tags": [
     "Decisions"
    ],
    "summary": "Answer typed questions about a state",
    "description": "Any mix of choice, score and noul questions about one state. With a `subject`, its memory and rules are read first. Billed per input token the engine read (`usage.input_tokens`).",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/DecisionRequest"
       },
       "examples": {
        "three_questions": {
         "summary": "A choice, a noul and a score about one message",
         "description": "Recorded on 2026-09-23 from a Jepela gateway (docs, Examples: \"Three questions in one request\"). The answer is shortened as on that page (no decision id, reading report, engine metadata, timings or most usage counters); its cost used that day's placeholder prices, not today's price.",
         "value": {
          "state": "Hi, you charged me twice for September. Please send the second payment back.",
          "questions": {
           "route": {
            "type": "choice",
            "instructions": "Which team should handle this?",
            "criteria": {
             "billing": "Charges, invoices and refunds",
             "support": "Product problems",
             "sales": "New licences and upgrades",
             "other": "None of these"
            }
           },
           "refund": {
            "type": "noul",
            "instructions": "Is the customer asking for money back?"
           },
           "urgency": {
            "type": "score",
            "instructions": "How urgent is this?",
            "criteria": [
             "Can wait a week",
             "Should be handled today",
             "Blocking the customer now"
            ]
           }
          },
          "model": "jepela-english"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "The answers.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/DecisionResponse"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "402": {
      "$ref": "#/components/responses/402"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     },
     "502": {
      "$ref": "#/components/responses/502"
     },
     "503": {
      "$ref": "#/components/responses/503"
     }
    }
   }
  },
  "/v1/memory/remember": {
   "post": {
    "operationId": "memoryRemember",
    "tags": [
     "Memory"
    ],
    "summary": "Add lines to a subject's memory",
    "description": "One fact per line, at most 2,000 characters a line (a longer one refuses the whole write: 422); a subject holds at most 10,000 lines (409, nothing stored). Free; lines are billed as input when a decision reads them. when, pin, exclude, question, expires or ttl_seconds (or a name close to one) with a value are refused (422, nothing stored or charged): a remembered line is recalled by its words, always and until forgotten; send such a line to /v1/memory/rules. Any other field this route does not read is named in an unknown_field warning.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "subject": {
          "$ref": "#/components/schemas/SubjectId"
         },
         "text": {
          "type": "string",
          "description": "One fact per line.",
          "minLength": 1
         }
        },
        "required": [
         "subject",
         "text"
        ]
       },
       "examples": {
        "facts": {
         "value": {
          "subject": "acme",
          "text": "ACME Logistics holds an enterprise contract.\nRefunds for ACME go to the billing team within one business day."
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "subject": {
           "type": "string"
          },
          "lines_added": {
           "type": "integer"
          },
          "lines_stored": {
           "type": "integer"
          },
          "warnings": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Warning"
           }
          },
          "usage": {
           "$ref": "#/components/schemas/Charge"
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "402": {
      "$ref": "#/components/responses/402"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "409": {
      "$ref": "#/components/responses/409"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     },
     "502": {
      "$ref": "#/components/responses/502"
     },
     "503": {
      "$ref": "#/components/responses/503"
     }
    }
   }
  },
  "/v1/memory/forget": {
   "post": {
    "operationId": "memoryForget",
    "tags": [
     "Memory"
    ],
    "summary": "Forget a concept and prove it gone",
    "description": "Removes every line, and every rule text, holding all the concept's content words, then checks recall. A word written in lowercase matches with case ignored, also as a part of a hyphenated word that holds a letter (dairy in dairy-free); a word written with a capital letter is a name and matches only a word with a capital (\"Per\" leaves \"per month\"), also in its possessive and genitive forms (Ann's, Åsas) and as a part of a hyphenated name (Smith in Smith-Jones), with the apostrophes inside it, however typed (O'Brien is OʼBrien, not Brien). A line or rule matched where the case does not show a name (the first word of a sentence, a list item or a table cell, capitals, a title-case heading, or a name of several parts in another case), or only with other accents or none, or with a letter such as ø, æ or ł written without it (\"José\" for \"Jose\", \"Søren\" for \"Soren\", both ways), is removed and listed in matched_unsure; lines and rules kept that may be about a name are listed in related. With preview true nothing is removed: the answer says what would go, and it is not billed. Never refused for lack of credit, and a suspended account may call it, like every route that removes data (forget, memory delete, rules delete, alias delete, golden delete, decisions delete, fine-tuned model delete).",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "subject": {
          "$ref": "#/components/schemas/SubjectId"
         },
         "concept": {
          "type": "string",
          "minLength": 1
         },
         "preview": {
          "type": "boolean",
          "default": false,
          "description": "true: remove nothing and answer what forget would remove (removed_lines, removed_rules, related, matched_unsure), with no usage and no verified_forgotten. Not billed, no ledger line."
         }
        },
        "required": [
         "subject",
         "concept"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "subject": {
           "type": "string"
          },
          "preview": {
           "type": "boolean",
           "description": "Present and true when the request was a preview: nothing was removed."
          },
          "lines_removed": {
           "type": "integer"
          },
          "removed_lines": {
           "type": "array",
           "items": {
            "type": "string"
           },
           "description": "The text of each line removed."
          },
          "rules_removed": {
           "type": "integer"
          },
          "removed_rules": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "id": {
              "type": "string"
             },
             "text": {
              "type": "string"
             }
            }
           },
           "description": "Each rule removed."
          },
          "matched_unsure": {
           "type": "object",
           "properties": {
            "lines": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "rules": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "id": {
                "type": "string"
               },
               "text": {
                "type": "string"
               }
              }
             }
            }
           },
           "description": "The removed lines and rules that hold a name of the concept only where the case does not show a name: the first word of a sentence, a list item or a table cell (\"Will you call?\" for \"Will\"), a word or a heading in capitals or title case (\"THE ORDER WILL SHIP\"), or a name of several parts in another case (\"ann lindqvist\" for \"Ann Lindqvist\"); or a word of the concept matched only with other accents or none (\"José\" for \"Jose\", \"Jose\" for \"José\"): removed, and listed to be checked."
          },
          "related": {
           "type": "object",
           "properties": {
            "lines": {
             "type": "array",
             "items": {
              "type": "string"
             }
            },
            "rules": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "id": {
                "type": "string"
               },
               "text": {
                "type": "string"
               }
              }
             }
            }
           },
           "description": "The lines and rules kept that may be about a name in the concept: they hold the concept with a name written in another case (\"Billing is per month.\" for \"Per\"), or a part of a name of two or more parts (\"Ann prefers email.\" for \"Ann Lindqvist\", \"Smith\" for \"Smith-Jones\"): not removed, listed so you can decide. They do not change verified_forgotten."
          },
          "verified_forgotten": {
           "type": "boolean",
           "description": "Recall returns no line or rule that still holds them, and nothing went that the concept does not name. Not in a preview."
          },
          "leaks": {
           "type": "array"
          },
          "usage": {
           "$ref": "#/components/schemas/Charge",
           "description": "Not in a preview."
          },
          "warnings": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Warning"
           },
           "description": "unknown_field for each field of the body that this route does not read; the request was carried out without it."
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     },
     "502": {
      "$ref": "#/components/responses/502"
     },
     "503": {
      "$ref": "#/components/responses/503"
     }
    }
   }
  },
  "/v1/memory/delete": {
   "post": {
    "operationId": "memoryDelete",
    "tags": [
     "Memory"
    ],
    "summary": "Delete a subject's whole memory",
    "description": "Lines and rules, in RAM and on disk, and the subject's requests and results in your batches. Immediate. Reads only subject: any other field with a value is refused (422) and nothing is deleted, since it asks for less than everything. To remove one fact use /v1/memory/forget, one rule /v1/memory/rules/delete. Never refused for lack of credit, and a suspended account may call it, like every route that removes data (forget, memory delete, rules delete, alias delete, golden delete, decisions delete, fine-tuned model delete). A query string is refused (422), and so is a field asking it only to look (preview, dry_run, simulate and the like): nothing is deleted.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "subject": {
          "$ref": "#/components/schemas/SubjectId"
         }
        },
        "required": [
         "subject"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "subject": {
           "type": "string"
          },
          "lines_removed": {
           "type": "integer",
           "description": "Lines that were in RAM."
          },
          "batch_requests_deleted": {
           "type": "integer"
          },
          "usage": {
           "$ref": "#/components/schemas/Charge"
          },
          "warnings": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Warning"
           },
           "description": "unknown_field for each field of the body that this route does not read; the request was carried out without it."
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     },
     "502": {
      "$ref": "#/components/responses/502"
     },
     "503": {
      "$ref": "#/components/responses/503"
     }
    }
   }
  },
  "/v1/memory": {
   "get": {
    "operationId": "memoryGet",
    "tags": [
     "Memory"
    ],
    "summary": "Read a subject's memory",
    "parameters": [
     {
      "name": "subject",
      "in": "query",
      "required": true,
      "schema": {
       "$ref": "#/components/schemas/SubjectId"
      }
     },
     {
      "name": "lines",
      "in": "query",
      "description": "true: every line and rule, word for word.",
      "schema": {
       "type": "boolean",
       "default": false
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "subject": {
           "type": "string"
          },
          "lines_stored": {
           "type": [
            "integer",
            "null"
           ]
          },
          "rules": {
           "type": "integer"
          },
          "aliases": {
           "type": "array",
           "items": {
            "type": "string"
           }
          },
          "lines": {
           "type": "array",
           "description": "With lines=true, word for word.",
           "items": {
            "type": "string"
           }
          },
          "rule_list": {
           "type": "array",
           "description": "With lines=true.",
           "items": {
            "$ref": "#/components/schemas/Rule"
           }
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     },
     "502": {
      "$ref": "#/components/responses/502"
     },
     "503": {
      "$ref": "#/components/responses/503"
     }
    }
   }
  },
  "/v1/memory/rules": {
   "post": {
    "operationId": "rulesAdd",
    "tags": [
     "Memory"
    ],
    "summary": "Add a rule",
    "description": "`when` is checked in code on the request's `values` and the state's fields (a bare name is read from `values` first; `values.` and `state.` name one of them); the text reaches the engine only when it holds. `pin` sends it first at every decision; `exclude` removes options in code. At most 100 rules per subject. Billed as one memory line written.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "subject": {
          "$ref": "#/components/schemas/SubjectId"
         },
         "when": {
          "type": "string",
          "description": "Optional with pin or exclude (then always).",
          "maxLength": 300
         },
         "text": {
          "type": "string",
          "description": "An exclusion rule's text is never sent to the engine.",
          "minLength": 1,
          "maxLength": 500
         },
         "pin": {
          "type": "boolean"
         },
         "exclude": {
          "type": "array",
          "description": "Option keys to remove from choice questions.",
          "items": {
           "type": "string"
          },
          "minItems": 1,
          "maxItems": 20
         },
         "question": {
          "type": "string",
          "description": "Limit an exclusion to one question id. Only with exclude: a rule with question and no exclude is refused (422)."
         },
         "expires": {
          "type": "string",
          "description": "An ISO date or time."
         },
         "ttl_seconds": {
          "type": "integer",
          "description": "At most ten years."
         }
        },
        "required": [
         "subject",
         "text"
        ]
       },
       "examples": {
        "condition": {
         "value": {
          "subject": "mia",
          "when": "session_result <= -30",
          "text": "Mia's stop-loss is reached: she stops playing now."
         }
        },
        "exclusion": {
         "value": {
          "subject": "player-9",
          "text": "Gold coins are off for this player (coin bug).",
          "exclude": [
           "gold_coins"
          ],
          "question": "reward"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "subject": {
           "type": "string"
          },
          "rule": {
           "$ref": "#/components/schemas/Rule"
          },
          "rules": {
           "type": "integer",
           "description": "Rules the subject has."
          },
          "usage": {
           "$ref": "#/components/schemas/Charge"
          },
          "warnings": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Warning"
           },
           "description": "unknown_field for each field of the body that this route does not read; the request was carried out without it."
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "402": {
      "$ref": "#/components/responses/402"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "409": {
      "$ref": "#/components/responses/409"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     },
     "502": {
      "$ref": "#/components/responses/502"
     },
     "503": {
      "$ref": "#/components/responses/503"
     }
    }
   },
   "get": {
    "operationId": "rulesList",
    "tags": [
     "Memory"
    ],
    "summary": "List a subject's rules",
    "parameters": [
     {
      "name": "subject",
      "in": "query",
      "required": true,
      "schema": {
       "$ref": "#/components/schemas/SubjectId"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "subject": {
           "type": "string"
          },
          "rules": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Rule"
           }
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     },
     "502": {
      "$ref": "#/components/responses/502"
     },
     "503": {
      "$ref": "#/components/responses/503"
     }
    }
   }
  },
  "/v1/memory/rules/delete": {
   "post": {
    "operationId": "rulesDelete",
    "tags": [
     "Memory"
    ],
    "summary": "Delete a rule",
    "description": "Never refused for lack of credit, and a suspended account may call it, like every route that removes data (forget, memory delete, rules delete, alias delete, golden delete, decisions delete, fine-tuned model delete). A query string is refused (422), and so is a field asking it only to look (preview, dry_run, simulate and the like): nothing is deleted.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "subject": {
          "$ref": "#/components/schemas/SubjectId"
         },
         "rule_id": {
          "type": "string"
         }
        },
        "required": [
         "subject",
         "rule_id"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "subject": {
           "type": "string"
          },
          "deleted": {},
          "rules": {
           "type": "integer"
          },
          "warnings": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Warning"
           },
           "description": "unknown_field for each field of the body that this route does not read; the request was carried out without it."
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     },
     "502": {
      "$ref": "#/components/responses/502"
     },
     "503": {
      "$ref": "#/components/responses/503"
     }
    }
   }
  },
  "/v1/memory/aliases": {
   "post": {
    "operationId": "aliasesSet",
    "tags": [
     "Memory"
    ],
    "summary": "Set the other names a subject goes by",
    "description": "Recall searches them with the state; an empty list clears them.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "subject": {
          "$ref": "#/components/schemas/SubjectId"
         },
         "aliases": {
          "type": "array",
          "maxItems": 20,
          "items": {
           "type": "string",
           "minLength": 1,
           "maxLength": 80
          }
         }
        },
        "required": [
         "subject",
         "aliases"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "subject": {
           "type": "string"
          },
          "aliases": {
           "type": "array",
           "items": {
            "type": "string"
           }
          },
          "searched_as": {
           "type": "array",
           "description": "Words recall adds to the state.",
           "items": {
            "type": "string"
           }
          },
          "warnings": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Warning"
           },
           "description": "unknown_field for each field of the body that this route does not read; the request was carried out without it."
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     },
     "502": {
      "$ref": "#/components/responses/502"
     },
     "503": {
      "$ref": "#/components/responses/503"
     }
    }
   }
  },
  "/v1/memory/aliases/delete": {
   "post": {
    "operationId": "aliasesDelete",
    "tags": [
     "Memory"
    ],
    "summary": "Take names out of the other names a subject goes by",
    "description": "Removes the names given from the subject's aliases, matched in any case, and adds none. Not billed. Never refused for lack of credit, and a suspended account may call it, like every route that removes data (forget, memory delete, rules delete, alias delete, golden delete, decisions delete, fine-tuned model delete). Without aliases nothing is removed (422). A query string is refused (422), and so is a field asking it only to look (preview, dry_run, simulate and the like): nothing is removed.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "subject": {
          "$ref": "#/components/schemas/SubjectId"
         },
         "aliases": {
          "type": "array",
          "maxItems": 20,
          "description": "The names to take out; an empty list removes nothing.",
          "items": {
           "type": "string",
           "minLength": 1,
           "maxLength": 80
          }
         }
        },
        "required": [
         "subject",
         "aliases"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "subject": {
           "type": "string"
          },
          "removed": {
           "type": "array",
           "items": {
            "type": "string"
           },
           "description": "The aliases that went, as they were stored."
          },
          "not_found": {
           "type": "array",
           "items": {
            "type": "string"
           },
           "description": "The names asked for that the subject did not hold."
          },
          "aliases": {
           "type": "array",
           "items": {
            "type": "string"
           },
           "description": "The aliases left."
          },
          "searched_as": {
           "type": "array",
           "items": {
            "type": "string"
           },
           "description": "Words recall adds to the state, from the names left."
          },
          "warnings": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Warning"
           },
           "description": "unknown_field for each field of the body that this route does not read; the request was carried out without it."
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     },
     "502": {
      "$ref": "#/components/responses/502"
     },
     "503": {
      "$ref": "#/components/responses/503"
     }
    }
   }
  },
  "/v1/feedback": {
   "post": {
    "operationId": "feedback",
    "tags": [
     "Quality"
    ],
    "summary": "Send the right answer for a decision",
    "description": "Labels feed the quality report and the calibration fitted on them (refit at 50, 100, 200 ... labels). Free. Decisions can be labelled for 30 days.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "decision_id": {
          "type": "string",
          "pattern": "^dec_[0-9a-f]{24}$"
         },
         "question": {
          "type": "string"
         },
         "correct": {
          "description": "An option (choice), a level from 0 (score), or true/false or \"yes\"/\"no\" (noul)."
         }
        },
        "required": [
         "decision_id",
         "question",
         "correct"
        ]
       },
       "examples": {
        "label": {
         "value": {
          "decision_id": "dec_4b637aef332bc18f111236e0",
          "question": "route",
          "correct": "billing"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "decision_id": {
           "type": "string"
          },
          "question": {
           "type": "string"
          },
          "label": {},
          "correct": {
           "type": "boolean",
           "description": "Whether Jepela was right."
          },
          "labels_for_this_question": {
           "type": "integer"
          },
          "calibration_refit": {
           "type": "array"
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "404": {
      "$ref": "#/components/responses/404"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/quality": {
   "get": {
    "operationId": "qualityReport",
    "tags": [
     "Quality"
    ],
    "summary": "How often Jepela is right on your labels",
    "description": "Per question and model: labels, accuracy, calibration_error, brier, log_loss, bands, and automate_at (per target accuracy: a threshold chosen on your labels with a margin; status, whether thresholds chosen that way reached the target on labels they were not chosen on; the share of those labels' decisions above them, their accuracy and its 95% lower bound; below 50 labels, not enough labels).",
    "parameters": [
     {
      "name": "question",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "days",
      "in": "query",
      "schema": {
       "type": "number",
       "default": 30
      }
     },
     {
      "name": "model",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "source",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "feedback",
        "golden"
       ],
       "default": "feedback"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/calibration/fit": {
   "post": {
    "operationId": "calibrationFit",
    "tags": [
     "Quality"
    ],
    "summary": "Fit calibration on your labels now",
    "description": "Per question, and per group with the same model, type and number of options: a temperature (and a shift for yes/no), kept only when it lowers the calibration error without raising the log loss on held-out labels.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "model": {
          "type": "string"
         },
         "question": {
          "type": "string"
         },
         "source": {
          "type": "string",
          "enum": [
           "feedback",
           "golden"
          ],
          "default": "feedback"
         },
         "min_labels": {
          "type": "integer",
          "minimum": 20,
          "default": 50
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "results": {
           "type": "array"
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/calibration": {
   "get": {
    "operationId": "calibrationList",
    "tags": [
     "Quality"
    ],
    "summary": "The temperatures in use",
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "temperatures": {}
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/golden": {
   "post": {
    "operationId": "goldenAdd",
    "tags": [
     "Quality"
    ],
    "summary": "Add golden cases",
    "description": "Cases with known answers to re-run before a change; at most 5,000 per account, body up to 16 MB.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "cases": {
          "type": "array",
          "minItems": 1,
          "items": {
           "$ref": "#/components/schemas/GoldenCase"
          }
         }
        },
        "required": [
         "cases"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "added": {
           "type": "integer"
          },
          "cases": {
           "type": "integer",
           "description": "Cases the account has now."
          },
          "warnings": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Warning"
           },
           "description": "unknown_field, with case, for each field a case is not kept with (customer is kept as subject)."
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "409": {
      "$ref": "#/components/responses/409"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   },
   "get": {
    "operationId": "goldenList",
    "tags": [
     "Quality"
    ],
    "summary": "List golden cases and runs",
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "count": {
           "type": "integer"
          },
          "cases": {
           "type": "array",
           "description": "Ids, questions, expected.",
           "items": {
            "type": "object"
           }
          },
          "runs": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "run_id": {},
             "model": {
              "type": "string"
             },
             "created": {}
            }
           }
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/golden/run": {
   "post": {
    "operationId": "goldenRun",
    "tags": [
     "Quality"
    ],
    "summary": "Run your golden set",
    "description": "Every case with the run's model: accuracy per question and every answer changed since that model's last run. Billed like decisions, in one request (use a long timeout). A case that fails or is not run (no credit) is left out of the accuracy, not billed, and listed in `failed` with the reason.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "model": {
          "type": "string",
          "default": "jepela"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "run_id": {},
          "cases": {
           "type": "integer"
          },
          "accuracy": {},
          "questions": {},
          "changed_since_last_run": {
           "description": "Null on a model's first run."
          },
          "failed": {
           "type": "array",
           "description": "Each case that failed or was not run, with the reason and what to change.",
           "items": {
            "type": "object",
            "properties": {
             "case": {
              "type": "string"
             },
             "error": {
              "type": "string"
             }
            }
           }
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "402": {
      "$ref": "#/components/responses/402"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/golden/delete": {
   "post": {
    "operationId": "goldenDelete",
    "tags": [
     "Quality"
    ],
    "summary": "Delete golden cases",
    "description": "{\"ids\": [...]} deletes those, {} every case, an empty list nothing. Models trained on a deleted case are listed. Without ids, any other field with a value is refused (422) and nothing is deleted. Never refused for lack of credit, and a suspended account may call it, like every route that removes data (forget, memory delete, rules delete, alias delete, golden delete, decisions delete, fine-tuned model delete). A query string is refused (422), and so is a field asking it only to look (preview, dry_run, simulate and the like): nothing is deleted.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "ids": {
          "type": "array",
          "items": {
           "type": "string"
          }
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "deleted": {
           "type": "integer"
          },
          "models_trained_on_deleted_cases": {
           "type": "array",
           "items": {
            "type": "string"
           }
          },
          "warnings": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Warning"
           },
           "description": "unknown_field for each field of the body that this route does not read; the request was carried out without it."
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/finetune": {
   "post": {
    "operationId": "finetuneStart",
    "tags": [
     "Fine-tuning"
    ],
    "summary": "Train a model of your own on your golden cases",
    "description": "At least 20 cases with expected answers; one in five is held out. The model is used only if it beats the standard engine on them and still follows memory as well. One job at a time. Free during the preview.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "base": {
          "type": "string",
          "enum": [
           "english",
           "multilingual",
           "typed-decisions"
          ],
          "default": "english"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/FinetuneJob"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "402": {
      "$ref": "#/components/responses/402"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "409": {
      "$ref": "#/components/responses/409"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   },
   "get": {
    "operationId": "finetuneList",
    "tags": [
     "Fine-tuning"
    ],
    "summary": "List your fine-tuning jobs",
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "jobs": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/FinetuneJob"
           }
          },
          "min_cases": {
           "type": "integer"
          },
          "bases": {
           "type": "array",
           "items": {
            "type": "string"
           }
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/finetune/{job}": {
   "get": {
    "operationId": "finetuneGet",
    "tags": [
     "Fine-tuning"
    ],
    "summary": "One fine-tuning job",
    "parameters": [
     {
      "name": "job",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^ft[0-9a-f]{12}$"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/FinetuneJob"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "404": {
      "$ref": "#/components/responses/404"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/finetune/delete": {
   "post": {
    "operationId": "finetuneDelete",
    "tags": [
     "Fine-tuning"
    ],
    "summary": "Delete a fine-tuned model",
    "description": "Its files go and its name stops working. Never refused for lack of credit, and a suspended account may call it, like every route that removes data (forget, memory delete, rules delete, alias delete, golden delete, decisions delete, fine-tuned model delete). A query string is refused (422), and so is a field asking it only to look (preview, dry_run, simulate and the like): nothing is deleted.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "model": {
          "type": "string",
          "examples": [
           "jepela-ft-acme-1a2b3c4d"
          ]
         }
        },
        "required": [
         "model"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "deleted": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "404": {
      "$ref": "#/components/responses/404"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/decisions/delete": {
   "post": {
    "operationId": "decisionsDelete",
    "tags": [
     "Quality"
    ],
    "summary": "Delete every decision record, label and batch",
    "description": "Records (ids, question hashes, probabilities; never the state or question text) are deleted after 30 days anyway. A running batch stops. The answers the cache keeps for you go too. It reads no field: one with a value is refused (422) and nothing is deleted. Never refused for lack of credit, and a suspended account may call it, like every route that removes data (forget, memory delete, rules delete, alias delete, golden delete, decisions delete, fine-tuned model delete). A query string is refused (422), and so is a field asking it only to look (preview, dry_run, simulate and the like): nothing is deleted.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "description": "Send {}."
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "decisions_deleted": {
           "type": "integer"
          },
          "labels_deleted": {
           "type": "integer"
          },
          "batches_deleted": {
           "type": "integer"
          },
          "warnings": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Warning"
           },
           "description": "unknown_field for each field of the body that this route does not read; the request was carried out without it."
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/batches": {
   "post": {
    "operationId": "batchCreate",
    "tags": [
     "Batches"
    ],
    "summary": "Start a batch of decisions",
    "description": "Up to 5,000 /v1/systemone bodies as JSON or JSON Lines (16 MB). `custom_id` is unique, 1 to 64 of letters, digits and . _ : - (default: the position from 0). A bad request fails alone. Each is billed like a single call; kept 30 days.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "requests": {
          "type": "array",
          "minItems": 1,
          "maxItems": 5000,
          "items": {
           "allOf": [
            {
             "$ref": "#/components/schemas/DecisionRequest"
            }
           ],
           "properties": {
            "custom_id": {
             "type": "string",
             "pattern": "^[A-Za-z0-9_.:-]{1,64}$"
            }
           }
          }
         }
        },
        "required": [
         "requests"
        ]
       }
      },
      "application/x-ndjson": {
       "schema": {
        "type": "string",
        "description": "One /v1/systemone body per line, with an optional custom_id."
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/BatchJob"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "402": {
      "$ref": "#/components/responses/402"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   },
   "get": {
    "operationId": "batchList",
    "tags": [
     "Batches"
    ],
    "summary": "List your batches",
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "batches": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/BatchJob"
           }
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/batches/{id}": {
   "get": {
    "operationId": "batchGet",
    "tags": [
     "Batches"
    ],
    "summary": "One batch",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^batch_[0-9a-f]{16}$"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/BatchJob"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "404": {
      "$ref": "#/components/responses/404"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/batches/{id}/results": {
   "get": {
    "operationId": "batchResults",
    "tags": [
     "Batches"
    ],
    "summary": "A batch's results",
    "description": "JSON Lines in the order sent, one BatchResultLine each.",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^batch_[0-9a-f]{16}$"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/x-ndjson": {
        "schema": {
         "type": "string"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "404": {
      "$ref": "#/components/responses/404"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/models": {
   "get": {
    "operationId": "modelsList",
    "tags": [
     "Account"
    ],
    "summary": "The models you can use",
    "description": "Whether each answers now, and your own fine-tuned models.",
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "models": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Model"
           }
          },
          "default": {
           "type": "string"
          },
          "aliases": {
           "type": "object",
           "additionalProperties": {
            "type": "string"
           }
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/usage": {
   "get": {
    "operationId": "usageGet",
    "tags": [
     "Account"
    ],
    "summary": "Credit, spending and the ledger",
    "description": "The totals and the balance always agree.",
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "tenant": {
           "type": "string"
          },
          "currency": {
           "type": "string"
          },
          "credited": {
           "type": "number"
          },
          "spent": {
           "type": "number"
          },
          "balance": {
           "type": "number"
          },
          "requests": {
           "type": "integer"
          },
          "rate_limit_per_minute": {
           "type": "integer"
          },
          "prices": {
           "type": "object"
          },
          "totals": {
           "type": "object",
           "properties": {
            "requests": {
             "type": "integer"
            },
            "answers": {
             "type": "integer"
            },
            "input_characters": {
             "type": "integer"
            },
            "input_tokens": {
             "type": "integer"
            },
            "memory_lines_written": {
             "type": "integer"
            },
            "cost": {
             "type": "number"
            }
           }
          },
          "recent": {
           "type": "array",
           "description": "The last 20 ledger lines.",
           "items": {
            "type": "object"
           }
          },
          "keys": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "key_id": {
              "type": "string"
             },
             "tenant": {
              "type": "string"
             },
             "name": {
              "type": [
               "string",
               "null"
              ]
             },
             "created": {
              "type": "string"
             },
             "revoked": {
              "type": [
               "string",
               "null"
              ]
             }
            }
           }
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/401"
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/v1/signup": {
   "post": {
    "operationId": "signup",
    "tags": [
     "Service"
    ],
    "summary": "Get a key by email",
    "description": "No key needed. Off unless turned on (403 signup_disabled); may need an invite code. 3 sign-ups per address a day by default; the new key gets 60 requests a minute. Accounts are normally made at https://app.jepela.com.",
    "security": [],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "email": {
          "type": "string",
          "format": "email"
         },
         "invite_code": {
          "type": "string"
         },
         "region": {
          "type": "string",
          "description": "Another region's sign-up is answered 421.",
          "default": "us"
         }
        },
        "required": [
         "email"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "tenant": {
           "type": "string"
          },
          "key": {
           "type": "string",
           "description": "Shown once."
          },
          "key_id": {
           "type": "string"
          },
          "region": {
           "type": "string"
          },
          "api": {
           "type": "string",
           "format": "uri"
          },
          "credit": {
           "type": "number"
          },
          "currency": {
           "type": "string"
          },
          "note": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "403": {
      "$ref": "#/components/responses/403"
     },
     "413": {
      "$ref": "#/components/responses/413"
     },
     "415": {
      "$ref": "#/components/responses/415"
     },
     "421": {
      "$ref": "#/components/responses/421"
     },
     "422": {
      "$ref": "#/components/responses/422"
     },
     "429": {
      "$ref": "#/components/responses/429"
     },
     "500": {
      "$ref": "#/components/responses/500"
     }
    }
   }
  },
  "/health": {
   "get": {
    "operationId": "health",
    "tags": [
     "Service"
    ],
    "summary": "Whether the API, the memory service and the engine are up",
    "description": "No key, not billed. Also the region and the engine's code hash; nothing about accounts or subjects.",
    "security": [],
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "status": {
           "type": "string"
          },
          "service": {
           "type": "string"
          },
          "region": {
           "type": "string"
          },
          "uptime_seconds": {
           "type": "integer"
          },
          "served": {
           "type": "integer"
          },
          "product": {
           "type": "object",
           "description": "The memory service: reachable, status, engines."
          },
          "engines": {
           "type": "object",
           "description": "Per engine: reachable, status, code."
          },
          "currency": {
           "type": "string"
          },
          "signup": {
           "type": "boolean",
           "description": "Self-serve sign-up is on."
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/v1/regions": {
   "get": {
    "operationId": "regions",
    "tags": [
     "Service"
    ],
    "summary": "The regions and their API addresses",
    "description": "No key needed.",
    "security": [],
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "this_region": {
           "type": "string"
          },
          "primary": {
           "type": "string"
          },
          "regions": {
           "type": "object",
           "additionalProperties": {
            "$ref": "#/components/schemas/Region"
           }
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/": {
   "get": {
    "operationId": "home",
    "tags": [
     "Pages"
    ],
    "summary": "A short how-to",
    "security": [],
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "text/html": {
        "schema": {
         "type": "string"
        }
       }
      }
     }
    }
   }
  },
  "/playground": {
   "get": {
    "operationId": "playground",
    "tags": [
     "Pages"
    ],
    "summary": "Try requests in the browser",
    "security": [],
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "text/html": {
        "schema": {
         "type": "string"
        }
       }
      }
     }
    }
   }
  },
  "/docs": {
   "get": {
    "operationId": "docsIndex",
    "tags": [
     "Pages"
    ],
    "summary": "The documentation's index",
    "security": [],
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "text/html": {
        "schema": {
         "type": "string"
        }
       }
      }
     }
    }
   }
  },
  "/docs/{page}": {
   "get": {
    "operationId": "docsPage",
    "tags": [
     "Pages"
    ],
    "summary": "A documentation page as Markdown",
    "security": [],
    "parameters": [
     {
      "name": "page",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^[a-z0-9-]+\\.md$",
       "examples": [
        "api.md"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "text/markdown": {
        "schema": {
         "type": "string"
        }
       }
      }
     },
     "404": {
      "$ref": "#/components/responses/404"
     }
    }
   }
  },
  "/llms.txt": {
   "get": {
    "operationId": "llmsTxt",
    "tags": [
     "Pages"
    ],
    "summary": "A guide for AI assistants",
    "security": [],
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "text/plain": {
        "schema": {
         "type": "string"
        }
       }
      }
     }
    }
   }
  },
  "/robots.txt": {
   "get": {
    "operationId": "robotsTxt",
    "tags": [
     "Pages"
    ],
    "summary": "Crawler rules",
    "security": [],
    "responses": {
     "200": {
      "description": "OK.",
      "content": {
       "text/plain": {
        "schema": {
         "type": "string"
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "securitySchemes": {
   "bearer": {
    "type": "http",
    "scheme": "bearer",
    "bearerFormat": "jj_live_<region>_<32 hex>",
    "description": "`Authorization: Bearer jj_live_us_...`. A key is shown once, when made. Older keys without a region (jj_live_ and 32 hex) are us keys."
   }
  },
  "schemas": {
   "ErrorBody": {
    "type": "object",
    "properties": {
     "type": {
      "type": "string",
      "enum": [
       "invalid_request_error",
       "authentication_error",
       "payment_required",
       "signup_disabled",
       "invite_required",
       "account_suspended",
       "not_found",
       "wrong_region",
       "conflict",
       "rate_limit_error",
       "server_error",
       "engine_error"
      ]
     },
     "message": {
      "type": "string",
      "description": "What went wrong and what to fix."
     }
    },
    "required": [
     "type",
     "message"
    ]
   },
   "Error": {
    "type": "object",
    "description": "Every error the API itself sends has this shape.",
    "properties": {
     "error": {
      "$ref": "#/components/schemas/ErrorBody"
     }
    },
    "required": [
     "error"
    ]
   },
   "State": {
    "description": "What to evaluate: a non-empty string, an object with named fields, or an array (such as the turns of a conversation). Text only. The engine reads the start of a long state (about 475 tokens on the English checkpoint, about 990 on the others, less with many options); `reading` and `usage` say what was read, and `windows` reads the rest.",
    "oneOf": [
     {
      "type": "string",
      "minLength": 1,
      "pattern": "\\S"
     },
     {
      "type": "object"
     },
     {
      "type": "array"
     }
    ]
   },
   "Instructions": {
    "description": "The question's text: a string, an object or an array.",
    "type": [
     "string",
     "object",
     "array"
    ]
   },
   "ChoiceQuestion": {
    "type": "object",
    "description": "Pick one of a set of unordered options.",
    "properties": {
     "type": {
      "const": "choice"
     },
     "instructions": {
      "$ref": "#/components/schemas/Instructions"
     },
     "criteria": {
      "type": "object",
      "description": "Option name to description (a string or an object). 1 to 255 options; up to 20 are read by the engine in one pass, more are matched by default (see `method`).",
      "minProperties": 1,
      "maxProperties": 255,
      "additionalProperties": {}
     }
    },
    "required": [
     "type",
     "instructions",
     "criteria"
    ]
   },
   "ScoreQuestion": {
    "type": "object",
    "description": "A position on ordered levels.",
    "properties": {
     "type": {
      "const": "score"
     },
     "instructions": {
      "$ref": "#/components/schemas/Instructions"
     },
     "criteria": {
      "type": "array",
      "description": "2 to 10 levels, lowest first; each a string or an object. Describe levels as situations.",
      "minItems": 2,
      "maxItems": 10
     }
    },
    "required": [
     "type",
     "instructions",
     "criteria"
    ]
   },
   "NoulQuestion": {
    "type": "object",
    "description": "A yes/no statement; the answer is the probability of yes.",
    "properties": {
     "type": {
      "const": "noul"
     },
     "instructions": {
      "$ref": "#/components/schemas/Instructions"
     },
     "criteria": {
      "type": "object",
      "description": "Optional: what yes and what no mean.",
      "properties": {
       "true": {},
       "false": {}
      },
      "additionalProperties": false
     }
    },
    "required": [
     "type",
     "instructions"
    ]
   },
   "Question": {
    "oneOf": [
     {
      "$ref": "#/components/schemas/ChoiceQuestion"
     },
     {
      "$ref": "#/components/schemas/ScoreQuestion"
     },
     {
      "$ref": "#/components/schemas/NoulQuestion"
     }
    ],
    "discriminator": {
     "propertyName": "type",
     "mapping": {
      "choice": "#/components/schemas/ChoiceQuestion",
      "score": "#/components/schemas/ScoreQuestion",
      "noul": "#/components/schemas/NoulQuestion"
     }
    }
   },
   "Questions": {
    "type": "object",
    "description": "Question id to question; at least one. A request may need at most 400 engine questions: one per question, one per option order of a robust choice, and for a choice of more than 20 options read by the engine one per group of 20 plus a final round. Matched choices do not count. Above 400: a 422. A question whose `depends_on` is set to anything but null or an empty value is refused with a 422: every question is answered on its own.",
    "minProperties": 1,
    "additionalProperties": {
     "$ref": "#/components/schemas/Question"
    }
   },
   "SubjectId": {
    "type": "string",
    "description": "Your id for what a decision is about (a customer, a user, a device, a case), scoped to your account.",
    "pattern": "^[A-Za-z0-9_-]{1,36}$"
   },
   "MemoryOptions": {
    "type": "object",
    "description": "How the subject's memory is used.",
    "properties": {
     "use": {
      "type": "boolean",
      "description": "Use the subject's memory and rules (default true with a subject)."
     },
     "top_k": {
      "type": "integer",
      "description": "Recalled lines at most.",
      "minimum": 1,
      "maximum": 20,
      "default": 6
     },
     "min_share": {
      "type": "number",
      "description": "Keep recalled lines that reach this share of the best line's score.",
      "minimum": 0.05,
      "maximum": 1,
      "default": 0.2
     },
     "compare": {
      "type": "boolean",
      "description": "Also answer without the memory and its rules, and show what changed. Free.",
      "default": false
     },
     "placebo": {
      "type": "boolean",
      "description": "Also answer with neutral lines in place of the memory. Free.",
      "default": false
     },
     "names": {
      "type": "boolean",
      "description": "Search the subject's name and aliases with the state.",
      "default": true
     },
     "question_words": {
      "type": "boolean",
      "description": "Search the questions' instructions too.",
      "default": true
     },
     "focus": {
      "type": "boolean",
      "description": "Keep only lines that share a word with the state beyond the subject's names.",
      "default": false
     },
     "explain": {
      "type": "boolean",
      "description": "Per memory line (at most 8), how far the answer moves without it. Free.",
      "default": false
     },
     "meaning": {
      "type": "boolean",
      "description": "Internal, not a supported option. It is accepted as true or false and is off unless sent, but it may change or be refused without notice; do not rely on it."
     }
    }
   },
   "DecisionRequest": {
    "type": "object",
    "description": "`context` is refused (422): Jepela builds it from the subject's memory and `derive`. `assets` and `images` (pictures) that hold anything, here or in a question, are refused (422, nothing charged): Jepela reads text only. Any other field Jepela does not read, here, in `memory` or in a question (whose fields are type, instructions and criteria), is answered without it, with an `unknown_field` warning that names it and the closest field Jepela reads.",
    "properties": {
     "state": {
      "$ref": "#/components/schemas/State"
     },
     "questions": {
      "$ref": "#/components/schemas/Questions"
     },
     "model": {
      "type": "string",
      "description": "A name from GET /v1/models or your own `jepela-ft-...`; `default`, `auto` and `jepela-latest` are aliases of `jepela`.",
      "default": "jepela",
      "examples": [
       "jepela",
       "jepela-english",
       "jepela-multilingual",
       "jepela-typed-decisions"
      ]
     },
     "subject": {
      "description": "Use this subject's memory and rules.",
      "$ref": "#/components/schemas/SubjectId"
     },
     "customer": {
      "description": "Older name for `subject`.",
      "deprecated": true,
      "$ref": "#/components/schemas/SubjectId"
     },
     "memory": {
      "$ref": "#/components/schemas/MemoryOptions"
     },
     "quality": {
      "type": "string",
      "description": "Above `fast`, the answers the engine is least sure of, those whose state it could not read in full, and every answer of a decision with memory lines or computed facts are answered again by a larger, slower second tier (`best` sends more). Billed as `fast`. Where the second tier is not running, `balanced` and `best` get a 422 that says so.",
      "enum": [
       "fast",
       "balanced",
       "best"
      ],
      "default": "fast"
     },
     "method": {
      "type": "string",
      "description": "Choice questions: `auto` matches choices of more than 20 options by vectors and asks the engine the rest; `engine` reads every option (two rounds above 20); `match` matches every choice (a 422 without a choice question).",
      "enum": [
       "auto",
       "engine",
       "match"
      ],
      "default": "auto"
     },
     "robust": {
      "description": "Ask each choice of 2 to 20 options in several fixed orders and average them; `true` is 3. Billed per order.",
      "oneOf": [
       {
        "type": "boolean"
       },
       {
        "type": "object",
        "properties": {
         "orders": {
          "type": "integer",
          "minimum": 1,
          "maximum": 5,
          "default": 3
         }
        },
        "additionalProperties": false
       }
      ]
     },
     "windows": {
      "description": "Read a cut state in overlapping windows (32 tokens shared), at most 16, from its start; an array too long for 16 windows is read from its end, keeping its newest elements. `combine` sets max, min or mean per question id. Billed per window.",
      "oneOf": [
       {
        "type": "boolean"
       },
       {
        "type": "object",
        "properties": {
         "combine": {
          "type": "object",
          "additionalProperties": {
           "enum": [
            "max",
            "min",
            "mean"
           ]
          }
         }
        },
        "additionalProperties": false
       }
      ]
     },
     "derive": {
      "type": "object",
      "description": "Facts computed in code, placed before the state: {name: expression} or {name: {when, fact, else}}. Expressions read `values` and the state's fields (a bare name from `values` first; `values.` and `state.` name one of them): comparisons, and/or/not, arithmetic, days_since, days_between, len, lower, contains, number, abs, min, max, round. Free.",
      "maxProperties": 20,
      "propertyNames": {
       "pattern": "^[A-Za-z][A-Za-z0-9_]{0,39}$"
      },
      "additionalProperties": {
       "oneOf": [
        {
         "type": "string",
         "maxLength": 300
        },
        {
         "type": "object",
         "properties": {
          "when": {
           "type": "string",
           "maxLength": 300
          },
          "fact": {
           "type": "string",
           "minLength": 1,
           "maxLength": 500
          },
          "else": {
           "type": "string",
           "minLength": 1,
           "maxLength": 500
          }
         },
         "required": [
          "when",
          "fact"
         ],
         "additionalProperties": false
        }
       ]
      }
     },
     "values": {
      "type": "object",
      "description": "Numbers and text for `derive` and memory rules, read before a field of the state with the same name; `today` sets the date days_since counts from.",
      "maxProperties": 50
     },
     "cache": {
      "type": "boolean",
      "description": "Return the stored answers of an identical request from the same account that uses no memory, given by the same engine build (its code, device and precision) at most 24 hours before; POST /v1/decisions/delete removes them. The build is the one the engine showed last, in an answer or its health check (asked at most every 30 seconds), so a repeat is answered while the engine is briefly away, and never across an engine change once it is seen. Billed like the first call.",
      "default": false
     }
    },
    "required": [
     "state",
     "questions"
    ]
   },
   "Answer": {
    "type": "object",
    "description": "One answer; fields depend on the question type and options.",
    "properties": {
     "type": {
      "type": "string",
      "enum": [
       "choice",
       "score",
       "noul"
      ]
     },
     "choice": {
      "type": "string",
      "description": "The option with the highest probability."
     },
     "score": {
      "type": "number",
      "description": "The probability-weighted level; it can fall between levels."
     },
     "noul": {
      "type": "number",
      "description": "The probability of yes.",
      "minimum": 0,
      "maximum": 1
     },
     "probabilities": {
      "type": "object",
      "description": "Choice: per option, summing to 1. Score: per level, \"0\" the lowest.",
      "additionalProperties": {
       "type": "number"
      }
     },
     "legend": {
      "type": "object",
      "description": "Score: level to description."
     },
     "confidence": {
      "type": [
       "number",
       "null"
      ],
      "description": "0 to 1, how concentrated the probabilities are (noul: the larger of noul and 1 - noul)."
     },
     "answer_confidence": {
      "type": "number",
      "description": "0 to 1, the probability of the answer given, on one scale for every type: the chosen option's, the most likely level's, or the larger of noul and 1 - noul (there it equals confidence). Always the top value of the answer's own probabilities, after a tenant temperature too. Compare the automate_at thresholds of GET /v1/quality with this, not with confidence.",
      "minimum": 0,
      "maximum": 1
     },
     "method": {
      "type": "string",
      "enum": [
       "engine",
       "match"
      ]
     },
     "similarity": {
      "type": "object",
      "description": "Matched answers: best and second-best cosine.",
      "properties": {
       "best": {
        "type": "number"
       },
       "second": {
        "type": [
         "number",
         "null"
        ]
       }
      }
     },
     "action": {
      "type": "object",
      "description": "Passed through from the engine, on the answers it read (method engine); not on matched answers (method match) or on second-tier answers (tier 2), nor in their first. Do not decide on it: act_probability was 1.0 on all 40 answers recorded on 2026-09-23 (the Examples page), at confidences from 0.016 to 1.0. Decide from probabilities and confidence, or from the automate_at thresholds of GET /v1/quality.",
      "properties": {
       "act_probability": {
        "type": "number",
        "description": "An output of the engine, not one of the answer's probabilities; 1.0 on every recorded answer."
       }
      }
     },
     "rounds": {
      "type": "integer",
      "description": "2 when a large choice was read in two rounds."
     },
     "groups": {
      "type": "integer"
     },
     "orders": {
      "type": "integer",
      "description": "With robust: orders asked."
     },
     "agreement": {
      "type": "number",
      "description": "With robust: share of orders that picked the winner."
     },
     "excluded": {
      "type": "object",
      "description": "Options removed by exclusion rules, with the rule's text (probability 0)."
     },
     "window": {
      "type": [
       "integer",
       "null"
      ],
      "description": "With windows: the window the answer came from."
     },
     "calibration": {
      "type": "object",
      "description": "A temperature fitted on your labels; `raw_` fields hold the values before it.",
      "properties": {
       "temperature": {
        "type": "number"
       },
       "shift": {
        "type": [
         "number",
         "null"
        ]
       },
       "labels": {
        "type": "integer"
       },
       "scope": {
        "type": "string"
       }
      }
     },
     "raw_probabilities": {
      "type": "object",
      "additionalProperties": {
       "type": "number"
      }
     },
     "raw_score": {
      "type": "number"
     },
     "raw_noul": {
      "type": "number"
     },
     "right_probability": {
      "type": "number",
      "description": "Quality balanced or best: the estimate that the engine's answer is right."
     },
     "tier": {
      "type": "integer",
      "description": "2 when the second tier answered; 1 when the question was sent on but the second tier gave no answer, so the engine's own answer stands (see quality.fallback).",
      "enum": [
       1,
       2
      ]
     },
     "first": {
      "type": "object",
      "description": "On a second-tier answer: the engine's own."
     }
    },
    "required": [
     "type"
    ]
   },
   "Warning": {
    "type": "object",
    "description": "A known trap in this request. The answer is returned either way.",
    "properties": {
     "code": {
      "type": "string",
      "description": "no_other_option, many_options, degree_words, compound_noul, criteria_conflict, negated_instructions, state_cut, state_long, memory_cut, memory_full, memory_number_rule, memory_names_option_negatively, memory_presence_only, unknown_field, language_not_served, rule_skipped (the request has no field a rule reads), rule_broken (a rule that could not be evaluated); on remember also memory_negative_rule."
     },
     "message": {
      "type": "string"
     },
     "question": {
      "type": "string"
     },
     "case": {
      "type": "string",
      "description": "On POST /v1/golden: the case the warning is about."
     }
    },
    "required": [
     "code",
     "message"
    ]
   },
   "Reading": {
    "type": "object",
    "description": "What the engine read for one question, memory and computed lines included.",
    "properties": {
     "state_tokens": {
      "type": "integer"
     },
     "read": {
      "type": "integer"
     },
     "room": {
      "type": "integer"
     },
     "chars_read": {
      "type": "integer",
      "description": "Characters read, counted from the start of the state, or for an array, which is cut from its start, its last characters. With turns_dropped: the characters sent, the lines and the newest elements, read whole. With windows: the characters of the state the windows covered, from its start, or for an array too long for the windows, which are then placed at its end, up to its end."
     },
     "truncated": {
      "type": "boolean"
     },
     "context_lines_read": {
      "type": "object",
      "additionalProperties": {
       "type": "integer"
      },
      "description": "Per section of placed lines (known_facts, computed_facts): how many of its lines were read."
     },
     "turns_dropped": {
      "type": "integer",
      "description": "Present when Jepela left out this many of an array's oldest elements so that the memory and computed lines placed before it were read; the newest element is never left out."
     },
     "method": {
      "type": "string",
      "enum": [
       "match"
      ],
      "description": "Present on the reading of a matched choice: what the matching model read; its counts are the matching model's tokens."
     },
     "windows": {
      "type": "integer"
     }
    }
   },
   "MemoryBlock": {
    "type": "object",
    "description": "With a subject: what the memory did.",
    "properties": {
     "subject": {
      "type": "string"
     },
     "lines_used": {
      "type": "integer",
      "description": "Fired rules and recalled lines sent."
     },
     "lines_seen": {
      "type": "integer",
      "description": "How many the engine read: the first ones, or in an array the last ones. Each line in hits and rules_fired says whether it was read."
     },
     "lines_dropped": {
      "type": "integer",
      "description": "Left out of the engine's 64 context lines."
     },
     "hits": {
      "type": "array",
      "description": "Recalled lines.",
      "items": {
       "type": "object",
       "properties": {
        "text": {
         "type": "string"
        },
        "read": {
         "type": "boolean",
         "description": "Whether the engine read this line."
        }
       }
      }
     },
     "rules_fired": {
      "type": "array",
      "description": "Rules whose condition held, sent as lines before the recalled ones.",
      "items": {
       "type": "object",
       "properties": {
        "id": {
         "type": "string"
        },
        "text": {
         "type": "string"
        },
        "read": {
         "type": "boolean",
         "description": "Whether the engine read this line."
        }
       }
      }
     },
     "rules_broken": {
      "type": "array",
      "description": "Rules that were not checked; the decision went on. `missing` names the field when the request had none the rule reads (warning rule_skipped); without it the condition could not be evaluated (warning rule_broken).",
      "items": {
       "type": "object",
       "properties": {
        "id": {
         "type": "string"
        },
        "error": {
         "type": "string"
        },
        "missing": {
         "type": "string",
         "description": "The field the rule reads that the request did not have."
        }
       }
      }
     },
     "recall_ms": {
      "type": [
       "number",
       "null"
      ]
     },
     "without_memory": {
      "type": [
       "object",
       "null"
      ],
      "description": "With compare: the answers without memory and rules (on the same elements of an array when Jepela left out its oldest)."
     },
     "changes": {
      "type": [
       "object",
       "null"
      ],
      "description": "With compare: per question, what the memory changed."
     },
     "pinned": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "excluded": {
      "type": "object"
     },
     "exclusions_skipped": {},
     "explain": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "line": {
         "type": "string"
        },
        "kind": {
         "type": "string",
         "enum": [
          "pinned",
          "rule",
          "recalled"
         ]
        },
        "read": {
         "type": "boolean",
         "description": "Whether the engine read this line in the answer."
        },
        "effect": {},
        "measured_on_first": {
         "type": "array",
         "description": "Where the second tier gave the answer: the questions whose effect is the move of the engine's own answer `first`."
        }
       }
      }
     },
     "placebo": {
      "type": "object",
      "properties": {
       "content_effect": {},
       "memory_effect": {},
       "presence_only": {
        "type": "array"
       },
       "measured_on_first": {
        "type": "array",
        "description": "Where the second tier gave the answer: the questions whose effects are measured on the engine's own answer `first`."
       }
      }
     }
    }
   },
   "Usage": {
    "type": "object",
    "properties": {
     "questions": {
      "type": "integer"
     },
     "answers": {
      "type": "integer"
     },
     "answers_billed": {
      "type": "integer"
     },
     "engine_answers": {
      "type": "integer"
     },
     "input_characters": {
      "type": "integer"
     },
     "input_tokens": {
      "type": "integer",
      "description": "The engine's own count of what it read: what you pay for."
     },
     "output_tokens": {
      "type": "integer",
      "description": "Always 0.",
      "const": 0
     },
     "state_tokens": {
      "type": "integer"
     },
     "state_tokens_read": {
      "type": "integer"
     },
     "state_truncated": {
      "type": "boolean"
     },
     "memory_lines_used": {
      "type": "integer"
     },
     "memory_lines_seen": {
      "type": "integer"
     },
     "match_questions": {
      "type": "integer"
     },
     "tier2_questions": {
      "type": "integer"
     },
     "tier2_input_tokens": {
      "type": "integer",
      "description": "Not billed."
     },
     "cached": {
      "type": "boolean"
     },
     "engine_ms": {
      "type": "number"
     },
     "gateway_ms": {
      "type": "number"
     },
     "cost": {
      "type": "number"
     },
     "currency": {
      "type": "string"
     },
     "balance": {
      "type": "number",
      "description": "Credit left after this call."
     }
    }
   },
   "DecisionResponse": {
    "type": "object",
    "properties": {
     "decision_id": {
      "type": "string",
      "description": "For POST /v1/feedback. The state is never stored.",
      "pattern": "^dec_[0-9a-f]{24}$"
     },
     "model": {
      "type": "string",
      "description": "Aliases resolved."
     },
     "engine": {
      "type": "object",
      "properties": {
       "name": {
        "type": "string",
        "const": "jepela"
       },
       "checkpoint": {
        "type": [
         "string",
         "null"
        ]
       },
       "runtime": {
        "type": "object",
        "description": "The engine's metadata: `version` is the Jepela build id, the hash of the engine's code (the engine's `code` in GET /health), which changes whenever the engine does; also `device` and timings."
       }
      }
     },
     "answers": {
      "type": "object",
      "additionalProperties": {
       "$ref": "#/components/schemas/Answer"
      }
     },
     "warnings": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Warning"
      }
     },
     "reading": {
      "type": "object",
      "additionalProperties": {
       "$ref": "#/components/schemas/Reading"
      }
     },
     "derived": {
      "type": "object",
      "description": "The value of every derive name."
     },
     "memory": {
      "$ref": "#/components/schemas/MemoryBlock"
     },
     "quality": {
      "type": "object",
      "properties": {
       "level": {
        "type": "string",
        "enum": [
         "fast",
         "balanced",
         "best"
        ]
       },
       "below": {
        "type": "number"
       },
       "asked_again": {
        "type": "array",
        "items": {
         "type": "string"
        }
       },
       "why": {
        "type": "object",
        "additionalProperties": {
         "enum": [
          "unsure",
          "cut",
          "memory"
         ]
        }
       },
       "fallback": {
        "type": "object",
        "description": "When the second tier gave no answer in time, was busy or failed: the questions that kept the engine's own answer (each marked tier 1) and why. The decision is still answered.",
        "properties": {
         "questions": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "reason": {
          "type": "string"
         }
        }
       }
      }
     },
     "windows": {
      "type": "object",
      "properties": {
       "count": {
        "type": "integer"
       },
       "covered_all": {
        "type": "boolean"
       },
       "state_tokens": {
        "type": "integer"
       },
       "tokens_per_window": {
        "type": "integer"
       }
      }
     },
     "usage": {
      "$ref": "#/components/schemas/Usage"
     }
    },
    "required": [
     "decision_id",
     "model",
     "engine",
     "answers",
     "warnings",
     "usage"
    ]
   },
   "Charge": {
    "type": "object",
    "description": "What the call cost, and the credit left.",
    "properties": {
     "cost": {
      "type": "number"
     },
     "currency": {
      "type": "string"
     },
     "balance": {
      "type": "number"
     },
     "unbilled": {
      "type": "number",
      "description": "Only on forget and memory delete, when the balance did not cover the price: the call was carried out, its ledger line says cost 0, and this is what it would have cost. Removing data never takes the balance below zero."
     }
    }
   },
   "Rule": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "when": {
      "type": [
       "string",
       "null"
      ]
     },
     "text": {
      "type": "string"
     },
     "expires_at": {
      "type": [
       "string",
       "null"
      ]
     },
     "pin": {
      "type": "boolean"
     },
     "exclude": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "question": {
      "type": "string"
     }
    }
   },
   "GoldenCase": {
    "type": "object",
    "description": "A case with known answers; it keeps its state until you delete it.",
    "properties": {
     "id": {
      "type": "string",
      "description": "An existing id is replaced.",
      "minLength": 1,
      "maxLength": 64
     },
     "state": {
      "$ref": "#/components/schemas/State"
     },
     "questions": {
      "$ref": "#/components/schemas/Questions"
     },
     "expected": {
      "type": "object",
      "description": "Question id to the right answer: an option, a level from 0, or true/false.",
      "minProperties": 1
     },
     "subject": {
      "$ref": "#/components/schemas/SubjectId"
     },
     "values": {
      "type": "object"
     },
     "derive": {
      "type": "object"
     },
     "memory": {
      "$ref": "#/components/schemas/MemoryOptions"
     },
     "robust": {},
     "windows": {},
     "method": {
      "type": "string",
      "enum": [
       "auto",
       "engine",
       "match"
      ]
     }
    },
    "required": [
     "id",
     "state",
     "questions",
     "expected"
    ]
   },
   "FinetuneJob": {
    "type": "object",
    "properties": {
     "job": {
      "type": "string",
      "pattern": "^ft[0-9a-f]{12}$"
     },
     "model": {
      "type": "string",
      "description": "jepela-ft-..."
     },
     "base": {
      "type": "string",
      "enum": [
       "english",
       "multilingual",
       "typed-decisions"
      ]
     },
     "cases": {
      "type": "integer"
     },
     "held_out": {
      "type": "integer"
     },
     "cases_with_memory": {
      "type": "integer"
     },
     "state": {
      "type": "string",
      "enum": [
       "running",
       "saving",
       "done",
       "failed"
      ]
     },
     "error": {
      "type": [
       "string",
       "null"
      ]
     },
     "result": {
      "type": [
       "object",
       "null"
      ],
      "description": "won, accuracy_won, memory_kept, base_accuracy, tuned_accuracy, base_memory_effect, tuned_memory_effect, held_out_questions, log losses, training_seconds."
     },
     "usable": {
      "type": "boolean",
      "description": "The model won both tests and is in service."
     },
     "warnings": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "started": {
      "type": "string"
     },
     "finished": {
      "type": "string"
     },
     "deleted": {
      "type": "string"
     }
    }
   },
   "BatchJob": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string",
      "pattern": "^batch_[0-9a-f]{16}$"
     },
     "status": {
      "type": "string",
      "description": "queued, running, done, `stopped: no credit left`, `stopped: account suspended`, `stopped: key revoked`, or failed (with error)."
     },
     "total": {
      "type": "integer"
     },
     "done": {
      "type": "integer"
     },
     "succeeded": {
      "type": "integer"
     },
     "failed": {
      "type": "integer"
     },
     "cost": {
      "type": "number"
     },
     "created": {
      "type": "string"
     },
     "finished": {
      "type": [
       "string",
       "null"
      ]
     },
     "expires": {
      "type": "string",
      "description": "When it is deleted: 30 days from the upload."
     },
     "removed": {
      "type": "integer",
      "description": "Requests taken out when their subject's memory was deleted."
     },
     "error": {
      "type": "string"
     }
    }
   },
   "BatchResultLine": {
    "type": "object",
    "description": "One line of GET /v1/batches/{id}/results.",
    "properties": {
     "custom_id": {
      "type": "string"
     },
     "status": {
      "type": "integer",
      "description": "The HTTP status of this request."
     },
     "response": {
      "$ref": "#/components/schemas/DecisionResponse"
     },
     "error": {
      "$ref": "#/components/schemas/ErrorBody"
     }
    },
    "required": [
     "custom_id",
     "status"
    ]
   },
   "Model": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "engine": {
      "type": "string"
     },
     "checkpoint": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "available": {
      "type": "boolean",
      "description": "The engine answers right now."
     },
     "pinned": {
      "type": [
       "object",
       "null"
      ],
      "description": "`runtime`: \"engine\" and the Jepela build id, the hash of the engine's code; `loaded`: the checkpoints the engine holds."
     },
     "fine_tuned": {
      "type": "boolean",
      "description": "Your own fine-tuned models."
     }
    }
   },
   "Region": {
    "type": "object",
    "properties": {
     "name": {
      "type": "string"
     },
     "api": {
      "type": "string",
      "format": "uri"
     },
     "status": {
      "type": "string",
      "examples": [
       "live",
       "planned"
      ]
     },
     "where": {
      "type": "string"
     }
    }
   }
  },
  "responses": {
   "401": {
    "description": "authentication_error: no key, an unknown key or a revoked key.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "402": {
    "description": "payment_required: the balance is zero or below; priced requests answer 402 until credit is added (GET routes still work, and so do the routes that remove data).",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "403": {
    "description": "account_suspended: the account is suspended (it may still read its data on every GET route and call the routes that remove data) or erased. Sign-up: signup_disabled or invite_required.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "404": {
    "description": "not_found: unknown route, page, batch, fine-tuning job or model, or a decision id of another account or older than 30 days.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "409": {
    "description": "conflict: a limit, nothing stored (5,000 golden cases per account; 10,000 memory lines or 100 rules per subject); or a fine-tuning job that cannot start now (another of yours runs, the engine is off or has too little free memory).",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "413": {
    "description": "invalid_request_error: body over 1 MB (16 MB for /v1/batches and /v1/golden); a compressed body counts once decompressed.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "415": {
    "description": "invalid_request_error: not application/json (or JSON Lines for /v1/batches), or a Content-Encoding other than one layer of gzip, x-gzip or zstd. A compressed body that does not decompress is a 400.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "421": {
    "description": "wrong_region: the key (or sign-up) belongs to another region; the message and Location give its API address.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    },
    "headers": {
     "Location": {
      "description": "The API address of the key's region.",
      "schema": {
       "type": "string",
       "format": "uri"
      }
     }
    }
   },
   "422": {
    "description": "invalid_request_error: the reason is in the message (invalid JSON or a duplicate key, a bad question, an unknown model (a Jev name such as jev-latest is told to set the model to jepela), a bad subject id, an expression that is not allowed, a value out of range).",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "429": {
    "description": "rate_limit_error: over the account's requests per minute (all its keys together; 600 by default, 60 after sign-up), or too many sign-ups from one address.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    },
    "headers": {
     "Retry-After": {
      "description": "Seconds to wait.",
      "schema": {
       "type": "integer"
      }
     }
    }
   },
   "500": {
    "description": "server_error: the API failed; the message names only the error type. Nothing was charged.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "502": {
    "description": "engine_error: the engine or the memory service is down or answered an error (`the engine is off` when switched off). Nothing was charged; retry with backoff.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "503": {
    "description": "From the proxy in front of the API when the service cannot be reached; the body need not be JSON. Nothing was charged; retry with backoff."
   }
  }
 }
}