{
  "openapi": "3.1.0",
  "info": {
    "title": "Kvick API",
    "version": "2026-11",
    "summary": "Calibrated decisions from Skena Kvick: yes or no, multiple choice and ratings, each with a probability.",
    "description": "Kvick answers questions about a text or any JSON with the probability of every possible answer. It speaks the Jev API (POST /v1/systemone) at https://api.skena.ai, the one base for every Skena API. Guides: https://skena.ai/docs/kvick.\n\nAuthenticate with a Skena API key you make on the dashboard (one per app or server): `Authorization: Bearer skl_…`. Every key calls Klok too. Every call with your API key is paid from Skena AI credit, whatever plan the account has. Without credit, a call is refused before it runs and costs nothing. Kvick costs $0.10 per million input tokens, output free. Images come with Kvick's next release, charged like text; until then the API refuses them.\n\nEvery request must declare its Content-Length (no chunked uploads), at most 24 MiB.\n\nKvick is a Skena Labs model: there is no uptime guarantee yet, and the API may change.\n\nPlease do not send personal or sensitive data.",
    "termsOfService": "https://skena.ai/terms",
    "contact": {
      "name": "Skena Labs",
      "email": "hello@skena.ai",
      "url": "https://skena.ai/docs"
    }
  },
  "servers": [
    {
      "url": "https://api.skena.ai",
      "description": "Skena Cloud: the one base for every Skena API"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "externalDocs": {
    "description": "Kvick API guide",
    "url": "https://skena.ai/docs/kvick"
  },
  "paths": {
    "/v1/systemone": {
      "post": {
        "operationId": "systemOne",
        "summary": "Ask questions about a state",
        "description": "Kvick reads the state once and answers every question against it. Each answer comes back under its question's id.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional: up to 128 bytes, a new one for each call. A call that reuses a key answers 409 inference-request-already-recorded and is neither run nor charged, so an automatic retry can never charge a call twice. The 409 does not repeat the first answer.",
            "schema": {
              "type": "string",
              "maxLength": 128,
              "description": "At most 128 bytes."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SystemOneRequest"
              },
              "examples": {
                "quickstart": {
                  "summary": "One yes or no question",
                  "value": {
                    "model": "kvick-1",
                    "state": "My card was charged twice, please refund one.",
                    "questions": {
                      "refund": {
                        "type": "noul",
                        "instructions": "Is the customer asking for money back?"
                      }
                    }
                  }
                },
                "allTypes": {
                  "summary": "One question of each type",
                  "value": {
                    "model": "kvick-1",
                    "state": "My card was charged twice, please refund one.",
                    "questions": {
                      "refund": {
                        "type": "noul",
                        "instructions": "Is the customer asking for money back?"
                      },
                      "department": {
                        "type": "choice",
                        "criteria": {
                          "billing": null,
                          "technical": null,
                          "sales": null
                        }
                      },
                      "frustration": {
                        "type": "score",
                        "instructions": "How frustrated is the customer?",
                        "criteria": [
                          "calm",
                          "mildly annoyed",
                          "annoyed",
                          "frustrated",
                          "furious"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The answers.",
            "headers": {
              "X-Decide-Request-Id": {
                "description": "Kvick's id for the call. Quote it when you report a problem.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Skena-Usage-Id": {
                "description": "The call's entry in your credit history.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SystemOneResponse"
                },
                "example": {
                  "model": "kvick-1",
                  "answers": {
                    "refund": {
                      "type": "noul",
                      "noul": 0.97,
                      "stats": {}
                    },
                    "department": {
                      "type": "choice",
                      "choice": "billing",
                      "confidence": 0.93,
                      "probabilities": {
                        "billing": 0.95,
                        "technical": 0.03,
                        "sales": 0.02
                      },
                      "stats": {}
                    },
                    "frustration": {
                      "type": "score",
                      "score": 1.64,
                      "confidence": 0.45,
                      "legend": {
                        "0": "calm",
                        "1": "mildly annoyed",
                        "2": "annoyed",
                        "3": "frustrated",
                        "4": "furious"
                      },
                      "probabilities": {
                        "0": 0.08,
                        "1": 0.35,
                        "2": 0.44,
                        "3": 0.11,
                        "4": 0.02
                      },
                      "stats": {}
                    }
                  },
                  "usage": {
                    "input_tokens": 214,
                    "output_tokens": 3
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid-decision-request`: Kvick cannot answer the request: a field is wrong, it is too long, or (until Kvick's next release) it holds an image. Refused before it runs, so it costs nothing.\n\n`unreadable-body`: The body could not be read.\n\n`invalid-credit-request-id`: The Idempotency-Key header is longer than 128 bytes, or holds a line break or a NUL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GatewayError"
                },
                "example": {
                  "error": {
                    "message": "invalid-decision-request",
                    "type": "skena_labs_error",
                    "code": "invalid-decision-request"
                  }
                }
              }
            }
          },
          "401": {
            "description": "`invalid-labs-key`: No key, a malformed key, a revoked key, or a device key from Skena Builder or Skena Servant (the API takes Skena API keys only).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GatewayError"
                }
              }
            }
          },
          "402": {
            "description": "`ai-credit-insufficient`: Your credit does not cover this call.\n\n`ai-credit-unavailable`: Nothing is left to pay for the call: the welcome gift is used up or expired, and the account has no other credit (403 while the welcome gift is paused).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GatewayError"
                }
              }
            }
          },
          "403": {
            "description": "`ai-credit-unavailable`: Nothing is left to pay for the call: the welcome gift is used up or expired, and the account has no other credit (403 while the welcome gift is paused).\n\n`ai-credit-required`: The account has no Skena AI credit. The API is paid from credit alone, whatever plan the account has.\n\n`ai-credit-activation-required`: Only the welcome gift is left, and no computer has activated it yet. Skena Cloud refuses before reading the body, so these calls cost nothing.\n\n`account-unverified`: The account's email is not verified.\n\n`account-suspended`: The account is suspended.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GatewayError"
                }
              }
            }
          },
          "404": {
            "description": "`unknown-endpoint`: the base serves no such path or method (answered by Skena Cloud's edge).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EdgeError"
                },
                "example": {
                  "error": {
                    "message": "unknown-endpoint",
                    "type": "skena_api_error",
                    "code": "unknown-endpoint"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`inference-request-already-recorded`: The call reused an Idempotency-Key this account already sent, whatever happened to that first call.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GatewayError"
                }
              }
            }
          },
          "411": {
            "description": "`length-required`: The body did not declare its length (a chunked upload), or it is empty.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GatewayError"
                }
              }
            }
          },
          "413": {
            "description": "`request-too-large`: The body is over the 24 MiB limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GatewayError"
                }
              }
            }
          },
          "422": {
            "description": "Rare: Kvick's engine refused a request that passed Skena Cloud's check (`engine_rejected`). A request that breaks the rules is refused earlier, with 400 `invalid-decision-request`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "429": {
            "description": "`too-many-concurrent-requests`: On the welcome gift: more than 2 calls in flight or waiting on this account, or its calls together hold more than a third of Skena Cloud's room.\n\n`daily-limit-reached`: On the welcome gift: 5,000 requests today.\n\n`daily-token-limit-reached`: On the welcome gift: 5,000,000 tokens today.\n\n`inference-attempt-limit`: Calls paid from credit, Kvick and Klok together: over 120 in the last minute (on the welcome gift, also 5,000 in the last 24 hours).\n\nKvick's own queue can also answer 429 with `{\"detail\": \"…\"}`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying, when the refusal is worth retrying after a wait (not on every 503).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/GatewayError"
                    },
                    {
                      "$ref": "#/components/schemas/EngineError"
                    }
                  ]
                },
                "example": {
                  "error": {
                    "message": "daily-limit-reached",
                    "type": "skena_labs_error",
                    "code": "daily-limit-reached"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something failed inside Kvick (`internal_error`), or `labs-upstream-auth`: Skena Cloud could not sign in to Kvick: our problem, not your key.\n\n`labs-upstream-response`: Kvick's answer was unreadable or too large to relay.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/GatewayError"
                    },
                    {
                      "$ref": "#/components/schemas/EngineError"
                    }
                  ]
                }
              }
            }
          },
          "503": {
            "description": "`labs-busy`: Skena Cloud was full and the call waited 10 seconds for room.\n\n`labs-not-serving`: Kvick is not serving right now.\n\n`credit-quote-unavailable`: Skena Cloud could not price the call before running it.\n\n`labs-store-unavailable`: A temporary problem on our side.\n\n`labs-cache-isolation-unavailable`: A temporary problem on our side.\n\n`labs-upstream-unavailable`: Kvick could not be reached.\n\nKvick's own queue can also answer 503 with `{\"detail\": \"…\"}`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying, when the refusal is worth retrying after a wait (not on every 503).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/GatewayError"
                    },
                    {
                      "$ref": "#/components/schemas/EngineError"
                    }
                  ]
                }
              }
            }
          },
          "529": {
            "description": "The decision engine is temporarily unavailable (`overloaded_error`).",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying, when the refusal is worth retrying after a wait (not on every 503).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngineError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/models": {
      "get": {
        "operationId": "listModels",
        "summary": "List every model the key can call",
        "description": "Lists every model your key can call, in one answer with two shapes: OpenAI's data (what OpenAI's SDKs read) and the Jev API's models (what Jev clients read, Kvick only). A model is listed only while Skena Cloud serves it. It needs a valid key on an account with credit, and never wakes a model. Its refusals use the skena_inference_error envelope, for Kvick too.",
        "responses": {
          "200": {
            "description": "The models, in OpenAI's shape (data) and the Jev API's (models).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelList"
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "id": "klok-1",
                      "object": "model",
                      "owned_by": "skena",
                      "name": "Skena Klok",
                      "context_window": 262144
                    },
                    {
                      "id": "kvick-1",
                      "object": "model",
                      "owned_by": "skena",
                      "name": "Skena Kvick"
                    }
                  ],
                  "models": [
                    {
                      "name": "kvick-1",
                      "description": "Skena Kvick: calibrated System One decisions (Jev-compatible)."
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "`invalid-inference-key`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelsError"
                }
              }
            }
          },
          "402": {
            "description": "`ai-credit-unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelsError"
                }
              }
            }
          },
          "403": {
            "description": "`account-unverified`, `account-suspended`, `ai-credit-required`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelsError"
                }
              }
            }
          },
          "404": {
            "description": "`unknown-endpoint`: the base serves no such path or method (answered by Skena Cloud's edge).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EdgeError"
                },
                "example": {
                  "error": {
                    "message": "unknown-endpoint",
                    "type": "skena_api_error",
                    "code": "unknown-endpoint"
                  }
                }
              }
            }
          },
          "503": {
            "description": "`inference-account-store-unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelsError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "A Skena API key (skl_…) you make on the dashboard's API key page. Every key calls Kvick and Klok."
      }
    },
    "schemas": {
      "Entry": {
        "description": "A string, number, boolean, null, array or object, nested up to 32 levels.",
        "type": [
          "string",
          "number",
          "boolean",
          "null",
          "array",
          "object"
        ],
        "items": {
          "$ref": "#/components/schemas/Entry"
        },
        "additionalProperties": {
          "$ref": "#/components/schemas/Entry"
        }
      },
      "Image": {
        "description": "An image anywhere in `state`, from Kvick's next release (until then refused with 400 invalid-decision-request): exactly these two keys (any other key makes it plain JSON text), a base64 data URL, at most 10 MiB decoded and 40 megapixels each, 8 per request. Charged by its real tokens, each at the text price times the image weight (today 440%, set from a GPU-time measurement before images go live); each holds 65536 tokens' worth of credit ($0.0288) before the call runs.",
        "type": "object",
        "required": [
          "type",
          "url"
        ],
        "additionalProperties": false,
        "properties": {
          "type": {
            "const": "image"
          },
          "url": {
            "type": "string",
            "pattern": "^data:image/[A-Za-z0-9.+-]+;base64,"
          }
        }
      },
      "SystemOneRequest": {
        "type": "object",
        "required": [
          "model",
          "state",
          "questions"
        ],
        "properties": {
          "model": {
            "type": "string",
            "description": "Send \"kvick-1\", the one model today. The answer names the model that answered.",
            "examples": [
              "kvick-1"
            ]
          },
          "state": {
            "description": "What the questions are about: a text, or any JSON. Images (see the Image schema) come with Kvick's next release; until then an image in it is refused with 400 invalid-decision-request.",
            "type": [
              "string",
              "object",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/Entry"
            },
            "additionalProperties": {
              "$ref": "#/components/schemas/Entry"
            }
          },
          "questions": {
            "type": "object",
            "minProperties": 1,
            "additionalProperties": {
              "$ref": "#/components/schemas/Question"
            },
            "description": "Questions keyed by an id you choose: at most 1,024 scored steps in all (one per question, a few more for a choice with over 26 options)."
          }
        }
      },
      "Question": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/NoulQuestion"
          },
          {
            "$ref": "#/components/schemas/ChoiceQuestion"
          },
          {
            "$ref": "#/components/schemas/ScoreQuestion"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "noul": "#/components/schemas/NoulQuestion",
            "choice": "#/components/schemas/ChoiceQuestion",
            "score": "#/components/schemas/ScoreQuestion"
          }
        }
      },
      "NoulQuestion": {
        "type": "object",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "const": "noul"
          },
          "instructions": {
            "$ref": "#/components/schemas/Entry"
          },
          "criteria": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "properties": {
              "true": {
                "$ref": "#/components/schemas/Entry"
              },
              "false": {
                "$ref": "#/components/schemas/Entry"
              }
            },
            "description": "What yes and no mean, when they need defining."
          }
        }
      },
      "ChoiceQuestion": {
        "type": "object",
        "required": [
          "type",
          "criteria"
        ],
        "properties": {
          "type": {
            "const": "choice"
          },
          "instructions": {
            "$ref": "#/components/schemas/Entry"
          },
          "criteria": {
            "type": "object",
            "minProperties": 1,
            "maxProperties": 255,
            "additionalProperties": {
              "$ref": "#/components/schemas/Entry"
            },
            "description": "Option name to description (null when the name says it all)."
          }
        }
      },
      "ScoreQuestion": {
        "type": "object",
        "required": [
          "type",
          "criteria"
        ],
        "properties": {
          "type": {
            "const": "score"
          },
          "instructions": {
            "$ref": "#/components/schemas/Entry"
          },
          "criteria": {
            "type": "array",
            "minItems": 2,
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/Entry"
            },
            "description": "Level descriptions, lowest first. A level's number is its position, from 0."
          }
        }
      },
      "Stats": {
        "type": "object",
        "description": "Empty: reserved for diagnostics."
      },
      "NoulAnswer": {
        "type": "object",
        "required": [
          "type",
          "noul",
          "stats"
        ],
        "properties": {
          "type": {
            "const": "noul"
          },
          "noul": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "The probability of yes."
          },
          "stats": {
            "$ref": "#/components/schemas/Stats"
          }
        }
      },
      "ChoiceAnswer": {
        "type": "object",
        "required": [
          "type",
          "choice",
          "confidence",
          "probabilities",
          "stats"
        ],
        "properties": {
          "type": {
            "const": "choice"
          },
          "choice": {
            "type": "string",
            "description": "The likeliest option."
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "0 for a uniform spread, 1 for certainty."
          },
          "probabilities": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            }
          },
          "stats": {
            "$ref": "#/components/schemas/Stats"
          }
        }
      },
      "ScoreAnswer": {
        "type": "object",
        "required": [
          "type",
          "score",
          "confidence",
          "legend",
          "probabilities",
          "stats"
        ],
        "properties": {
          "type": {
            "const": "score"
          },
          "score": {
            "type": "number",
            "description": "The expected level, from 0; it can fall between levels."
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "legend": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/Entry"
            },
            "description": "Level number to its description."
          },
          "probabilities": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "description": "Level number to its probability."
          },
          "stats": {
            "$ref": "#/components/schemas/Stats"
          }
        }
      },
      "Answer": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/NoulAnswer"
          },
          {
            "$ref": "#/components/schemas/ChoiceAnswer"
          },
          {
            "$ref": "#/components/schemas/ScoreAnswer"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "noul": "#/components/schemas/NoulAnswer",
            "choice": "#/components/schemas/ChoiceAnswer",
            "score": "#/components/schemas/ScoreAnswer"
          }
        }
      },
      "SystemOneResponse": {
        "type": "object",
        "required": [
          "model",
          "answers",
          "usage"
        ],
        "properties": {
          "model": {
            "type": "string"
          },
          "answers": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/Answer"
            }
          },
          "usage": {
            "type": "object",
            "required": [
              "input_tokens",
              "output_tokens"
            ],
            "properties": {
              "input_tokens": {
                "type": "integer",
                "description": "What Kvick read. Credit pays for these."
              },
              "output_tokens": {
                "type": "integer",
                "description": "Answer steps scored. Free."
              },
              "image_tokens": {
                "type": "integer",
                "description": "A Kvick extension, only when the request has images (from Kvick's next release): how many of input_tokens the images took. They are charged at the image weight (today 440%)."
              }
            }
          }
        }
      },
      "ModelList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "models"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "description": "OpenAI's shape: every model the key can call.",
            "items": {
              "type": "object",
              "required": [
                "id",
                "object",
                "owned_by"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "object": {
                  "const": "model"
                },
                "owned_by": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "context_window": {
                  "type": "integer"
                }
              }
            }
          },
          "models": {
            "type": "array",
            "description": "The Jev API's shape: the models it speaks (Kvick).",
            "items": {
              "type": "object",
              "required": [
                "name"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "ModelsError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "type"
            ],
            "properties": {
              "code": {
                "enum": [
                  "invalid-inference-key",
                  "ai-credit-unavailable",
                  "account-unverified",
                  "account-suspended",
                  "ai-credit-required",
                  "inference-account-store-unavailable"
                ]
              },
              "message": {
                "type": "string"
              },
              "type": {
                "const": "skena_inference_error"
              }
            }
          }
        }
      },
      "EdgeError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "type"
            ],
            "properties": {
              "code": {
                "const": "unknown-endpoint"
              },
              "message": {
                "type": "string"
              },
              "type": {
                "const": "skena_api_error"
              }
            }
          }
        }
      },
      "GatewayError": {
        "type": "object",
        "required": [
          "error"
        ],
        "description": "A refusal from Skena Cloud, before or around the call.",
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "type"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid-decision-request",
                  "unreadable-body",
                  "invalid-credit-request-id",
                  "invalid-labs-key",
                  "ai-credit-insufficient",
                  "ai-credit-unavailable",
                  "ai-credit-required",
                  "ai-credit-activation-required",
                  "account-unverified",
                  "account-suspended",
                  "inference-request-already-recorded",
                  "length-required",
                  "request-too-large",
                  "too-many-concurrent-requests",
                  "daily-limit-reached",
                  "daily-token-limit-reached",
                  "inference-attempt-limit",
                  "labs-busy",
                  "labs-not-serving",
                  "credit-quote-unavailable",
                  "labs-store-unavailable",
                  "labs-cache-isolation-unavailable",
                  "labs-upstream-unavailable",
                  "labs-upstream-auth",
                  "labs-upstream-response"
                ]
              },
              "message": {
                "type": "string"
              },
              "type": {
                "const": "skena_labs_error"
              }
            }
          }
        }
      },
      "ValidationError": {
        "type": "object",
        "required": [
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "loc",
                "msg",
                "type"
              ],
              "properties": {
                "loc": {
                  "type": "array",
                  "items": {
                    "type": [
                      "string",
                      "integer"
                    ]
                  }
                },
                "msg": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "EngineError": {
        "type": "object",
        "required": [
          "detail"
        ],
        "properties": {
          "detail": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "object",
                "required": [
                  "error_type",
                  "message"
                ],
                "properties": {
                  "error_type": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  }
                }
              }
            ]
          }
        }
      }
    }
  }
}
