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

# Using the Guarded Responses API

> Build with the Gloo AI Responses API: guardrails by default, values-aligned traditions, SSE streaming, and vision — with examples in six languages.

This guide shows how to use the Gloo AI Responses API. You'll build four working examples: a basic guarded response with a theological tradition, instructions with multi-turn input, streaming, and vision (image input).

<Info>
  **Why Responses?** The Responses API is the recommended way to build on Gloo AI. It uses the OpenAI-compatible Responses request shape and is **guarded by default** — every request runs the same guardrails, values-aligned `tradition` responses, and output moderation that power [Completions V2](/api-guides/completions-v2), with no extra configuration.
</Info>

## Prerequisites

Before starting, ensure you have:

* A Gloo AI Studio account
* Your API key from the [API Credentials page](/studio/manage-api-credentials)
* **Authentication setup** - Complete the [Authentication Tutorial](/tutorials/authentication) first

## Understanding the Responses API

All requests go to a single guarded endpoint:

**POST** `/ai/v2/guarded/responses`

### Key Request Fields

| Parameter | Description |
| - | - |
| `model` | The exact model ID. The Responses API is pinned-model only — no auto-routing or `model_family`. |
| `input` | A plain string, or an array of `{role, content}` items for multi-turn conversations. |
| `instructions` | Optional system-level guidance. Replaces the Completions `system` role message. |
| `tradition` | Optional theological tradition for values-aligned answers (e.g. `"evangelical"`). |
| `stream` | Set `true` to receive the response as Server-Sent Events. |
| `max_output_tokens` | Optional cap on response length. |

