GreenPT Docs

Decisions

Ask Metis yes/no, multiple-choice and score questions about a case, and get a probability for every possible answer.

POST

Metis is named after the Greek figure of wise counsel, whose name means "wise advice".

Give Metis a case, the state, and a set of named questions. It answers each question with a probability for every possible answer: yes or no, one of a set of named options, or a level on an ordered scale. It applies the rules and facts you supply, reads every answer of a request in a single forward pass, and generates no text.

The endpoint speaks the System One API from TypeSafe, so the official TypeSafe SDK works against GreenPT once it points at GreenPT's host and model (see Use the TypeSafe SDK).

API endpoint

POST /v1/systemone

Takes a state and up to 32 questions about it, and returns one answer per question.

Example request

A support ticket and three questions about it: is a live service down, which team should take it, and how urgent is it.

curl https://api.greenpt.ai/v1/systemone \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer sk-your_api_key' \
  --data '{
    "model": "metis",
    "state": "Support ticket #T-4821 from an enterprise customer: \"Since 09:00 every checkout on our web shop fails with error 502, so we cannot take any orders. Our status page shows the payment service as down. Please help, this is costing us sales.\"",
    "questions": {
      "outage": {
        "type": "noul",
        "instructions": "Is a live service down?",
        "criteria": { "true": "a production service is unavailable", "false": "every service is reachable" }
      },
      "team": {
        "type": "choice",
        "instructions": "Which team should take this ticket?",
        "criteria": {
          "billing": "invoices, payments and refunds",
          "technical": "bugs, errors and outages",
          "account": "logins, users and permissions",
          "sales": "plans, pricing and upgrades"
        }
      },
      "urgency": {
        "type": "score",
        "instructions": "How urgent is this ticket?",
        "criteria": ["can wait", "this week", "today", "immediately"]
      }
    }
  }'
import { TypeSafeClient, choice, noul, score } from '@typesafe-ai/sdk';

const client = new TypeSafeClient({
  apiKey: process.env.GREENPT_API_KEY,
  baseURL: 'https://api.greenpt.ai',
  defaultModel: 'metis',
  timeout: 60_000,
});

const { answers, usage } = await client.systemOne({
  state:
    'Support ticket #T-4821 from an enterprise customer: "Since 09:00 every checkout on our ' +
    'web shop fails with error 502, so we cannot take any orders. Our status page shows the ' +
    'payment service as down. Please help, this is costing us sales."',
  questions: {
    outage: noul('Is a live service down?', {
      true: 'a production service is unavailable',
      false: 'every service is reachable',
    }),
    team: choice('Which team should take this ticket?', {
      billing: 'invoices, payments and refunds',
      technical: 'bugs, errors and outages',
      account: 'logins, users and permissions',
      sales: 'plans, pricing and upgrades',
    }),
    urgency: score('How urgent is this ticket?', ['can wait', 'this week', 'today', 'immediately']),
  },
});

console.log(answers.outage.noul); // probability of yes
console.log(answers.team.choice); // 'technical'
console.log(answers.urgency.probabilities); // { '0': ..., '1': ..., '2': ..., '3': ... }
console.log(usage.input_tokens);

Response format

Numbers shortened to four decimals:

{
  "model": "metis",
  "answers": {
    "outage": { "type": "noul", "noul": 0.9533 },
    "team": {
      "type": "choice",
      "choice": "technical",
      "probabilities": {
        "billing": 0.0552,
        "technical": 0.9299,
        "account": 0.0083,
        "sales": 0.0066
      },
      "confidence": 0.9065
    },
    "urgency": {
      "type": "score",
      "score": 2.9415,
      "legend": { "0": "can wait", "1": "this week", "2": "today", "3": "immediately" },
      "probabilities": { "0": 0.0067, "1": 0.0064, "2": 0.0257, "3": 0.9613 },
      "confidence": 0.9415
    }
  },
  "usage": { "input_tokens": 409, "output_tokens": 0 },
  "impact": {
    "inferenceTime": { "total": 72, "unit": "ms" },
    "energy": { "total": 16882, "unit": "Wms" },
    "emissions": { "total": 80, "unit": "ugCO2e" },
    "version": "20250922"
  }
}

Metis puts 0.95 on a live service being down, 0.93 on the technical team and 0.96 on the immediately level. answers has one entry per question, under the id you gave it.

