> ## 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.

# Support triage with presets and guardrail-aware failures

> Run a preset-driven support triage agent that returns strict JSON, pauses high-risk cases for review, and keeps guardrail failures visible.

Use this recipe when one support workflow should:

* inherit routing and prompt defaults from a dashboard preset
* return strict structured output
* pause risky cases for human review
* keep gateway failures visible in logs instead of hiding them inside agent code

## 1. Start with a preset

Create a preset such as `support-triage` that owns:

* the default routed model or router target
* provider preferences
* the support-system prompt
* stable decoding parameters

That keeps the agent code focused on workflow control instead of duplicating request policy.

## 2. Define a narrow triage contract

```ts theme={null}
type SupportTriageDecision = {
  queue: "billing" | "reliability" | "product" | "security";
  severity: "low" | "medium" | "high";
  needsHumanReview: boolean;
  summary: string;
};
```

Keep the first output shape small enough that operators can inspect it quickly.

## 3. Create the preset-driven agent

```ts theme={null}
import { createAgent } from "@phaseo/agent-sdk";

const supportTriageAgent = createAgent<string, SupportTriageDecision>({
  id: "support-triage-agent",
  preset: "support-triage",
  parseOutput(text) {
    return JSON.parse(text) as SupportTriageDecision;
  },
  humanReview: ({ parsedOutput }) =>
    parsedOutput?.needsHumanReview
      ? {
          reason: "support_triage_review_required",
          payload: parsedOutput,
        }
      : null,
});
```

The SDK resolves `preset: "support-triage"` to the gateway alias form `@support-triage`.

## 4. Configure the gateway-backed adapter

Use adapter defaults for the parts that should stay fixed across every triage run:

```ts theme={null}
import {
  createGatewayAgentClient,
} from "@phaseo/agent-sdk";

const client = createGatewayAgentClient({
  clientOptions: {
    apiKey: process.env.PHASEO_API_KEY!,
  },
  responseFormat: {
    type: "json_schema",
    name: "support_triage_decision",
    schema: {
      type: "object",
      properties: {
        queue: {
          type: "string",
          enum: ["billing", "reliability", "product", "security"],
        },
        severity: {
          type: "string",
          enum: ["low", "medium", "high"],
        },
        needsHumanReview: { type: "boolean" },
        summary: { type: "string" },
      },
      required: ["queue", "severity", "needsHumanReview", "summary"],
      additionalProperties: false,
    },
  },
  plugins: [{ id: "response-healing", mode: "strict" }],
});
```

## 5. Run the workflow with bounded retries

```ts theme={null}
const result = await supportTriageAgent.run({
  input: "Customer says webhook deliveries failed overnight and asks whether data was lost.",
  client,
  modelRetry: {
    maxRetries: 2,
    backoffMs: 250,
  },
  onEvent(event) {
    console.log(event.type, event.runId, event.attempt);
  },
});
```

If the run pauses for review, resume it explicitly:

```ts theme={null}
if (result.run.status === "waiting_for_human") {
  const continued = await supportTriageAgent.continueRun({
    run: result,
    client,
    humanInput: "Approved. Finalize the triage decision.",
  });

  console.log(continued.output);
}
```

## 6. Treat gateway failures as operational events

Do not hide failures by swallowing them inside the agent callback chain.

Instead, catch `AgentGatewayError` explicitly:

```ts theme={null}
import { AgentGatewayError } from "@phaseo/agent-sdk";

try {
  await supportTriageAgent.run({
    input: "Customer says webhook deliveries failed overnight and asks whether data was lost.",
    client,
    store,
  });
} catch (error) {
  if (error instanceof AgentGatewayError) {
    console.error("Gateway request failed", {
      status: error.status,
      requestId: error.requestId,
      generationId: error.generationId,
      reason: error.reason,
      providerFailureDiagnostics: error.providerFailureDiagnostics,
      routingDiagnostics: error.routingDiagnostics,
    });
  }
  throw error;
}
```

Then:

1. let the runtime persist `failed` run and step state
2. inspect `loaded.run.errorDetails` or `loaded.steps[n].errorDetails` on later recovery paths when the original exception is no longer in memory
3. inspect the request details for:
   * guardrail enforcement
   * routing details
   * plugin execution
   * request ids and provider data

That is especially important when:

* a guardrail blocks the request
* the preset allowlist rejects the requested model
* provider credentials or enablement filters remove the routed candidate set
* response healing fails to recover schema-valid JSON

## 7. What to verify

After one successful run and one intentionally risky run, confirm:

* the request detail view shows the preset-driven request target
* high-risk cases pause in `waiting_for_human`
* retrying model steps persist `modelAttempts`
* guardrail or preset failures remain visible in request details, not hidden inside agent exceptions

## Related guides

* [TypeScript Agent SDK](https://phaseo.app/docs/v1/sdk-reference/typescript/agent-sdk.md)
* [Presets](https://phaseo.app/docs/v1/guides/presets.md)
* [Routing and fallbacks](https://phaseo.app/docs/v1/guides/routing-and-fallbacks.md)
* [Validate structured outputs](https://phaseo.app/docs/v1/guides/structured-outputs.md)
* [Roll out presets and debug routing](https://phaseo.app/docs/v1/cookbook/preset-rollout-and-routing-debug.md)
* [Recover malformed structured JSON](https://phaseo.app/docs/v1/cookbook/response-healing-for-structured-json.md)
* [Build a durable agent loop in TypeScript](https://phaseo.app/docs/v1/cookbook/agent-sdk-durable-loop.md)


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