Chat completions

POST /v1/chat/completions

The OpenAI chat completions API. The request and response shapes are theirs; what follows covers what is ours.

curl https://endpoint.pirol.ai/v1/chat/completions \
  -H "Authorization: Bearer $PIROL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "pirol/pirol-ai",
    "messages": [
      {"role": "system", "content": "Answer in one sentence."},
      {"role": "user", "content": "What is an embedding?"}
    ]
  }'

model and messages are required. A request without them is refused with 400.

Streaming

Set stream: true and read server-sent events, exactly as with OpenAI:

curl https://endpoint.pirol.ai/v1/chat/completions \
  -H "Authorization: Bearer $PIROL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "pirol/pirol-ai", "stream": true,
       "messages": [{"role": "user", "content": "Count to five."}]}'
data: {"id":"chatcmpl-…","choices":[{"delta":{"content":"One"}}]}
data: {"id":"chatcmpl-…","choices":[{"delta":{"content":", two"}}]}
data: [DONE]

Tool calls arrive whole. A function call is never split across two chunks, so you can parse arguments as it comes rather than stitching fragments together. Empty arguments arrive as {}, not as an empty string — both paths promise the same thing, so JSON.parse always works.

Response headers

x-endpoint-provider says which provider answered.

Message content

content may be a plain string or a list of blocks:

{"role": "user", "content": [{"type": "text", "text": "What is an embedding?"}]}

Both are treated the same. The list form is what you need for prompt caching, and it behaves identically to the string form everywhere else.