Parameters

ParameterTypeRequiredDescription
modelstringYes"metis".
statestring, object, array or nullYesThe case to decide about: a document, a ticket, a record. Objects and arrays are read as JSON.
questionsobjectYes1 to 32 questions, keyed by an id of 1 to 128 characters. Each answer comes back under its id.

Every question has these fields:

FieldTypeRequiredDescription
type"noul", "choice" or "score"YesThe kind of answer. See Answers.
instructionsstring, object, array or nullNoThe question itself, or the rule to apply. Without it, the question's id is read in its place.
criteriadepends on typeYes, except for noulThe possible answers and what each one means.

What criteria holds per type:

  • noul (yes or no): an optional object { "true": ..., "false": ... } describing what yes and what no mean. Either key may be left out, and so may the whole object.
  • choice (one of a set): an object from option key to description, with 2 to 255 options. The keys are what the answer returns.
  • score (a level on an ordered scale): an array of 2 to 10 level descriptions, lowest first. Level i is the entry at index i.

A description can be text, a JSON object or array, or null. A null description shows the model the option key (for choice) or the level number (for score) in its place. The descriptions are what Metis reads, so a short phrase stating what the option means works better than a bare label.

Numbers in the state, the instructions and the descriptions are read as double-precision floats, so an integer larger than 9,007,199,254,740,992 in magnitude, such as a 64-bit id or an account number, reaches Metis rounded. Send such integers as strings.

A question id, an option key, or a key anywhere inside the state, the instructions or a description may not be __proto__; a request with one answers 422.

Unknown fields are ignored.

Answers

Metis reads the state and every question of a request together, and answers them all in one pass with a probability for every option. The probabilities of one question add up to 1. The questions of a request are decided jointly, so one question can inform the answer to another. Ask the questions about one case in one request, and send a question in a request of its own when its answer must not depend on any other.

noul

{ "type": "noul", "noul": 0.2689 }

noul is the probability of yes. The probability of no is 1 - noul. A noul answer has no confidence field.

choice

{
  "type": "choice",
  "choice": "request_approval",
  "probabilities": { "execute": 0.0561, "request_approval": 0.683, "reduce_size": 0.2083, "reject": 0.0527 },
  "confidence": 0.5773
}

choice is the most likely option; on an exact tie, the first one wins. probabilities has every option, keyed as in criteria.

score

{
  "type": "score",
  "score": 1.9137,
  "legend": { "0": "low", "1": "moderate", "2": "high", "3": "critical" },
  "probabilities": { "0": 0.0527, "1": 0.1432, "2": 0.6418, "3": 0.1623 },
  "confidence": 0.5891
}

score is the expected level, the sum of each level times its probability, so it can fall between levels. legend maps each level to the description you sent, and probabilities holds the probability of each level. For the single most likely level, read the largest entry in probabilities rather than rounding score: the two can differ when the distribution is lopsided.

Confidence

choice and score answers carry a confidence between 0 and 1, computed from the probabilities with a different formula per type, the same ones the System One API uses.

For choice, with n options and p_max the probability of the chosen one:

confidence = (p_max - 1/n) / (1 - 1/n)

It is 0 when every option is equally likely and 1 when one option holds all the probability. In the example, (0.683 - 0.25) / 0.75 = 0.5773.

For score, with n levels numbered 0 to n - 1, p_i the probability of level i, and mode the most likely level (the lowest one on a tie):

confidence  = max(0, 1 - sum(p_i * |i - mode|) / MAD_uniform)
MAD_uniform = mean over all levels i of |i - (n - 1) / 2|

It measures how concentrated the probability is around the most likely level: 1 when one level holds all of it, 0 when it is spread as widely as a uniform distribution or wider. In the example, mode is 2, the weighted distance is 0.0527 * 2 + 0.1432 * 1 + 0.1623 * 1 = 0.4109, and MAD_uniform for four levels is 1, so confidence is 0.5891. Probabilities of [0.5, 0, 0.5] give 0, where the choice formula would give 0.25.

Use confidence or the probabilities to decide when to act on an answer and when to hand the case to a person.

Usage and impact

"usage": { "input_tokens": 606, "output_tokens": 3 }
  • input_tokens is the length of the one prompt Metis reads: the state, every question's instructions and options, and a fixed instruction.
  • output_tokens is 0: Metis generates no tokens.

