Home / Docs / API reference

/v1/chat/completions

POST /v1/chat/completions

Authentication relay key (bearer)

Request body

FieldTypeDescription
modelstringA model id or an alias from GET /v1/models. Optional: the key's routing decides when absent.
messages requiredarray of objectRequired and non-empty; an empty or missing list is a 400.
messages[].rolestringWho wrote the message: system, user, assistant or tool.
messages[].contentstring or arrayText; the gateway reads the text of a message.
max_completion_tokensnumberUpper bound on the generated tokens, the newer OpenAI name for max_tokens. Send one of the two.
max_tokensnumberUpper bound on the tokens the model may generate for this answer. Send this or max_completion_tokens, not both.
temperaturenumberSampling temperature: lower is more deterministic, higher more varied. Usually between 0 and 2.
streambooleanDefault false.
toolsarrayCarried and forwarded verbatim upstream; never validated here.
response_formatobjectCarried and forwarded verbatim upstream.
reasoning_effortstringThe o-series field; read as the request's effort.

Example request

curl
curl -X POST https://api.massrelay.ai/v1/chat/completions \
  -H "Authorization: Bearer $RELAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"Say hello in one sentence."}]}'
Python
import os, requests

r = requests.post(
    "https://api.massrelay.ai/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['RELAY_KEY']}"},
    json={
        "messages": [
            {
                "role": "user",
                "content": "Say hello in one sentence."
            }
        ]
    },
)
r.raise_for_status()
print(r.json())
TypeScript
const r = await fetch("https://api.massrelay.ai/v1/chat/completions", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.RELAY_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({"messages":[{"role":"user","content":"Say hello in one sentence."}]}),
});
if (!r.ok) throw new Error(`${r.status} ${await r.text()}`);
console.log(await r.json());

Response

FieldTypeDescription
id requiredstringchatcmpl-<decisionId>; the decision id is the key of GET /v1/route/explain/{decisionId}.
object requiredstringThe kind of object returned, for example chat.completion. Always "chat.completion".
created requiredintegerWhen the answer was created, in Unix seconds.
model requiredstringThe model id. In a request it is optional: without it the key's routing chooses.
choices requiredarray of objectThe generated answers; one unless n asks for more.
choices[].indexintegerThe position of this choice in the list.
choices[].messageobjectThe generated message: its role and content.
choices[].message.rolestringWho wrote the message: system, user, assistant or tool. Always "assistant".
choices[].message.contentstringThe text of the message.
choices[].finish_reasonstringWhy generation stopped, for example stop or length.
usage requiredobjectTokens counted for this call.
usage.prompt_tokensnumberTokens in the input.
usage.completion_tokensnumberTokens generated.
usage.total_tokensnumberInput plus generated tokens.
Example response (shape)
{
  "id": "chatcmpl-1f2e3d4c",
  "object": "chat.completion",
  "created": 1761400000,
  "model": "provider/model-id",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello there."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 12,
    "completion_tokens": 6,
    "total_tokens": 18
  }
}

Common errors

  • 401 invalid_api_key
  • 400 the body is not usable
  • 402 out of credit or over a budget
  • 413 the body is over the limit
  • 403 insufficient_scope
  • 429 over the request rate; read Retry-After

Every error has the same JSON shape; see Errors and Limits.

Full schema (JSON)

Request schema

{
  "description": "Every other field is accepted and not read (for example n, stop, top_p, seed are ignored). Fields the gateway does not read are not documented as honoured.",
  "type": "object",
  "required": [
    "messages"
  ],
  "additionalProperties": true,
  "properties": {
    "model": {
      "type": "string",
      "description": "A model id or an alias from GET /v1/models. Optional: the key's routing decides when absent."
    },
    "messages": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "role": {
            "type": "string"
          },
          "content": {
            "description": "Text; the gateway reads the text of a message.",
            "type": [
              "string",
              "array"
            ]
          }
        }
      },
      "description": "Required and non-empty; an empty or missing list is a 400."
    },
    "max_completion_tokens": {
      "type": "number"
    },
    "max_tokens": {
      "type": "number"
    },
    "temperature": {
      "type": "number"
    },
    "stream": {
      "type": "boolean",
      "description": "Default false."
    },
    "tools": {
      "description": "Carried and forwarded verbatim upstream; never validated here.",
      "type": "array"
    },
    "response_format": {
      "description": "Carried and forwarded verbatim upstream.",
      "type": "object",
      "additionalProperties": true
    },
    "reasoning_effort": {
      "type": "string",
      "description": "The o-series field; read as the request's effort."
    }
  }
}

Response schema

{
  "description": "Every other field is accepted and not read (streaming responses are server-sent events and are not described here). Fields the gateway does not read are not documented as honoured.",
  "type": "object",
  "additionalProperties": true,
  "required": [
    "id",
    "object",
    "created",
    "model",
    "choices",
    "usage"
  ],
  "properties": {
    "id": {
      "type": "string",
      "description": "chatcmpl-<decisionId>; the decision id is the key of GET /v1/route/explain/{decisionId}."
    },
    "object": {
      "const": "chat.completion"
    },
    "created": {
      "type": "integer"
    },
    "model": {
      "type": "string"
    },
    "choices": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer"
          },
          "message": {
            "type": "object",
            "properties": {
              "role": {
                "const": "assistant"
              },
              "content": {
                "type": "string"
              }
            }
          },
          "finish_reason": {
            "type": "string"
          }
        }
      }
    },
    "usage": {
      "type": "object",
      "properties": {
        "prompt_tokens": {
          "type": "number"
        },
        "completion_tokens": {
          "type": "number"
        },
        "total_tokens": {
          "type": "number"
        }
      }
    }
  }
}

Every answer carries a decision id; the explain endpoint returns why that model was chosen.