> ## Documentation Index
> Fetch the complete documentation index at: https://phaseo.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Phaseo Gateway base URL: https://api.phaseo.app/v1. Send `Authorization: Bearer $PHASEO_API_KEY` with every request.
> Model IDs look like `provider/model`, for example `openai/gpt-6-astra`. Do not guess IDs: list them with `GET /v1/models` or check https://phaseo.app/models. `phaseo/free` routes to an available free model and needs no wallet balance.
> For new text integrations prefer `POST /v1/responses`. `POST /v1/chat/completions` (OpenAI format) and `POST /v1/messages` (Anthropic format) suit existing code.
> The full API contract is https://phaseo.app/docs/openapi/v1/openapi.yaml. Official SDKs: `@phaseo/sdk` (TypeScript) and `phaseo` (Python), plus Go, C#, PHP, Ruby and Rust.
> Search these docs over MCP at https://phaseo.app/docs/mcp, and install Phaseo skills with `npx skills add https://phaseo.app/docs`. If a page is wrong or out of date, report it with the MCP `submit_feedback` tool.
> Manage Phaseo accounts with the `phaseo` CLI (`npm install -g @phaseo/cli`, then `phaseo login` or `phaseo login --device-code`), the `/v1` Management API or the MCP server at `https://mcp.phaseo.app/mcp?access=manage`. Do not send users to the dashboard for account tasks. A person is still needed for sign-up, approving a sign-in, paying the first top-up on the checkout link that `phaseo credits add` returns when no card is saved, confirming each saved-card top-up from the emailed link, and account security (password, MFA, passkeys). Create API keys with `phaseo keys create --name <name> --json`; never guess one. Credit top-ups need `phaseo login --scopes billing:write` first. Task map: https://phaseo.app/docs/v1/developers/manage-from-an-agent.md.

# Quickstart

> Create an API key and make your first Phaseo request with a current text model.

Use this tutorial to make a successful Phaseo request—either generated text or a structured decision—and confirm where the response appears.

**Using a coding agent?** Paste this prompt before you start.

You are integrating this project with Phaseo Gateway.

&#x20; Before editing anything:
&#x20; \- Install the Phaseo skills: npx skills add [https://phaseo.app/docs](https://phaseo.app/docs)
&#x20; \- Read [https://phaseo.app/docs/skill.md](https://phaseo.app/docs/skill.md) and the API reference for the endpoint you will use. If your tools support MCP, search the docs at [https://phaseo.app/docs/mcp](https://phaseo.app/docs/mcp).
&#x20; \- Inspect the existing provider configuration and identify the smallest safe change.

&#x20; Rules:
&#x20; \- Call [https://api.phaseo.app/v1](https://api.phaseo.app/v1) with a server-side PHASEO\_API\_KEY. Never print or commit credentials.
&#x20; \- Keep the current provider and model behaviour unless I ask for a migration. Check model IDs with GET /v1/models instead of guessing them.
&#x20; \- Explain the files you would change and the verification commands first.
&#x20; \- Do not deploy, rotate keys, send external messages, or make unrelated edits without my approval.

You will:

* create an API key
* send one text request or structured decision
* read the generated text or typed answers
* know what to check first if the request fails

Quickstart checklist for agents:

