{
  "openapi": "3.1.0",
  "info": {
    "title": "Laya Studio API",
    "version": "1.0.0",
    "description": "Laya Studio: Decisions (hosted inference for the open-source Laya decision model) and Laya Guard (checks agent tool calls before they run). Decisions are wire-compatible with the TypeSafe Jev /v1/systemone protocol. Billing: decisions per input token (1 credit = 1 input token, the usage.input_tokens of a successful response), 15% below Jev's list price; guard checks at a flat $0.20 per 1k checks from the same balance; 6k free tokens per new workspace; failed requests are free. Headers: x-credits-charged, x-free-runs-remaining."
  },
  "servers": [
    {
      "url": "https://api.laya.studio"
    },
    {
      "url": "https://laya.studio"
    }
  ],
  "security": [
    {
      "bearer": []
    }
  ],
  "paths": {
    "/v1/systemone": {
      "post": {
        "operationId": "systemone",
        "summary": "Answer typed questions about a state in one forward pass",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DecisionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Answers with calibrated probabilities",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DecisionResponse"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "402": {
            "description": "Not enough credits (input tokens) for this request"
          },
          "422": {
            "description": "A question is invalid (unknown type, missing criteria, …)"
          },
          "429": {
            "description": "Rate limited — see Retry-After"
          },
          "503": {
            "description": "Inference temporarily unavailable — retry with backoff"
          }
        }
      }
    },
    "/v1/ask": {
      "post": {
        "operationId": "ask",
        "summary": "Plain words in: an LLM plans the questions, Laya answers them",
        "description": "Send what you want to know in plain words, with the text to judge. A planner (Mercury 2.5 via phi-cloud; a Swiss-resident model when the request is Swiss-only) writes typed questions and calls decide; Laya answers them with calibrated probabilities and the reply is built from those numbers. Billed: the decision's input tokens (+15% when Swiss-only) plus the planner's tokens: 3 credits per prompt token and 11 per completion token on Mercury, 18 and 36 on the Swiss planner (Gemma-4 31B). Credits for the whole ask are reserved before the planner runs; the planner's tokens are billed once it has answered (even if its plan is unusable), the decision only when it succeeds. Set screen: true to have Laya Guard check the text for prompt injection first (5,602 credits per check): a block verdict returns 422 prompt_injection_detected before anything is planned.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "prompt"
                ],
                "properties": {
                  "prompt": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 60000
                  },
                  "screen": {
                    "type": "boolean",
                    "default": false,
                    "description": "Run a Laya Guard prompt-injection check on the text before planning (recommended for untrusted documents, emails, transcripts)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The reply, the plan (reusable with /v1/systemone), the answers and the planner usage.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "reply": {
                      "type": "string"
                    },
                    "plan": {
                      "type": "object",
                      "properties": {
                        "state": {},
                        "questions": {
                          "type": "object"
                        }
                      }
                    },
                    "answers": {
                      "type": "object"
                    },
                    "usage": {
                      "type": "object"
                    },
                    "llm": {
                      "type": "object",
                      "properties": {
                        "model": {
                          "type": "string"
                        },
                        "region": {
                          "type": "string"
                        },
                        "input_tokens": {
                          "type": "integer"
                        },
                        "output_tokens": {
                          "type": "integer"
                        },
                        "credits": {
                          "type": "integer"
                        }
                      }
                    },
                    "screened": {
                      "type": "object",
                      "description": "Present when screen was true: the Guard verdict on the text (allow or ask; block is a 422).",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "verdict": {
                          "type": "string",
                          "enum": [
                            "allow",
                            "ask",
                            "block"
                          ]
                        },
                        "p_unsafe": {
                          "type": "number"
                        },
                        "credits": {
                          "type": "integer"
                        }
                      }
                    },
                    "credits_charged": {
                      "type": "integer",
                      "description": "Everything this ask charged: decision, planner and any Guard screen."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing prompt"
          },
          "402": {
            "description": "The balance cannot cover the estimate (reserved before the planner runs)"
          },
          "422": {
            "description": "unplannable: the planner could not turn the request into questions (its tokens are billed); prompt_injection_detected: the Guard screen blocked the text (the check is billed, nothing is planned)"
          },
          "503": {
            "description": "Planner or inference unavailable (a failed decision is free; planner tokens already spent are billed)"
          }
        }
      }
    },
    "/v1/systemone/batch": {
      "post": {
        "operationId": "systemoneBatch",
        "summary": "Up to 64 decisions in one call",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "requests"
                ],
                "properties": {
                  "requests": {
                    "type": "array",
                    "maxItems": 64,
                    "items": {
                      "$ref": "#/components/schemas/DecisionRequest"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "One result (or {error}) per request, in order"
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "402": {
            "description": "Not enough credits (input tokens) for this request"
          },
          "422": {
            "description": "A question is invalid (unknown type, missing criteria, …)"
          },
          "429": {
            "description": "Rate limited — see Retry-After"
          },
          "503": {
            "description": "Inference temporarily unavailable — retry with backoff"
          }
        }
      }
    },
    "/v1/guard": {
      "post": {
        "operationId": "guard",
        "summary": "Laya Guard: check one agent action before it runs",
        "description": "Judge a tool call before your agent executes it: allow / ask / block, with calibrated scores for nine questions. $0.20 per 1k checks (5,602 credits per check, +15% when Swiss-only); failed checks are free.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GuardCheck"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The verdict and scores",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GuardResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "402": {
            "description": "The balance cannot cover the check"
          },
          "422": {
            "description": "A question is invalid (unknown type, missing criteria, …)"
          },
          "429": {
            "description": "Rate limited — see Retry-After"
          },
          "503": {
            "description": "Inference temporarily unavailable — retry with backoff"
          }
        }
      }
    },
    "/v1/guard/batch": {
      "post": {
        "operationId": "guardBatch",
        "summary": "Laya Guard: up to 64 checks in one call",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "checks"
                ],
                "properties": {
                  "checks": {
                    "type": "array",
                    "maxItems": 64,
                    "items": {
                      "$ref": "#/components/schemas/GuardCheck"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "One result (or {error}) per check, in order; only checks with a verdict are billed"
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "402": {
            "description": "The balance cannot cover the checks"
          },
          "422": {
            "description": "A question is invalid (unknown type, missing criteria, …)"
          },
          "429": {
            "description": "Rate limited — see Retry-After"
          },
          "503": {
            "description": "Inference temporarily unavailable — retry with backoff"
          }
        }
      }
    },
    "/v1/models": {
      "get": {
        "operationId": "models",
        "summary": "List served checkpoints",
        "security": [],
        "responses": {
          "200": {
            "description": "Models"
          }
        }
      }
    },
    "/v1/usage": {
      "get": {
        "operationId": "usage",
        "summary": "Credit balance and 30-day usage",
        "responses": {
          "200": {
            "description": "Usage"
          },
          "401": {
            "description": "Missing or invalid API key"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "lsk_live_… key from the dashboard"
      }
    },
    "schemas": {
      "Question": {
        "type": "object",
        "required": [
          "type",
          "instructions"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "choice",
              "score",
              "noul"
            ]
          },
          "instructions": {
            "type": "string",
            "description": "What the model should decide, in plain language."
          },
          "criteria": {
            "description": "choice: object label → description, or array of labels. score: array of level descriptions (index 0 first). noul: optional {\"true\": \"...\", \"false\": \"...\"}.",
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          }
        }
      },
      "DecisionRequest": {
        "type": "object",
        "required": [
          "questions"
        ],
        "properties": {
          "state": {
            "description": "The input to decide about: a string, a JSON object, or a list of conversation turns."
          },
          "questions": {
            "type": "object",
            "maxProperties": 32,
            "additionalProperties": {
              "$ref": "#/components/schemas/Question"
            }
          },
          "model": {
            "type": "string",
            "enum": [
              "english",
              "multilingual",
              "typed-decisions"
            ],
            "description": "Pin a checkpoint. Omit to route automatically by script and language. Unknown ids (e.g. Jev model names) are ignored."
          },
          "lang": {
            "type": "string",
            "maxLength": 16,
            "description": "Optional language hint such as \"en\" or \"de-CH\"; overrides detection."
          }
        }
      },
      "Answer": {
        "oneOf": [
          {
            "type": "object",
            "title": "ChoiceAnswer",
            "properties": {
              "type": {
                "const": "choice"
              },
              "choice": {
                "type": "string"
              },
              "probabilities": {
                "type": "object",
                "additionalProperties": {
                  "type": "number"
                }
              },
              "confidence": {
                "type": "number",
                "description": "1 − H(p)/log k (normalised-entropy confidence)."
              },
              "action": {
                "type": "object",
                "properties": {
                  "act_probability": {
                    "type": "number"
                  }
                }
              }
            }
          },
          {
            "type": "object",
            "title": "ScoreAnswer",
            "properties": {
              "type": {
                "const": "score"
              },
              "score": {
                "type": "number",
                "description": "Expected level index."
              },
              "legend": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              },
              "probabilities": {
                "type": "object",
                "additionalProperties": {
                  "type": "number"
                }
              },
              "confidence": {
                "type": "number"
              },
              "action": {
                "type": "object",
                "properties": {
                  "act_probability": {
                    "type": "number"
                  }
                }
              }
            }
          },
          {
            "type": "object",
            "title": "NoulAnswer",
            "properties": {
              "type": {
                "const": "noul"
              },
              "noul": {
                "type": "number",
                "description": "P(true)."
              },
              "confidence": {
                "type": "number"
              },
              "action": {
                "type": "object",
                "properties": {
                  "act_probability": {
                    "type": "number"
                  }
                }
              }
            }
          }
        ]
      },
      "DecisionResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "answers": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/Answer"
            }
          },
          "usage": {
            "type": "object",
            "properties": {
              "input_tokens": {
                "type": "integer"
              },
              "output_tokens": {
                "type": "integer",
                "const": 0
              }
            }
          },
          "routing": {
            "type": "object",
            "properties": {
              "model": {
                "type": "string"
              },
              "reason": {
                "type": "string"
              },
              "repo": {
                "type": "string"
              }
            }
          }
        }
      },
      "GuardCheck": {
        "type": "object",
        "properties": {
          "action": {
            "description": "The tool call about to run: a string such as \"db.drop_table(users)\", or an object {tool, args}.",
            "anyOf": [
              {
                "type": "string",
                "minLength": 1,
                "maxLength": 20000
              },
              {
                "type": "object",
                "properties": {
                  "tool": {
                    "type": "string",
                    "minLength": 1
                  },
                  "args": {
                    "type": "object"
                  }
                },
                "required": [
                  "tool"
                ]
              }
            ]
          },
          "intent": {
            "type": "string",
            "maxLength": 20000,
            "description": "What the user asked the agent to do. The most useful field after the action."
          },
          "user_message": {
            "type": "string",
            "maxLength": 20000,
            "description": "The latest user message, when it differs from the intent."
          },
          "trigger": {
            "type": "string",
            "enum": [
              "user_request",
              "tool_result",
              "correction",
              "user_override"
            ],
            "description": "What produced the action. Use tool_result when the idea came from a tool output or document (possible prompt injection)."
          },
          "constraints": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 32,
            "description": "Rules the action must respect, e.g. \"staging only\", \"never email outside acme.com\"."
          },
          "context": {
            "type": "string",
            "maxLength": 20000,
            "description": "Agent role, environment (production / staging), anything else worth knowing."
          },
          "conversation": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "object",
              "properties": {
                "role": {
                  "type": "string"
                },
                "content": {
                  "type": "string"
                }
              },
              "required": [
                "role",
                "content"
              ]
            },
            "description": "Recent turns, oldest first. Only the most recent part is read, so keep it short."
          }
        },
        "required": [
          "action"
        ],
        "additionalProperties": false
      },
      "GuardResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "verdict": {
            "type": "string",
            "enum": [
              "allow",
              "ask",
              "block"
            ],
            "description": "Run it, ask a human, or never run it."
          },
          "p_unsafe": {
            "type": "number",
            "description": "Calibrated probability that the action is unsafe."
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "scores": {
            "type": "object",
            "description": "Nine heads: safe, violation, severity, destructive, exfiltration, injected, approval_policy, blast_radius, args_grounded (probabilities, or {expected, level, probabilities} for ordinal heads)."
          },
          "model": {
            "type": "string"
          },
          "latency_ms": {
            "type": "number"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}