Answers come back as a typed `output[]` array. See [Example 1's "Understanding the Response"](#understanding-the-response) for the full shape and how to extract the text.

### Guardrail Behavior

**Values-aligned static answers arrive as normal messages.** When guardrails intervene with a curated, tradition-appropriate answer, it comes back as a regular `message` item in `output[]` — identical shape to a model answer. Your client needs no special handling; just extract the text as usual.

**Hard blocks return 403.** Requests that guardrails reject outright return an HTTP `403`:

```json theme={null}
{
  "detail": {
    "message": "Your request was rejected as it violates content policies.",
    "type": "content_policy_violation",
    "code": "content_policy_violation",
    "retryable": false
  }
}
```

A 403 is not a transient failure — don't blindly retry. Adjust the request content instead.

Model text output also passes through Gloo's output-moderation layer before it reaches you — for the full pipeline, see [Guardrails and values](/api-guides/responses#guardrails-and-values) in the Responses API Guide.

See the [Responses API Guide](/api-guides/responses) for complete endpoint documentation.

## Moving from Completions

If you already use [Completions V2](/api-guides/completions-v2), the migration is mostly a rename:

| Completions V2 | Responses API |
| :- | :- |
| `POST /ai/v2/guarded/chat/completions` | `POST /ai/v2/guarded/responses` |
| `messages` | `input` |
| `system` role message | `instructions` |
| `max_tokens` | `max_output_tokens` |
| `choices[].message.content` (string) | `output[]` (typed items) |
| `tradition` | `tradition` (unchanged) |

<Note>
  Auto-routing and `model_family` selection remain [Completions V2](/api-guides/completions-v2) features. The Responses API is pinned-model only — you specify an exact `model` on every request.
</Note>

***

## Example 1: Basic Guarded Response with Tradition

Send a plain-string `input` and a `tradition` to get a values-aligned answer:

<CodeGroup>
  ```python Python theme={null}
  import os
  import requests
  from dotenv import load_dotenv

  load_dotenv()

  API_KEY = os.getenv("GLOO_API_KEY", "YOUR_API_KEY")
  API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses"

  def make_basic_response(message, tradition="evangelical"):
      """Example 1: Basic guarded response with a theological tradition."""
      headers = {
          "Authorization": f"Bearer {API_KEY}",
          "Content-Type": "application/json",
      }
      payload = {
          "model": "gloo-anthropic-claude-sonnet-4.6",
          "input": message,
          "tradition": tradition,
          "max_output_tokens": 256,
      }
      response = requests.post(API_URL, headers=headers, json=payload)
      response.raise_for_status()
      return response.json()

  result = make_basic_response("How can our small group support a grieving member?")
  print(result)
  ```

  ```javascript JavaScript theme={null}
  require("dotenv").config();

  const API_KEY = process.env.GLOO_API_KEY || "YOUR_API_KEY";
  const API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses";

  async function makeBasicResponse(message, tradition = "evangelical") {
    const payload = {
      model: "gloo-anthropic-claude-sonnet-4.6",
      input: message,
      tradition: tradition,
      max_output_tokens: 256,
    };
    const response = await fetch(API_URL, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(payload),
    });
    if (!response.ok) {
      throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
    }
    return response.json();
  }

  makeBasicResponse("How can our small group support a grieving member?").then(console.log);
  ```

  ```typescript TypeScript theme={null}
  import { config } from "dotenv";

  config();

  const API_KEY = process.env.GLOO_API_KEY || "YOUR_API_KEY";
  const API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses";

  async function makeBasicResponse(message: string, tradition: string = "evangelical"): Promise<any> {
    const payload = {
      model: "gloo-anthropic-claude-sonnet-4.6",
      input: message,
      tradition: tradition,
      max_output_tokens: 256,
    };
    const response = await fetch(API_URL, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(payload),
    });
    if (!response.ok) {
      throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
    }
    return response.json();
  }

  makeBasicResponse("How can our small group support a grieving member?").then(console.log);
  ```

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

  $dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
  $dotenv->safeLoad();

  $apiKey = $_ENV['GLOO_API_KEY'] ?? getenv('GLOO_API_KEY') ?: 'YOUR_API_KEY';
  $apiUrl = 'https://platform.ai.gloo.com/ai/v2/guarded/responses';

  function makeBasicResponse(string $message, string $tradition = 'evangelical'): array {
      global $apiKey, $apiUrl;
      $payload = json_encode([
          'model' => 'gloo-anthropic-claude-sonnet-4.6',
          'input' => $message,
          'tradition' => $tradition,
          'max_output_tokens' => 256,
      ]);
      $ch = curl_init($apiUrl);
      curl_setopt_array($ch, [
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_POST => true,
          CURLOPT_POSTFIELDS => $payload,
          CURLOPT_HTTPHEADER => [
              'Authorization: Bearer ' . $apiKey,
              'Content-Type: application/json',
          ],
      ]);
      $body = curl_exec($ch);
      if (curl_errno($ch)) {
          $error = curl_error($ch);
          curl_close($ch);
          throw new RuntimeException('API request failed: ' . $error);
      }
      $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
      curl_close($ch);
      if ($status >= 400) {
          throw new RuntimeException('API request failed with status ' . $status . ': ' . $body);
      }
      return json_decode($body, true);
  }

  print_r(makeBasicResponse('How can our small group support a grieving member?'));
  ?>
  ```

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

  import (
  	"bytes"
  	"encoding/json"
  	"fmt"
  	"io"
  	"net/http"
  	"os"
  )

  const apiURL = "https://platform.ai.gloo.com/ai/v2/guarded/responses"

  func makeBasicResponse(message, tradition string) (map[string]interface{}, error) {
  	payload := map[string]interface{}{
  		"model":             "gloo-anthropic-claude-sonnet-4.6",
  		"input":             message,
  		"tradition":         tradition,
  		"max_output_tokens": 256,
  	}
  	body, _ := json.Marshal(payload)

  	req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(body))
  	if err != nil {
  		return nil, err
  	}
  	req.Header.Add("Authorization", "Bearer "+os.Getenv("GLOO_API_KEY"))
  	req.Header.Add("Content-Type", "application/json")

  	resp, err := http.DefaultClient.Do(req)
  	if err != nil {
  		return nil, err
  	}
  	defer resp.Body.Close()

  	respBody, _ := io.ReadAll(resp.Body)
  	if resp.StatusCode != http.StatusOK {
  		return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(respBody))
  	}

  	var result map[string]interface{}
  	json.Unmarshal(respBody, &result)
  	return result, nil
  }

  func main() {
  	result, err := makeBasicResponse("How can our small group support a grieving member?", "evangelical")
  	if err != nil {
  		fmt.Println("Error:", err)
  		return
  	}
  	out, _ := json.MarshalIndent(result, "", "  ")
  	fmt.Println(string(out))
  }
  ```

  ```java Java theme={null}
  import com.google.gson.Gson;
  import java.io.IOException;
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;
  import java.util.HashMap;
  import java.util.Map;

  public class BasicResponse {

      private static final String API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses";
      private static final String API_KEY = System.getenv().getOrDefault("GLOO_API_KEY", "YOUR_API_KEY");
      private static final Gson gson = new Gson();

      public static Map<String, Object> makeBasicResponse(String message, String tradition) throws Exception {
          Map<String, Object> payload = new HashMap<>();
          payload.put("model", "gloo-anthropic-claude-sonnet-4.6");
          payload.put("input", message);
          payload.put("tradition", tradition);
          payload.put("max_output_tokens", 256);

          HttpRequest request = HttpRequest.newBuilder()
                  .uri(URI.create(API_URL))
                  .header("Content-Type", "application/json")
                  .header("Authorization", "Bearer " + API_KEY)
                  .POST(HttpRequest.BodyPublishers.ofString(gson.toJson(payload)))
                  .build();

          HttpResponse<String> response = HttpClient.newHttpClient()
                  .send(request, HttpResponse.BodyHandlers.ofString());
          if (response.statusCode() != 200) {
              throw new IOException("API request failed with status " + response.statusCode() + ": " + response.body());
          }
          return gson.fromJson(response.body(), Map.class);
      }

      public static void main(String[] args) throws Exception {
          Map<String, Object> result = makeBasicResponse(
              "How can our small group support a grieving member?", "evangelical");
          System.out.println(gson.toJson(result));
      }
  }
  ```
</CodeGroup>

### Understanding the Response

The answer comes back as a typed `output[]` array — a `message` item whose `content[]` holds `output_text` parts:

```json theme={null}
{
  "id": "resp_...",
  "object": "response",
  "model": "gloo-anthropic-claude-sonnet-4.6",
  "output": [
    {
      "type": "message",
      "role": "assistant",
      "content": [
        { "type": "output_text", "text": "Grief support starts with presence..." }
      ]
    }
  ],
  "usage": { "input_tokens": 24, "output_tokens": 38, "total_tokens": 62 }
}
```

To extract the text, walk `output[]` for `message` items and join their `output_text` parts — the cookbook samples do exactly this in an `extractText` helper.

### What You'll See

Running the full sample prints one block per example, then the final banner:

```text theme={null}
=== Gloo AI Guarded Responses API Test ===

Example 1: Basic Guarded Response (tradition=evangelical)
Testing: How can our small group support a grieving member?
   Model used: gloo-anthropic-claude-sonnet-4.6
   Response: I'm so sorry your group is walking through this loss. A few ways to sup...
   Usage: {"input_tokens": 24, "output_tokens": 38, "total_tokens": 62}
   ✓ Example 1 passed
```

***

## Example 2: Instructions + Multi-Turn Input

Use `instructions` for system-level guidance and pass the conversation as an `input` array. The history alternates user → assistant → user (three turns), so the model continues the conversation with full context:

<CodeGroup>
  ```python Python theme={null}
  import os
  import requests
  from dotenv import load_dotenv

  load_dotenv()

  API_KEY = os.getenv("GLOO_API_KEY", "YOUR_API_KEY")
  API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses"

  def make_instructions_response(instructions, input_items):
      """Example 2: Instructions plus multi-turn input array (alternating user/assistant roles)."""
      headers = {
          "Authorization": f"Bearer {API_KEY}",
          "Content-Type": "application/json",
      }
      payload = {
          "model": "gloo-anthropic-claude-sonnet-4.6",
          "instructions": instructions,
          "input": input_items,
          "max_output_tokens": 256,
      }
      response = requests.post(API_URL, headers=headers, json=payload)
      response.raise_for_status()
      return response.json()

  result = make_instructions_response(
      "You are a compassionate pastoral assistant. Keep answers brief and warm.",
      [
          {
              "role": "user",
              "content": "I've been asked to lead a grief support group at church. Where do I start?",
          },
          {
              "role": "assistant",
              "content": "That's a meaningful calling. Start with prayerful preparation — ask God to prepare your own heart before you prepare the room.",
          },
          {
              "role": "user",
              "content": "What should I do in the very first meeting?",
          },
      ],
  )
  print(result)
  ```

  ```javascript JavaScript theme={null}
  require("dotenv").config();

  const API_KEY = process.env.GLOO_API_KEY || "YOUR_API_KEY";
  const API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses";

  async function makeInstructionsResponse(instructions, inputItems) {
    const payload = {
      model: "gloo-anthropic-claude-sonnet-4.6",
      instructions: instructions,
      input: inputItems,
      max_output_tokens: 256,
    };
    const response = await fetch(API_URL, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(payload),
    });
    if (!response.ok) {
      throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
    }
    return response.json();
  }

  makeInstructionsResponse(
    "You are a compassionate pastoral assistant. Keep answers brief and warm.",
    [
      {
        role: "user",
        content: "I've been asked to lead a grief support group at church. Where do I start?",
      },
      {
        role: "assistant",
        content: "That's a meaningful calling. Start with prayerful preparation — ask God to prepare your own heart before you prepare the room.",
      },
      {
        role: "user",
        content: "What should I do in the very first meeting?",
      },
    ]
  ).then(console.log);
  ```

  ```typescript TypeScript theme={null}
  import { config } from "dotenv";

  config();

  const API_KEY = process.env.GLOO_API_KEY || "YOUR_API_KEY";
  const API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses";

  async function makeInstructionsResponse(instructions: string, inputItems: any[]): Promise<any> {
    const payload = {
      model: "gloo-anthropic-claude-sonnet-4.6",
      instructions: instructions,
      input: inputItems,
      max_output_tokens: 256,
    };
    const response = await fetch(API_URL, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(payload),
    });
    if (!response.ok) {
      throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
    }
    return response.json();
  }

  makeInstructionsResponse(
    "You are a compassionate pastoral assistant. Keep answers brief and warm.",
    [
      {
        role: "user",
        content: "I've been asked to lead a grief support group at church. Where do I start?",
      },
      {
        role: "assistant",
        content: "That's a meaningful calling. Start with prayerful preparation — ask God to prepare your own heart before you prepare the room.",
      },
      {
        role: "user",
        content: "What should I do in the very first meeting?",
      },
    ]
  ).then(console.log);
  ```

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

  $dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
  $dotenv->safeLoad();

  $apiKey = $_ENV['GLOO_API_KEY'] ?? getenv('GLOO_API_KEY') ?: 'YOUR_API_KEY';
  $apiUrl = 'https://platform.ai.gloo.com/ai/v2/guarded/responses';

  function makeInstructionsResponse(string $instructions, array $inputItems): array {
      global $apiKey, $apiUrl;
      $payload = json_encode([
          'model' => 'gloo-anthropic-claude-sonnet-4.6',
          'instructions' => $instructions,
          'input' => $inputItems,
          'max_output_tokens' => 256,
      ]);
      $ch = curl_init($apiUrl);
      curl_setopt_array($ch, [
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_POST => true,
          CURLOPT_POSTFIELDS => $payload,
          CURLOPT_HTTPHEADER => [
              'Authorization: Bearer ' . $apiKey,
              'Content-Type: application/json',
          ],
      ]);
      $body = curl_exec($ch);
      if (curl_errno($ch)) {
          $error = curl_error($ch);
          curl_close($ch);
          throw new RuntimeException('API request failed: ' . $error);
      }
      $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
      curl_close($ch);
      if ($status >= 400) {
          throw new RuntimeException('API request failed with status ' . $status . ': ' . $body);
      }
      return json_decode($body, true);
  }

  print_r(makeInstructionsResponse(
      'You are a compassionate pastoral assistant. Keep answers brief and warm.',
      [
          ['role' => 'user', 'content' => "I've been asked to lead a grief support group at church. Where do I start?"],
          ['role' => 'assistant', 'content' => "That's a meaningful calling. Start with prayerful preparation — ask God to prepare your own heart before you prepare the room."],
          ['role' => 'user', 'content' => 'What should I do in the very first meeting?'],
      ]
  ));
  ?>
  ```

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

  import (
  	"bytes"
  	"encoding/json"
  	"fmt"
  	"io"
  	"net/http"
  	"os"
  )

  const apiURL = "https://platform.ai.gloo.com/ai/v2/guarded/responses"

  func makeInstructionsResponse(instructions string, inputItems []map[string]interface{}) (map[string]interface{}, error) {
  	payload := map[string]interface{}{
  		"model":             "gloo-anthropic-claude-sonnet-4.6",
  		"instructions":      instructions,
  		"input":             inputItems,
  		"max_output_tokens": 256,
  	}
  	body, _ := json.Marshal(payload)

  	req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(body))
  	if err != nil {
  		return nil, err
  	}
  	req.Header.Add("Authorization", "Bearer "+os.Getenv("GLOO_API_KEY"))
  	req.Header.Add("Content-Type", "application/json")

  	resp, err := http.DefaultClient.Do(req)
  	if err != nil {
  		return nil, err
  	}
  	defer resp.Body.Close()

  	respBody, _ := io.ReadAll(resp.Body)
  	if resp.StatusCode != http.StatusOK {
  		return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(respBody))
  	}

  	var result map[string]interface{}
  	json.Unmarshal(respBody, &result)
  	return result, nil
  }

  func main() {
  	result, err := makeInstructionsResponse(
  		"You are a compassionate pastoral assistant. Keep answers brief and warm.",
  		[]map[string]interface{}{
  			{"role": "user", "content": "I've been asked to lead a grief support group at church. Where do I start?"},
  			{"role": "assistant", "content": "That's a meaningful calling. Start with prayerful preparation — ask God to prepare your own heart before you prepare the room."},
  			{"role": "user", "content": "What should I do in the very first meeting?"},
  		},
  	)
  	if err != nil {
  		fmt.Println("Error:", err)
  		return
  	}
  	out, _ := json.MarshalIndent(result, "", "  ")
  	fmt.Println(string(out))
  }
  ```

  ```java Java theme={null}
  import com.google.gson.Gson;
  import java.io.IOException;
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;

  public class InstructionsResponse {

      private static final String API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses";
      private static final String API_KEY = System.getenv().getOrDefault("GLOO_API_KEY", "YOUR_API_KEY");
      private static final Gson gson = new Gson();

      public static Map<String, Object> makeInstructionsResponse(String instructions, List<Map<String, Object>> inputItems) throws Exception {
          Map<String, Object> payload = new HashMap<>();
          payload.put("model", "gloo-anthropic-claude-sonnet-4.6");
          payload.put("instructions", instructions);
          payload.put("input", inputItems);
          payload.put("max_output_tokens", 256);

          HttpRequest request = HttpRequest.newBuilder()
                  .uri(URI.create(API_URL))
                  .header("Content-Type", "application/json")
                  .header("Authorization", "Bearer " + API_KEY)
                  .POST(HttpRequest.BodyPublishers.ofString(gson.toJson(payload)))
                  .build();

          HttpResponse<String> response = HttpClient.newHttpClient()
                  .send(request, HttpResponse.BodyHandlers.ofString());
          if (response.statusCode() != 200) {
              throw new IOException("API request failed with status " + response.statusCode() + ": " + response.body());
          }
          return gson.fromJson(response.body(), Map.class);
      }

      public static void main(String[] args) throws Exception {
          Map<String, Object> result = makeInstructionsResponse(
              "You are a compassionate pastoral assistant. Keep answers brief and warm.",
              List.of(
                  Map.of("role", "user", "content", "I've been asked to lead a grief support group at church. Where do I start?"),
                  Map.of("role", "assistant", "content", "That's a meaningful calling. Start with prayerful preparation — ask God to prepare your own heart before you prepare the room."),
                  Map.of("role", "user", "content", "What should I do in the very first meeting?")
              ));
          System.out.println(gson.toJson(result));
      }
  }
  ```
</CodeGroup>

### What You'll See

The reply arrives as the same `message` item in `output[]` shown in Example 1, continuing the conversation.

Running the full sample prints the second block:

```text theme={null}
Example 2: Instructions + Multi-turn Input
Testing: three-turn conversation (user → assistant → user) with pastoral-care instructions
   Model used: gloo-anthropic-claude-sonnet-4.6
   Response: Start with introductions and a simple opening question. Invite each member to share...
   Usage: {"input_tokens": 87, "output_tokens": 41, "total_tokens": 128}
   ✓ Example 2 passed
```

***

## Example 3: Streaming (SSE)

Set `"stream": true` to receive the response as Server-Sent Events.

<CodeGroup>
  ```python Python theme={null}
  import json
  import os
  import requests
  from dotenv import load_dotenv

  load_dotenv()

  API_KEY = os.getenv("GLOO_API_KEY", "YOUR_API_KEY")
  API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses"

  def stream_response(message):
      """Example 3: Streaming via SSE; returns (accumulated_text, usage)."""
      headers = {
          "Authorization": f"Bearer {API_KEY}",
          "Content-Type": "application/json",
      }
      payload = {
          "model": "gloo-anthropic-claude-sonnet-4.6",
          "input": message,
          "stream": True,
          "max_output_tokens": 256,
      }

      response = requests.post(API_URL, headers=headers, json=payload, stream=True)
      response.raise_for_status()

      text_parts = []
      done_parts = []
      usage = None
      event = None

      # Each data: payload is the flattened event object itself; lifecycle
      # events are ignored, and response.completed carries usage but no output[].
      for line in response.iter_lines(decode_unicode=True):
          if line is None:
              continue
          if line.startswith("event:"):
              event = line[len("event:"):].strip()
              continue
          if not line.startswith("data:"):
              continue

          data = json.loads(line[len("data:"):].strip())

          if event == "response.output_text.delta":
              delta = data.get("delta", "")
              print(delta, end="", flush=True)
              text_parts.append(delta)
          elif event == "response.output_text.done":
              done_parts.append(data.get("text", ""))
          elif event == "response.completed":
              final = data.get("response") or data
              usage = final.get("usage")
          elif event is not None and event.startswith("response."):
              pass  # lifecycle events — ignore
          else:
              raise RuntimeError(f"Stream terminated by event '{event}': {data}")

          event = None

      print()
      text = "".join(text_parts) or "".join(done_parts)
      return text, usage

  text, usage = stream_response("Write a one-paragraph prayer for a new season of ministry.")
  print(f"Streamed text ({len(text)} chars), usage: {usage}")
  ```

  ```javascript JavaScript theme={null}
  require("dotenv").config();

  const API_KEY = process.env.GLOO_API_KEY || "YOUR_API_KEY";
  const API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses";

  async function streamResponse(message) {
    const payload = {
      model: "gloo-anthropic-claude-sonnet-4.6",
      input: message,
      stream: true,
      max_output_tokens: 256,
    };

    const response = await fetch(API_URL, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(payload),
    });
    if (!response.ok) {
      throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
    }

    const textParts = [];
    const doneParts = [];
    let usage = null;

    // Each data: payload is the flattened event object itself; lifecycle
    // events are ignored, and response.completed carries usage but no output[].
    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let buffer = "";
    let event = null;

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      buffer += decoder.decode(value, { stream: true });
      const lines = buffer.split("\n");
      buffer = lines.pop() ?? ""; // keep the incomplete last line

      for (const rawLine of lines) {
        const line = rawLine.replace(/\r$/, "");
        if (line.startsWith("event:")) {
          event = line.slice("event:".length).trim();
          continue;
        }
        if (!line.startsWith("data:")) continue;

        const data = JSON.parse(line.slice("data:".length).trim());

        if (event === "response.output_text.delta") {
          const delta = data.delta || "";
          process.stdout.write(delta);
          textParts.push(delta);
        } else if (event === "response.output_text.done") {
          doneParts.push(data.text || "");
        } else if (event === "response.completed") {
          const final = data.response || data;
          usage = final.usage || null;
        } else if (event !== null && event.startsWith("response.")) {
          // lifecycle events — ignore
        } else {
          throw new Error(`Stream terminated by event '${event}': ${JSON.stringify(data)}`);
        }
        event = null;
      }
    }

    process.stdout.write("\n");
    return { text: textParts.join("") || doneParts.join(""), usage };
  }

  streamResponse("Write a one-paragraph prayer for a new season of ministry.").then(
    ({ text, usage }) => console.log(`Streamed text (${text.length} chars), usage: ${JSON.stringify(usage)}`)
  );
  ```

  ```typescript TypeScript theme={null}
  import { config } from "dotenv";

  config();

  const API_KEY = process.env.GLOO_API_KEY || "YOUR_API_KEY";
  const API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses";

  async function streamResponse(message: string): Promise<{ text: string; usage: any }> {
    const payload = {
      model: "gloo-anthropic-claude-sonnet-4.6",
      input: message,
      stream: true,
      max_output_tokens: 256,
    };

    const response = await fetch(API_URL, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(payload),
    });
    if (!response.ok) {
      throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
    }

    const textParts: string[] = [];
    const doneParts: string[] = [];
    let usage: any = null;

    // Each data: payload is the flattened event object itself; lifecycle
    // events are ignored, and response.completed carries usage but no output[].
    const reader = response.body!.getReader();
    const decoder = new TextDecoder();
    let buffer = "";
    let event: string | null = null;

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      buffer += decoder.decode(value, { stream: true });
      const lines = buffer.split("\n");
      buffer = lines.pop() ?? ""; // keep the incomplete last line

      for (const rawLine of lines) {
        const line = rawLine.replace(/\r$/, "");
        if (line.startsWith("event:")) {
          event = line.slice("event:".length).trim();
          continue;
        }
        if (!line.startsWith("data:")) continue;

        const data = JSON.parse(line.slice("data:".length).trim()) as Record<string, any>;

        if (event === "response.output_text.delta") {
          const delta = (data.delta as string) || "";
          process.stdout.write(delta);
          textParts.push(delta);
        } else if (event === "response.output_text.done") {
          doneParts.push((data.text as string) || "");
        } else if (event === "response.completed") {
          const final = (data.response as any) || data;
          usage = final.usage || null;
        } else if (event !== null && event.startsWith("response.")) {
          // lifecycle events — ignore
        } else {
          throw new Error(`Stream terminated by event '${event}': ${JSON.stringify(data)}`);
        }
        event = null;
      }
    }

    process.stdout.write("\n");
    return { text: textParts.join("") || doneParts.join(""), usage };
  }

  streamResponse("Write a one-paragraph prayer for a new season of ministry.").then(
    ({ text, usage }) => console.log(`Streamed text (${text.length} chars), usage: ${JSON.stringify(usage)}`)
  );
  ```

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

  $dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
  $dotenv->safeLoad();

  $apiKey = $_ENV['GLOO_API_KEY'] ?? getenv('GLOO_API_KEY') ?: 'YOUR_API_KEY';
  $apiUrl = 'https://platform.ai.gloo.com/ai/v2/guarded/responses';

  /**
   * Process one raw SSE event block (event:/data: lines), mutating $state.
   */
  function processEvent(string $rawEvent, array &$state): void {
      $event = null;
      $data = null;
      foreach (preg_split('/\r?\n/', $rawEvent) as $line) {
          if (str_starts_with($line, 'event:')) {
              $event = trim(substr($line, strlen('event:')));
          } elseif (str_starts_with($line, 'data:')) {
              $data = json_decode(trim(substr($line, strlen('data:'))), true);
          }
      }

      if ($event === 'response.output_text.delta') {
          $delta = $data['delta'] ?? '';
          echo $delta;
          $state['text_parts'][] = $delta;
      } elseif ($event === 'response.output_text.done') {
          $state['done_parts'][] = $data['text'] ?? '';
      } elseif ($event === 'response.completed') {
          // Flattened payload: usage is at the top level; no output[] array.
          $final = $data['response'] ?? $data;
          $state['usage'] = $final['usage'] ?? null;
      } elseif ($event !== null && str_starts_with($event, 'response.')) {
          // lifecycle events — ignore
      } else {
          throw new RuntimeException("Stream terminated by event '{$event}': " . json_encode($data));
      }
  }

  function streamResponse(string $message): array {
      global $apiKey, $apiUrl;
      $payload = json_encode([
          'model' => 'gloo-anthropic-claude-sonnet-4.6',
          'input' => $message,
          'stream' => true,
          'max_output_tokens' => 256,
      ]);

      $state = ['buffer' => '', 'text_parts' => [], 'done_parts' => [], 'usage' => null];

      $ch = curl_init($apiUrl);
      curl_setopt_array($ch, [
          CURLOPT_POST => true,
          CURLOPT_POSTFIELDS => $payload,
          CURLOPT_HTTPHEADER => [
              'Authorization: Bearer ' . $apiKey,
              'Content-Type: application/json',
              'Accept: text/event-stream',
          ],
          CURLOPT_WRITEFUNCTION => function ($ch, string $chunk) use (&$state): int {
              $state['buffer'] .= $chunk;
              // SSE events are separated by blank lines; process every complete event.
              while (($pos = strpos($state['buffer'], "\n\n")) !== false) {
                  $rawEvent = substr($state['buffer'], 0, $pos);
                  $state['buffer'] = substr($state['buffer'], $pos + 2);
                  processEvent($rawEvent, $state);
              }
              return strlen($chunk);
          },
      ]);
      curl_exec($ch);
      if (curl_errno($ch)) {
          $error = curl_error($ch);
          curl_close($ch);
          throw new RuntimeException('API request failed: ' . $error);
      }
      $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
      curl_close($ch);
      if ($status >= 400) {
          throw new RuntimeException('API request failed with status ' . $status);
      }

      // Flush any final event not terminated by a blank line.
      if (trim($state['buffer']) !== '') {
          processEvent($state['buffer'], $state);
      }

      echo PHP_EOL;
      $text = implode('', $state['text_parts']) ?: implode('', $state['done_parts']);
      return [$text, $state['usage']];
  }

  [$text, $usage] = streamResponse('Write a one-paragraph prayer for a new season of ministry.');
  echo 'Streamed text (' . mb_strlen($text) . " chars), usage: " . json_encode($usage) . "\n";
  ?>
  ```

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

  import (
  	"bufio"
  	"bytes"
  	"encoding/json"
  	"fmt"
  	"io"
  	"net/http"
  	"os"
  	"strings"
  )

  const apiURL = "https://platform.ai.gloo.com/ai/v2/guarded/responses"

  // streamResponse streams via SSE; returns accumulated text and usage.
  //
  // Each data: payload is the flattened event object itself; lifecycle
  // events are ignored, and response.completed carries usage but no output[].
  func streamResponse(message string) (string, map[string]interface{}, error) {
  	payload := map[string]interface{}{
  		"model":             "gloo-anthropic-claude-sonnet-4.6",
  		"input":             message,
  		"stream":            true,
  		"max_output_tokens": 256,
  	}
  	body, _ := json.Marshal(payload)

  	req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(body))
  	if err != nil {
  		return "", nil, err
  	}
  	req.Header.Add("Authorization", "Bearer "+os.Getenv("GLOO_API_KEY"))
  	req.Header.Add("Content-Type", "application/json")
  	req.Header.Set("Accept", "text/event-stream")

  	resp, err := http.DefaultClient.Do(req)
  	if err != nil {
  		return "", nil, err
  	}
  	defer resp.Body.Close()

  	if resp.StatusCode != http.StatusOK {
  		respBody, _ := io.ReadAll(resp.Body)
  		return "", nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(respBody))
  	}

  	var textParts, doneParts []string
  	var usage map[string]interface{}
  	event := ""

  	scanner := bufio.NewScanner(resp.Body)
  	scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024)
  	for scanner.Scan() {
  		line := scanner.Text()
  		switch {
  		case strings.HasPrefix(line, "event:"):
  			event = strings.TrimSpace(line[len("event:"):])
  			continue
  		case !strings.HasPrefix(line, "data:"):
  			continue
  		}

  		var data map[string]interface{}
  		if err := json.Unmarshal([]byte(strings.TrimSpace(line[len("data:"):])), &data); err != nil {
  			return "", nil, fmt.Errorf("failed to parse SSE data: %w", err)
  		}

  		switch {
  		case event == "response.output_text.delta":
  			delta, _ := data["delta"].(string)
  			fmt.Print(delta)
  			textParts = append(textParts, delta)
  		case event == "response.output_text.done":
  			text, _ := data["text"].(string)
  			doneParts = append(doneParts, text)
  		case event == "response.completed":
  			final, ok := data["response"].(map[string]interface{})
  			if !ok {
  				final = data // flattened payload
  			}
  			if u, ok := final["usage"].(map[string]interface{}); ok {
  				usage = u
  			}
  		case strings.HasPrefix(event, "response."):
  			// lifecycle events — ignore
  		default:
  			dataJSON, _ := json.Marshal(data)
  			return "", nil, fmt.Errorf("stream terminated by event '%s': %s", event, string(dataJSON))
  		}
  		event = ""
  	}

  	fmt.Println()
  	text := strings.Join(textParts, "")
  	if text == "" {
  		text = strings.Join(doneParts, "")
  	}
  	return text, usage, nil
  }

  func main() {
  	text, usage, err := streamResponse("Write a one-paragraph prayer for a new season of ministry.")
  	if err != nil {
  		fmt.Println("Error:", err)
  		return
  	}
  	usageJSON, _ := json.Marshal(usage)
  	fmt.Printf("Streamed text (%d chars), usage: %s\n", len([]rune(text)), string(usageJSON))
  }
  ```

  ```java Java theme={null}
  import com.google.gson.Gson;
  import com.google.gson.JsonElement;
  import com.google.gson.JsonObject;
  import com.google.gson.JsonParser;
  import java.io.BufferedReader;
  import java.io.InputStreamReader;
  import java.io.IOException;
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;
  import java.nio.charset.StandardCharsets;
  import java.util.HashMap;
  import java.util.Map;

  public class StreamResponse {

      private static final String API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses";
      private static final String API_KEY = System.getenv().getOrDefault("GLOO_API_KEY", "YOUR_API_KEY");
      private static final Gson gson = new Gson();

      public static Map<String, Object> streamResponse(String message) throws Exception {
          Map<String, Object> payload = new HashMap<>();
          payload.put("model", "gloo-anthropic-claude-sonnet-4.6");
          payload.put("input", message);
          payload.put("stream", true);
          payload.put("max_output_tokens", 256);

          HttpRequest request = HttpRequest.newBuilder()
                  .uri(URI.create(API_URL))
                  .header("Content-Type", "application/json")
                  .header("Authorization", "Bearer " + API_KEY)
                  .header("Accept", "text/event-stream")
                  .POST(HttpRequest.BodyPublishers.ofString(gson.toJson(payload)))
                  .build();

          HttpResponse<java.io.InputStream> response = HttpClient.newHttpClient()
                  .send(request, HttpResponse.BodyHandlers.ofInputStream());
          if (response.statusCode() != 200) {
              String body = new String(response.body().readAllBytes(), StandardCharsets.UTF_8);
              throw new IOException("API request failed with status " + response.statusCode() + ": " + body);
          }

          StringBuilder textBuilder = new StringBuilder();
          StringBuilder doneBuilder = new StringBuilder();
          JsonElement usage = null;

          try (BufferedReader reader = new BufferedReader(
                  new InputStreamReader(response.body(), StandardCharsets.UTF_8))) {
              String event = null;
              String line;
              while ((line = reader.readLine()) != null) {
                  if (line.startsWith("event:")) {
                      event = line.substring("event:".length()).trim();
                      continue;
                  }
                  if (!line.startsWith("data:")) {
                      continue;
                  }

                  JsonObject data = JsonParser.parseString(line.substring("data:".length()).trim()).getAsJsonObject();

                  if ("response.output_text.delta".equals(event)) {
                      String delta = data.has("delta") && !data.get("delta").isJsonNull()
                              ? data.get("delta").getAsString() : "";
                      System.out.print(delta);
                      textBuilder.append(delta);
                  } else if ("response.output_text.done".equals(event)) {
                      String text = data.has("text") && !data.get("text").isJsonNull()
                              ? data.get("text").getAsString() : "";
                      doneBuilder.append(text);
                  } else if ("response.completed".equals(event)) {
                      // Flattened payload: usage is at the top level; no output[] array.
                      JsonObject finalResponse = data.has("response") && data.get("response").isJsonObject()
                              ? data.getAsJsonObject("response")
                              : data;
                      usage = finalResponse.get("usage");
                  } else if (event != null && event.startsWith("response.")) {
                      // lifecycle events — ignore
                  } else {
                      throw new IOException("Stream terminated by event '" + event + "': " + gson.toJson(data));
                  }
                  event = null;
              }
          }

          System.out.println();
          String text = textBuilder.length() > 0 ? textBuilder.toString() : doneBuilder.toString();
          Map<String, Object> result = new HashMap<>();
          result.put("text", text);
          result.put("usage", usage);
          return result;
      }

      public static void main(String[] args) throws Exception {
          Map<String, Object> result = streamResponse("Write a one-paragraph prayer for a new season of ministry.");
          System.out.println("Streamed text (" + ((String) result.get("text")).length()
                  + " chars), usage: " + gson.toJson(result.get("usage")));
      }
  }
  ```
</CodeGroup>

### Understanding the Response

When you set `"stream": true`, the API response is an SSE stream. Here are the key event types, and what to do with each:

| Event Type | When | What to do |
| - | - | - |
| `response.created` | Once — stream start | Nothing required — the stream is open. |
| `response.output_text.delta` | Many — as tokens are generated | Print or buffer each `delta` as it arrives. |
| `response.output_text.done` | Once per `message` item | Optionally capture `text` — the item's complete text. |
| `response.completed` | Once — stream end | Read `usage`, then close the stream. |
| `response.in_progress`<br />`response.output_item.added` / `done`<br />`response.content_part.added` / `done` | Interleaved throughout | Ignore — they're purely structural. |
| `error.*` (or any event you don't recognize) | Terminal — ends the stream | Treat as an error: close the stream and surface the message. |

On the wire, each event is an `event:` line followed by a `data:` line:

```text theme={null}
event: response.created
data: {"type":"response.created","id":"resp_a1b2c3","object":"response",...}

event: response.output_text.delta
data: {"type":"response.output_text.delta","item_id":"msg_...","delta":"Grief "}

event: response.output_text.done
data: {"type":"response.output_text.done","item_id":"msg_...","text":"Grief support starts..."}

event: response.completed
data: {"type":"response.completed","id":"resp_a1b2c3",...,"usage":{"input_tokens":24,"output_tokens":38,"total_tokens":62}}
```

<Note>
  The live API sends **flattened payloads**: each `data:` line is the event object itself (its `type` matches the `event:` line), with no nested `response` wrapper. The `response.completed` payload carries `usage` at the top level and does **not** include the `output[]` array — the full text arrives via `response.output_text.done`. The snippets below accept both the flattened shape and the nested shape so they work with either.
</Note>

### What You'll See

When you run the full sample, the deltas print live as they arrive. After the stream completes, the runner prints the model, the streamed-text length with a preview, and the usage from `response.completed`:

```text theme={null}
Example 3: Streaming (SSE)
Testing: Write a one-paragraph prayer for a new season of ministry.
Lord, as this new season begins, we ask for Your guidance... (deltas print live as they arrive)
   Model used: gloo-anthropic-claude-sonnet-4.6
   Streamed text (273 chars): Lord, as this new season begins, we ask for Your guidance...
   Usage: {"input_tokens": 21, "output_tokens": 74, "total_tokens": 95}
   ✓ Example 3 passed
```

<Warning>
  Streams can fail after the response has started — a dropped connection mid-stream means partial output. See [Handling Streaming Failures](/best-practices/completions-streaming-failures) for retry, continuation, and partial-output guidance.
</Warning>

***

## Example 4: Vision (Image Input)

Vision uses the same endpoint — pass typed content parts (`input_text` + `input_image`) in the `content` of an input item. Use a vision-capable model such as `gloo-google-gemini-3.1-pro`:

<CodeGroup>
  ```python Python theme={null}
  import os
  import requests
  from dotenv import load_dotenv

  load_dotenv()

  API_KEY = os.getenv("GLOO_API_KEY", "YOUR_API_KEY")
  API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses"

  def make_vision_response(image_url, question):
      """Example 4: Vision — image input via typed content parts."""
      headers = {
          "Authorization": f"Bearer {API_KEY}",
          "Content-Type": "application/json",
      }
      payload = {
          "model": "gloo-google-gemini-3.1-pro",
          "input": [
              {
                  "role": "user",
                  "content": [
                      {"type": "input_text", "text": question},
                      {"type": "input_image", "image_url": image_url},
                  ],
              }
          ],
          "max_output_tokens": 256,
      }
      response = requests.post(API_URL, headers=headers, json=payload)
      response.raise_for_status()
      return response.json()

  result = make_vision_response(
      "https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg",
      "What animal is in this image?",
  )
  print(result)
  ```

  ```javascript JavaScript theme={null}
  require("dotenv").config();

  const API_KEY = process.env.GLOO_API_KEY || "YOUR_API_KEY";
  const API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses";

  async function makeVisionResponse(imageUrl, question) {
    const payload = {
      model: "gloo-google-gemini-3.1-pro",
      input: [
        {
          role: "user",
          content: [
            { type: "input_text", text: question },
            { type: "input_image", image_url: imageUrl },
          ],
        },
      ],
      max_output_tokens: 256,
    };
    const response = await fetch(API_URL, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(payload),
    });
    if (!response.ok) {
      throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
    }
    return response.json();
  }

  makeVisionResponse(
    "https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg",
    "What animal is in this image?"
  ).then(console.log);
  ```

  ```typescript TypeScript theme={null}
  import { config } from "dotenv";

  config();

  const API_KEY = process.env.GLOO_API_KEY || "YOUR_API_KEY";
  const API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses";

  async function makeVisionResponse(imageUrl: string, question: string): Promise<any> {
    const payload = {
      model: "gloo-google-gemini-3.1-pro",
      input: [
        {
          role: "user",
          content: [
            { type: "input_text", text: question },
            { type: "input_image", image_url: imageUrl },
          ],
        },
      ],
      max_output_tokens: 256,
    };
    const response = await fetch(API_URL, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(payload),
    });
    if (!response.ok) {
      throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
    }
    return response.json();
  }

  makeVisionResponse(
    "https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg",
    "What animal is in this image?"
  ).then(console.log);
  ```

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

  $dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
  $dotenv->safeLoad();

  $apiKey = $_ENV['GLOO_API_KEY'] ?? getenv('GLOO_API_KEY') ?: 'YOUR_API_KEY';
  $apiUrl = 'https://platform.ai.gloo.com/ai/v2/guarded/responses';

  function makeVisionResponse(string $imageUrl, string $question): array {
      global $apiKey, $apiUrl;
      $payload = json_encode([
          'model' => 'gloo-google-gemini-3.1-pro',
          'input' => [
              [
                  'role' => 'user',
                  'content' => [
                      ['type' => 'input_text', 'text' => $question],
                      ['type' => 'input_image', 'image_url' => $imageUrl],
                  ],
              ],
          ],
          'max_output_tokens' => 256,
      ]);
      $ch = curl_init($apiUrl);
      curl_setopt_array($ch, [
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_POST => true,
          CURLOPT_POSTFIELDS => $payload,
          CURLOPT_HTTPHEADER => [
              'Authorization: Bearer ' . $apiKey,
              'Content-Type: application/json',
          ],
      ]);
      $body = curl_exec($ch);
      if (curl_errno($ch)) {
          $error = curl_error($ch);
          curl_close($ch);
          throw new RuntimeException('API request failed: ' . $error);
      }
      $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
      curl_close($ch);
      if ($status >= 400) {
          throw new RuntimeException('API request failed with status ' . $status . ': ' . $body);
      }
      return json_decode($body, true);
  }

  print_r(makeVisionResponse(
      'https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg',
      'What animal is in this image?'
  ));
  ?>
  ```

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

  import (
  	"bytes"
  	"encoding/json"
  	"fmt"
  	"io"
  	"net/http"
  	"os"
  )

  const apiURL = "https://platform.ai.gloo.com/ai/v2/guarded/responses"

  func makeVisionResponse(imageURL, question string) (map[string]interface{}, error) {
  	payload := map[string]interface{}{
  		"model": "gloo-google-gemini-3.1-pro",
  		"input": []map[string]interface{}{
  			{
  				"role": "user",
  				"content": []map[string]interface{}{
  					{"type": "input_text", "text": question},
  					{"type": "input_image", "image_url": imageURL},
  				},
  			},
  		},
  		"max_output_tokens": 256,
  	}
  	body, _ := json.Marshal(payload)

  	req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(body))
  	if err != nil {
  		return nil, err
  	}
  	req.Header.Add("Authorization", "Bearer "+os.Getenv("GLOO_API_KEY"))
  	req.Header.Add("Content-Type", "application/json")

  	resp, err := http.DefaultClient.Do(req)
  	if err != nil {
  		return nil, err
  	}
  	defer resp.Body.Close()

  	respBody, _ := io.ReadAll(resp.Body)
  	if resp.StatusCode != http.StatusOK {
  		return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(respBody))
  	}

  	var result map[string]interface{}
  	json.Unmarshal(respBody, &result)
  	return result, nil
  }

  func main() {
  	result, err := makeVisionResponse(
  		"https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg",
  		"What animal is in this image?",
  	)
  	if err != nil {
  		fmt.Println("Error:", err)
  		return
  	}
  	out, _ := json.MarshalIndent(result, "", "  ")
  	fmt.Println(string(out))
  }
  ```

  ```java Java theme={null}
  import com.google.gson.Gson;
  import java.io.IOException;
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;

  public class VisionResponse {

      private static final String API_URL = "https://platform.ai.gloo.com/ai/v2/guarded/responses";
      private static final String API_KEY = System.getenv().getOrDefault("GLOO_API_KEY", "YOUR_API_KEY");
      private static final Gson gson = new Gson();

      public static Map<String, Object> makeVisionResponse(String imageUrl, String question) throws Exception {
          Map<String, Object> textPart = new HashMap<>();
          textPart.put("type", "input_text");
          textPart.put("text", question);
          Map<String, Object> imagePart = new HashMap<>();
          imagePart.put("type", "input_image");
          imagePart.put("image_url", imageUrl);

          Map<String, Object> inputItem = new HashMap<>();
          inputItem.put("role", "user");
          inputItem.put("content", List.of(textPart, imagePart));

          Map<String, Object> payload = new HashMap<>();
          payload.put("model", "gloo-google-gemini-3.1-pro");
          payload.put("input", List.of(inputItem));
          payload.put("max_output_tokens", 256);

          HttpRequest request = HttpRequest.newBuilder()
                  .uri(URI.create(API_URL))
                  .header("Content-Type", "application/json")
                  .header("Authorization", "Bearer " + API_KEY)
                  .POST(HttpRequest.BodyPublishers.ofString(gson.toJson(payload)))
                  .build();

          HttpResponse<String> response = HttpClient.newHttpClient()
                  .send(request, HttpResponse.BodyHandlers.ofString());
          if (response.statusCode() != 200) {
              throw new IOException("API request failed with status " + response.statusCode() + ": " + response.body());
          }
          return gson.fromJson(response.body(), Map.class);
      }

      public static void main(String[] args) throws Exception {
          Map<String, Object> result = makeVisionResponse(
              "https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg",
              "What animal is in this image?");
          System.out.println(gson.toJson(result));
      }
  }
  ```
</CodeGroup>

### What You'll See

The reply arrives as a normal `message` item in `output[]` — for the cat photo above, the answer identifies the animal:

```json theme={null}
{
  "id": "resp_...",
  "object": "response",
  "model": "gloo-google-gemini-3.1-pro",
  "output": [
    {
      "type": "message",
      "role": "assistant",
      "content": [
        { "type": "output_text", "text": "The animal in this image is a cat." }
      ]
    }
  ],
  "usage": { "input_tokens": 275, "output_tokens": 9, "total_tokens": 284 }
}
```

Running the full sample prints the fourth block:

```text theme={null}
Example 4: Vision (Image Input)
Testing: https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg
   Model used: gloo-google-gemini-3.1-pro
   Response: The animal in this image is a cat.
   Usage: {"input_tokens": 275, "output_tokens": 9, "total_tokens": 284}
   ✓ Example 4 passed
```

***

## Complete Example

<Card title="View Complete Code" icon="github" href="https://github.com/GlooDeveloper/gloo-ai-docs-cookbook/tree/main/responses">
  Clone or browse the complete working examples for all 6 languages (JavaScript, TypeScript, Python, PHP, Go, Java) with setup instructions.
</Card>

### What's Included

Each language implementation provides:

* **`auth`** — Shared API key configuration
* **`config`** — Centralized configuration (endpoint, env vars)
* **Runner per example** — Basic guarded response, instructions + multi-turn, streaming, and vision, plus a final banner
* **Env helper** — Ignores unset `.env` placeholders
* **`post` helper** — Shared request helper that annotates 403s with "(guardrails hard-blocked this request)"
* **`extractText` helper** — Reusable helper for pulling text out of `output[]`

Set up your credentials first:

```bash theme={null}
# Create a .env file in the language directory (or export for Go/Java)
GLOO_API_KEY=your_actual_api_key_here
```

Then run per language:

<CodeGroup>
  ```bash Python theme={null}
  cd responses/python
  pip install -r requirements.txt
  python main.py
  ```

  ```bash JavaScript theme={null}
  cd responses/javascript
  npm install && npm start
  ```

  ```bash TypeScript theme={null}
  cd responses/typescript
  npm install && npx tsx index.ts
  ```

  ```bash PHP theme={null}
  cd responses/php
  composer install && php index.php
  ```

  ```bash Go theme={null}
  cd responses/go
  go mod tidy && go run main.go
  ```

  ```bash Java theme={null}
  cd responses/java
  mvn compile && mvn exec:java
  ```
</CodeGroup>

Each runner prints a block per example (model used, first \~100 characters of the response, token usage, and a ✓ pass line), then the final banner:

```text theme={null}
=== All Responses API tests passed! ===
```

<Tip>
  The snippets in this tutorial are simplified to be self-contained. The cookbook samples are structured the same way but add robustness: an env helper that ignores unset `.env` placeholders, a shared `post` helper that annotates 403s with "(guardrails hard-blocked this request)", and a reusable `extractText` helper for pulling text out of `output[]`. The prompts, field names, and endpoint are identical.
</Tip>

***

## Troubleshooting

**`Please set your GLOO_API_KEY environment variable`**
: Your `.env` file is missing or doesn't contain `GLOO_API_KEY`. Create one in the language's directory (see [Complete Example](#complete-example)), or export the variable in your shell for Go and Java.

**`API request failed with status 403`**
: Guardrails hard-blocked the request. The 403 body includes a `content_policy_violation` detail — rephrase the request rather than retrying. See [Guardrail behavior](#guardrail-behavior).

**Vision request returns 400 / `INVALID_REQUEST`**
: The model isn't vision-capable. Vision requires a multimodal model like `gloo-google-gemini-3.1-pro` — sending image parts to a text-only model (e.g. `gloo-anthropic-claude-sonnet-4.6`) fails. Check [Supported Models](/api-guides/supported-models) for capabilities.

**Vision request fails on image fetch**
: The API fetches `image_url` server-side, so the URL must be publicly reachable. Test it in a browser or `curl -I <url>`. Private or expiring URLs fail; use a public URL or a base64 `data:` URI.

**Stream parses nothing, or every event looks unknown**
: Remember the payloads are **flattened** — the `data:` line is the event object itself, not `{ "response": {...} }`. Match on the `event:` line, handle `response.output_text.delta`/`done` and `response.completed`, and ignore other `response.*` lifecycle events. Treating unknown events as terminal (and closing the stream) is the safe default.

***

## Next Steps

Now that you understand the Responses API, explore:

1. **[Responses API Guide](/api-guides/responses)** - Full API documentation
2. **[Tool Use](/api-guides/tool-use)** - Function calling with Responses
3. **[Grounded Responses](/api-guides/grounded-responses)** - RAG with source attribution
4. **[Direct Responses](/api-guides/direct-responses)** - Unguarded direct endpoint
5. **[Completions V2 Guide](/api-guides/completions-v2)** - Auto-routing and `model_family` selection
6. **[Supported Model IDs](/api-guides/supported-models)** - All available models


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