OpenAI Responses API output structure varies when tools are enabled → mapping intermittently empty in Make

Absolutely—here’s a concise post you can drop into the Make Community, fully in English and with a real response sample.


Title: OpenAI Responses API output structure varies when tools are enabled → mapping intermittently empty in Make

Post:
Hi all,

I’m calling the OpenAI Responses API from Make via HTTP (not the built-in OpenAI app).
Model: gpt-4o-mini with text.format.type = "text". Sometimes I also pass tools with tool_choice: "auto" for file_search (vector store).

Problem
When tools are enabled, the API returns a mixed output[] array (e.g., a file_search_call followed by a message). The UI “click-to-map” path (like output[1].content[0].text) then changes between runs, and output_text may be missing; result: my mapping is sometimes empty.
If I remove tools/tool_choice, the structure stays stable and {{<step>.data.output_text}} works every time.

Concrete response example (trimmed):

{
  "id": "resp_...",
  "model": "gpt-4o-mini-2024-07-18",
  "text": { "format": { "type": "text" }, "verbosity": "medium" },
  "tools": [
    { "type": "file_search", "vector_store_ids": ["vs_..."], "max_num_results": 5 }
  ],
  "tool_choice": "auto",
  "output": [
    {
      "id": "fs_...",
      "type": "file_search_call",
      "status": "completed",
      "queries": ["..."]
    },
    {
      "id": "msg_...",
      "type": "message",
      "status": "completed",
      "role": "assistant",
      "content": [
        { "type": "output_text", "text": "Hello …(final text)…" }
      ]
    }
  ],
  "output_text": null
}

Because of this, index-based mappings (e.g., output[1].content[0].text) are brittle.

Question
What’s your best practice in Make to reliably extract the text when tools might add/remove entries in output[]?

What I’m doing now (works):

  1. HTTP body (simplified)
{
  "model": "gpt-4o-mini",
  "temperature": 0.2,
  "max_output_tokens": 400,
  "text": { "format": { "type": "text" } },
  "input": [ ... ],
  "tools": [
    { "type": "file_search", "vector_store_ids": ["vs_..."], "max_num_results": 5 }
  ],
  "tool_choice": "auto"
}

  1. Normalizer step in Make (Set variable right after the HTTP call)
    I create a single ai_text variable so all later modules can use it without deep, indexy mappings.
    (Replace 8 with your HTTP step number; Make mapper syntax):
{{
  ifempty(
    8.data.output_text;
    join(
      map(
        8.data.output[];
        join(
          map(
            ifempty(get(item;"content"); array());
            get(item;"text")
          );
          ""
        )
      );
      ""
    )
  )
}}

  • First tries output_text (when present).

  • Otherwise: loops through all output[], safely gets content[] (or empty array), pulls each text, and concatenates.

Then in my JSON body / email body / anywhere I need the answer, I just use {{ai_text}}. This made the flow stable even with tools on.

Alternative (UI-driven): Iterator + Text Aggregator

  • Iterator over 8.data.output[]

  • (Optional) Filter for type = message

  • Text Aggregator to join(map(content[]; text); "")

  • Store result as ai_text

Why it “just works” without tools
If I remove tools (or set tool_choice: "none"), the response is usually a single message and output_text is filled; index paths remain stable.

Would love to hear if there’s an even cleaner pattern (e.g., a recommended “coalesce text” recipe in Make) or tips to handle tool-added outputs more elegantly. Thanks!