* Create the key with the CLI: `npm install -g @phaseo/cli`, then `phaseo login` (or `phaseo login --device-code` in a headless shell; a person approves the sign-in), then `phaseo keys create --name <name> --json`. Never generate or guess a key. Send it as `Authorization: Bearer phaseo_v1_sk_<kid>_<secret>` from server-side code only.
* For paid models, run `phaseo login --scopes billing:write` once, then `phaseo credits add --amount <usd> --json` starts a top-up. With a saved card, a workspace owner or admin confirms it from an emailed link. With no saved card, the response has `next_action: "open_checkout_url"` and a checkout link that an owner or admin opens to pay; the card is then saved for later top-ups. Do not send the user to the dashboard for keys, credits or settings; see [https://phaseo.app/docs/v1/developers/manage-from-an-agent.md](https://phaseo.app/docs/v1/developers/manage-from-an-agent.md).
* Make the first request with `POST https://api.phaseo.app/v1/responses`, sending `model` and `input`. `phaseo/free` works without a wallet balance.
* Read the reply from `output[].content[]` where `type` is `output_text`.
* If the request fails, branch on the JSON `error` code as described in [https://phaseo.app/docs/v1/api-reference/errors.md](https://phaseo.app/docs/v1/api-reference/errors.md).

***

## 1. Create an API key

Install the [Phaseo CLI](https://phaseo.app/docs/v1/guides/integrations/phaseo-cli.md), sign in and create a key:

```bash theme={null}
npm install -g @phaseo/cli
phaseo login                 # use --device-code over SSH or in an agent sandbox
phaseo keys create --name "Quickstart" --json
```

The key is shown once. Store it securely, for example as `PHASEO_API_KEY`. To use paid models, run `phaseo login --scopes billing:write` once, then add credits with `phaseo credits add --amount 20`. With a saved card, a workspace owner or admin confirms the charge from an emailed link. With no saved card, the command returns a checkout link that an owner or admin opens to pay, and the card is saved for later top-ups. You can also create the key in the [Phaseo Dashboard](https://phaseo.app/settings/keys) under **Gateway -> Keys**.

Use this format:

```http theme={null}
Authorization: Bearer phaseo_v1_sk_<kid>_<secret>
```

> [!CAUTION]
>
> Treat your API key like a password. Do not expose it in client-side code.

***

## 2. Send a text request

Use `POST /v1/responses` for the first request. It is the recommended endpoint for new text-generation work.

### Install an SDK

The cURL and JavaScript examples need nothing installed. To use the TypeScript or Python SDK, install it first:

```bash npm theme={null}
npm install @phaseo/sdk
```

```bash pnpm theme={null}
pnpm add @phaseo/sdk
```

```bash pip theme={null}
pip install phaseo
```

See the [TypeScript](https://phaseo.app/docs/v1/sdk-reference/typescript/installation.md) and [Python](https://phaseo.app/docs/v1/sdk-reference/python/installation.md) installation pages for runtime requirements and the other SDKs.

### Request

```bash cURL theme={null}
curl https://api.phaseo.app/v1/responses \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-6-astra",
    "input": "Reply with: quickstart works"
  }'
```

```javascript JavaScript theme={null}
const response = await fetch("https://api.phaseo.app/v1/responses", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "openai/gpt-6-astra",
    input: "Reply with: quickstart works",
  }),
});

const data = await response.json();
const assistantText = data.output
  ?.find((item) => item.type === "message")
  ?.content?.find((part) => part.type === "output_text")
  ?.text;

console.log(assistantText);
```

```typescript TypeScript SDK theme={null}
import Phaseo from "@phaseo/sdk";

const client = new Phaseo({ apiKey: process.env.PHASEO_API_KEY! });

const response = await client.generateResponse({
  model: "openai/gpt-6-astra",
  input: "Reply with: quickstart works",
});

const assistantText = response.output
  ?.find((item: any) => item.type === "message")
  ?.content?.find((part: any) => part.type === "output_text")
  ?.text;

console.log(assistantText);
```

```python Python SDK theme={null}
from phaseo import Phaseo

client = Phaseo(api_key="YOUR_API_KEY")

response = client.generate_response(
    {
        "model": "openai/gpt-6-astra",
        "input": "Reply with: quickstart works",
    }
)

assistant_text = next(
    (
        part.get("text")
        for item in response.get("output", [])
        if item.get("type") == "message"
        for part in item.get("content", [])
        if part.get("type") == "output_text"
    ),
    None,
)

print(assistant_text)
```

```go Go SDK theme={null}
package main

import (
  "context"
  "fmt"

  phaseo "github.com/phaseoteam/Phaseo/packages/sdk/sdk-go/v3"
)

func main() {
  client := phaseo.New("YOUR_API_KEY", "https://api.phaseo.app/v1")
  input := map[string]interface{}{
    "role": "user",
    "content": []map[string]interface{}{
      {
        "type": "input_text",
        "text": "Reply with: quickstart works",
      },
    },
  }

  response, err := client.GenerateResponse(context.Background(), phaseo.ResponsesRequest{
    Model: "openai/gpt-6-astra",
    Input: &input,
  })
  if err != nil {
    panic(err)
  }

  fmt.Println(response)
}
```

```csharp C# SDK theme={null}
using PhaseoSdk;
using System.Collections.Generic;

var client = new Phaseo("YOUR_API_KEY");

var response = await client.GenerateResponse(new Dictionary<string, object>
{
    ["model"] = "openai/gpt-6-astra",
    ["input"] = "Reply with: quickstart works",
});

Console.WriteLine(response);
```

```php PHP SDK theme={null}
<?php
require 'vendor/autoload.php';

use Phaseo\Sdk\Phaseo;

$client = new Phaseo(getenv('PHASEO_API_KEY') ?: 'YOUR_API_KEY');

$response = $client->generateResponse([
    'model' => 'openai/gpt-6-astra',
    'input' => 'Reply with: quickstart works',
]);

print_r($response);
```

```ruby Ruby SDK theme={null}
require 'phaseo_sdk'

client = PhaseoSdk::Phaseo.new(api_key: ENV.fetch('PHASEO_API_KEY', 'YOUR_API_KEY'))

response = client.generate_response(
  model: 'openai/gpt-6-astra',
  input: 'Reply with: quickstart works',
)

puts response
```

```java Java SDK theme={null}
// Build from source: https://phaseo.app/docs/v1/sdk-reference/java/installation
import app.phaseo.sdk.Phaseo;
import java.util.Map;

public class Main {
    public static void main(String[] args) throws Exception {
        var client = new Phaseo(System.getenv("PHASEO_API_KEY"));
        var response = client.createResponse(Map.of(
            "model", "openai/gpt-6-astra",
            "input", "Reply with: quickstart works"
        ));
        for (var item : response.path("output")) {
            for (var part : item.path("content")) {
                if ("output_text".equals(part.path("type").asText())) {
                    System.out.println(part.path("text").asText());
                }
            }
        }
    }
}
```

```rust Rust SDK theme={null}
use phaseo::Phaseo;
use serde_json::{json, Value};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Phaseo::from_env()?;
    let response = client.responses(&json!({
        "model": "openai/gpt-6-astra",
        "input": "Reply with: quickstart works"
    }))?;

    let assistant_text = response.body
        .get("output")
        .and_then(Value::as_array)
        .into_iter()
        .flatten()
        .filter(|item| item.get("type").and_then(Value::as_str) == Some("message"))
        .flat_map(|item| {
            item.get("content")
                .and_then(Value::as_array)
                .into_iter()
                .flatten()
        })
        .find_map(|part| {
            (part.get("type").and_then(Value::as_str) == Some("output_text"))
                .then(|| part.get("text").and_then(Value::as_str))
                .flatten()
        })
        .unwrap_or("");

    println!("{assistant_text}");
    Ok(())
}
```

```typescript Vercel AI SDK theme={null}
import { phaseo } from "@phaseo/ai-sdk-provider";
import { generateText } from "ai";

const result = await generateText({
  model: phaseo("openai/gpt-6-astra"),
  prompt: "Reply with: quickstart works",
});

console.log(result.text);
```

### Response

```json theme={null}
{
  "id": "resp_...",
  "object": "response",
  "created_at": 1730000000,
  "status": "completed",
  "completed_at": 1730000001,
  "model": "openai/gpt-6-astra",
  "output": [
    {
      "type": "message",
      "id": "msg_...",
      "status": "completed",
      "role": "assistant",
      "content": [{ "type": "output_text", "text": "quickstart works", "annotations": [] }]
    }
  ],
  "usage": {
    "input_tokens": 9,
    "output_tokens": 3,
    "total_tokens": 12
  },
  "error": null,
  "incomplete_details": null
}
```

For direct Responses API calls, read the assistant reply from `output[].content[]` where `type` is `output_text`.

If you use the Vercel AI SDK, read the reply from `result.text`.

### Stream the reply

To print the reply as it is generated, stream it. Each chunk's `text` holds only the new text, so this loop prints the answer once. Closing events such as `response.output_text.done` have an empty `text`; their full text is in `fullText` (TypeScript) or `full_text` (Python).

```typescript TypeScript SDK theme={null}
import Phaseo from "@phaseo/sdk";

const client = new Phaseo({ apiKey: process.env.PHASEO_API_KEY! });

for await (const chunk of client.streamResponses({
  model: "openai/gpt-6-astra",
  input: "Reply with: quickstart works",
})) {
  if (chunk.text) process.stdout.write(chunk.text);
}
```

```python Python SDK theme={null}
from phaseo import Phaseo

client = Phaseo()  # Uses PHASEO_API_KEY from the environment

for chunk in client.stream_responses(
    {
        "model": "openai/gpt-6-astra",
        "input": "Reply with: quickstart works",
    }
):
    if chunk["text"]:
        print(chunk["text"], end="", flush=True)
print()
```

If the answer prints twice, update the SDK: older releases also put the full text on the closing event.

***

## 3. Make a structured decision

Use the OpenAI-compatible format for new calls: `input` contains text or user messages with inline `input_image` parts, and `questions` is an ordered array of `predicate`, `choice`, or `score` questions. Answers use the same order, with optional names. Boolean choice values remain booleans. Hosted image URLs are unsupported; Luna accepts up to 128 inline images, while other providers impose their own limits. Explicit image detail and `safety_identifier` require an OpenAI route. Existing `state`, named question maps, `noul`, and `criteria` remain supported and return the original answer map. Together Tev requires that legacy format because it returns labels without probabilities. The legacy examples below remain valid.

```json theme={null}
{
  "model": "openai/gpt-6-luna",
  "input": "I was charged twice for my order.",
  "questions": [{
    "type": "choice",
    "name": "department",
    "instructions": "Which department should handle this?",
    "choices": [
      { "value": "billing", "description": "Payments and refunds" },
      { "value": "other", "description": "Everything else" }
    ]
  }]
}
```

Use `POST /v1/decisions` when your application needs typed answers instead of generated text. Phaseo exposes TypeSafe's Jev 1.13 with the model ID `typesafe/jev-1.13.0`. Send a `state` value and one or more named questions; each answer is returned under the same name.

> [!NOTE]
>
> Decisions is a beta capability and may require workspace enablement. Jev 1.13 is billed at \$0.042 per 1M input tokens; output tokens are free.

Run these examples on your server with `PHASEO_API_KEY` set. Never expose a Gateway API key in browser code.

The three question types are:

* `choice` selects one option from a criteria map and returns probabilities and confidence.
* `noul` returns a yes/no probability from 0 to 1.
* `score` evaluates an ordered criteria array and returns a probability-weighted score.

You can mix question types in the same request.

### Request

```bash cURL theme={null}
curl https://api.phaseo.app/v1/decisions \
  -H "Authorization: Bearer $PHASEO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "typesafe/jev-1.13.0",
    "state": {
      "customer_message": "I was charged twice and need help with a refund.",
      "account_tier": "pro",
      "days_waiting": 3
    },
    "questions": {
      "department": {
        "type": "choice",
        "instructions": "Which team should handle this request?",
        "criteria": {
          "billing": "Payments, invoices, refunds, and duplicate charges.",
          "support": "Product usage questions and troubleshooting.",
          "sales": "Upgrades and new accounts."
        }
      },
      "is_urgent": {
        "type": "noul",
        "instructions": "Does this request require urgent handling?",
        "criteria": {
          "true": "The customer is blocked or the issue is time-sensitive.",
          "false": "The request can follow the normal support queue."
        }
      },
      "customer_impact": {
        "type": "score",
        "instructions": "How severe is the customer impact?",
        "criteria": [
          "No impact",
          "Minor inconvenience",
          "Significant impact",
          "Service blocked"
        ]
      }
    }
  }'
```

```javascript JavaScript theme={null}
const response = await fetch("https://api.phaseo.app/v1/decisions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PHASEO_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "typesafe/jev-1.13.0",
    state: {
      customer_message: "I was charged twice and need help with a refund.",
      account_tier: "pro",
      days_waiting: 3,
    },
    questions: {
      department: {
        type: "choice",
        instructions: "Which team should handle this request?",
        criteria: {
          billing: "Payments, invoices, refunds, and duplicate charges.",
          support: "Product usage questions and troubleshooting.",
          sales: "Upgrades and new accounts.",
        },
      },
      is_urgent: {
        type: "noul",
        instructions: "Does this request require urgent handling?",
        criteria: {
          true: "The customer is blocked or the issue is time-sensitive.",
          false: "The request can follow the normal support queue.",
        },
      },
      customer_impact: {
        type: "score",
        instructions: "How severe is the customer impact?",
        criteria: [
          "No impact",
          "Minor inconvenience",
          "Significant impact",
          "Service blocked",
        ],
      },
    },
  }),
});

if (!response.ok) throw new Error(await response.text());
const data = await response.json();
console.log(data.answers);
```

```typescript TypeScript SDK theme={null}
import Phaseo from "@phaseo/sdk";

const client = new Phaseo({ apiKey: process.env.PHASEO_API_KEY! });

const decision = await client.decisions.make({
  model: "typesafe/jev-1.13.0",
  state: {
    customer_message: "I was charged twice and need help with a refund.",
    account_tier: "pro",
    days_waiting: 3,
  },
  questions: {
    department: {
      type: "choice",
      instructions: "Which team should handle this request?",
      criteria: {
        billing: "Payments, invoices, refunds, and duplicate charges.",
        support: "Product usage questions and troubleshooting.",
        sales: "Upgrades and new accounts.",
      },
    },
    is_urgent: {
      type: "noul",
      instructions: "Does this request require urgent handling?",
      criteria: {
        true: "The customer is blocked or the issue is time-sensitive.",
        false: "The request can follow the normal support queue.",
      },
    },
    customer_impact: {
      type: "score",
      instructions: "How severe is the customer impact?",
      criteria: [
        "No impact",
        "Minor inconvenience",
        "Significant impact",
        "Service blocked",
      ],
    },
  },
});

console.log(decision.answers);
```

```python Python SDK theme={null}
from phaseo import Phaseo

client = Phaseo()  # Uses PHASEO_API_KEY from the environment

decision = client.decisions.make(
    {
        "model": "typesafe/jev-1.13.0",
        "state": {
            "customer_message": "I was charged twice and need help with a refund.",
            "account_tier": "pro",
            "days_waiting": 3,
        },
        "questions": {
            "department": {
                "type": "choice",
                "instructions": "Which team should handle this request?",
                "criteria": {
                    "billing": "Payments, invoices, refunds, and duplicate charges.",
                    "support": "Product usage questions and troubleshooting.",
                    "sales": "Upgrades and new accounts.",
                },
            },
            "is_urgent": {
                "type": "noul",
                "instructions": "Does this request require urgent handling?",
                "criteria": {
                    "true": "The customer is blocked or the issue is time-sensitive.",
                    "false": "The request can follow the normal support queue.",
                },
            },
            "customer_impact": {
                "type": "score",
                "instructions": "How severe is the customer impact?",
                "criteria": [
                    "No impact",
                    "Minor inconvenience",
                    "Significant impact",
                    "Service blocked",
                ],
            },
        },
    }
)

print(decision["answers"])
```

### Response

```json theme={null}
{
  "model": "typesafe/jev-1.13.0",
  "answers": {
    "department": {
      "type": "choice",
      "choice": "billing",
      "probabilities": { "billing": 0.91, "support": 0.06, "sales": 0.03 },
      "confidence": 0.89
    },
    "is_urgent": {
      "type": "noul",
      "noul": 0.84
    },
    "customer_impact": {
      "type": "score",
      "score": 2.4,
      "legend": {
        "0": "No impact",
        "1": "Minor inconvenience",
        "2": "Significant impact",
        "3": "Service blocked"
      },
      "probabilities": { "0": 0.02, "1": 0.12, "2": 0.61, "3": 0.25 },
      "confidence": 0.76
    }
  },
  "usage": {
    "input_tokens": 42,
    "output_tokens": 18,
    "total_tokens": 60
  }
}
```

Read the typed value from the answer that matches your question ID—for example, `answers.department.choice`, `answers.is_urgent.noul`, or `answers.customer_impact.score`. Use `confidence` and `probabilities` when you need to set an application threshold instead of branching on the value alone.

See the [Decisions API reference](https://phaseo.app/docs/v1/api-reference/endpoint/decisions.md) for the full request and response contract, and TypeSafe's [Jev model reference](https://docs.typesafe.ai/models) for model behavior and upstream limits.

***

## 4. Troubleshoot the first request

* `401`: check the API key and `Authorization` header.
* `400`: check the request body and model ID.
* `402`: switch to a `:free` model or add credits before using a paid model.
* `429` or `5xx`: retry with exponential backoff.

Use the [Error Handling reference](https://phaseo.app/docs/v1/api-reference/errors.md) to debug quickly.

***

## 5. If you are building video

Video generation is async. Create a job first, then poll or subscribe for the result.

1. Create a job with `POST /v1/videos`.
2. Poll status with `GET /v1/videos/{video_id}` until completed.
3. Download content from `GET /v1/videos/{video_id}/content`.

```bash theme={null}
# Create
curl https://api.phaseo.app/v1/videos \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<video-model-id>",
    "prompt": "A cinematic sunrise over a mountain lake"
  }'

# Poll status
curl https://api.phaseo.app/v1/videos/VIDEO_ID \
  -H "Authorization: Bearer YOUR_API_KEY"
```

***

## 6. Keep building

> [card] [Integrating with the Gateway](https://phaseo.app/docs/v1/developers/integrating-with-the-gateway.md)
>
> Production integration patterns and endpoint selection.

> [card] [API Reference](https://phaseo.app/docs/v1/api-reference/introduction.md)
>
> Full request and response docs for every endpoint.

> [card] [Examples](https://phaseo.app/docs/v1/guides/examples.md)
>
> More complete request samples for common workflows.

> [card] [Support](https://phaseo.app/help)
>
> Get help with debugging, routing, and model behavior.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.