Billing uses these counts. For the price, call GET /v1/pricing/metis. If any question fails, the whole request fails and nothing is billed.

impact has the same shape as on chat completions and covers the whole request: inferenceTime is the time of its one pass.

Limits

WhatLimit
Questions per request32
Question id1 to 128 characters
Options per choice question2 to 255
Levels per score question2 to 10
Context per request16,384 tokens

The context limit applies to the whole request: the state, every question's instructions and options, plus a fixed instruction and the model's prompt format.

Errors

StatusWhen
400model is not metis. An unknown id, such as the TypeSafe SDK's default jev-latest, answers Unsupported model; a model of another kind, such as glm-5.2, answers Unsupported model type. A body that is not valid JSON answers Malformed JSON in request body.
401The API key is missing or invalid. The body is Unauthorized.
402Your credit balance is spent. The body is {"error": "No remaining credits (EUR)"}.
403The request went to the US endpoint. See Region.
422The body is invalid (invalid_request), or the request does not fit the 16,384-token context (context_length_exceeded).
429Your account's rate limit is spent.
500An internal failure that is not caused by your request, such as an answer from the model that cannot be read. These answer {"error": "Internal server error", "code": "internal_error"}; other internal failures may answer without a code. Retrying meets the same failure.
503The model is at capacity or temporarily degraded. The body is {"error": "Service temporarily degraded. Please retry.", "code": "service_degraded"}.
504The request did not finish within the time limit.

A 422 is JSON with a code to branch on and an error that says what failed:

{ "error": "<what failed>", "code": "invalid_request" }

A 503 comes with a Retry-After: 5 header. Retry after the delay.

Requests are limited per account (shared across all your API keys): 600 requests per 15-minute window, shared across every GreenPT API endpoint. Exceeding it returns 429 Too many requests, please try again later. with RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset and Retry-After response headers.

Use the TypeSafe SDK

The official TypeSafe JavaScript SDK, @typesafe-ai/sdk, sends POST /v1/systemone with a bearer key, which GreenPT accepts. Out of the box it calls TypeSafe's own host and model, so it needs two settings, not one: the base URL and the model. Set them in the environment:

export TYPESAFE_API_KEY=sk-your_api_key   # your GreenPT API key
export TYPESAFE_BASE_URL=https://api.greenpt.ai
export TYPESAFE_DEFAULT_MODEL=metis

or in code, with baseURL and defaultModel as in the example, or per call with model: 'metis' in the request. The base URL has no /v1: the SDK appends /v1/systemone itself.

Set the model as well as the base URL

Changing only the base URL sends TypeSafe's default model, jev-latest, which GreenPT answers with 400 Unsupported model.

  • The SDK gives up on an attempt after 10 seconds by default, and then retries it. A request with many questions over a long state, or one that waits behind other requests, can take longer than that, so every attempt times out. Pass timeout: 60_000 (in milliseconds) or more to the client or the call.
  • It also retries 429 and 5xx responses on its own, and honours Retry-After.
  • client.models.list() expects TypeSafe's response shape and fails against GreenPT. List models with GET /v1/models instead.

Scope

  • Metis applies the rules, policies and facts you put in the state and the instructions. It does not look anything up, and it does not predict prices.
  • It is not legal, tax or investment advice. Treat an answer as an input to your own process, and route uncertain cases to a person.
  • It returns probabilities, not explanations. For the reasoning behind a decision, ask a chat model.
  • It is a general-purpose decision model: routing and classification, policy and rule checks, and choosing a tool or an action.
  • It does not calculate reliably. For a rule that compares amounts, put the computed figures in the state (an order of USD 61,800 against a limit of USD 24,000), not only the inputs they come from.

Region

Metis is served from the EU endpoint only: https://api.greenpt.ai, or https://api.eu.greenpt.ai under its explicit name. The US endpoint answers 403 with {"code": "endpoint_not_available_in_location"}. See Region Inference.

Model and attribution

Metis runs Clef-flash, a decision model by Cloudflare, released under the Apache License 2.0, in its full-precision release. It uses the model's own prompt format and decision head.

Clef-flash is a fine-tune of Qwen3.5-9B by the Qwen team at Alibaba Cloud, licensed under the Apache License 2.0. Its model card reports its evaluation.

metis is GreenPT's id for this endpoint's model: the model behind it can be replaced without the id changing.

On this page