api

Tool calling and structured output API

Direct answertools let a model propose a structured call; the application must still authorize, validate, execute, and return the result. strict constrains shape, not business truth.

Updated · Reviewed

Beginner: the model proposes; the application executes

Tool calling does not grant a model server authority. The request tools describe allowed functions and arguments. When the model returns a call, the application verifies identity, tenant authorization, tool name, and arguments before performing any operation, then returns a result for the next model turn. Structured output constrains a final answer with JSON Schema for extraction, classification, or form generation. Neither feature replaces business rules.

Create a site API key in token management, then open the model marketplace. Confirm that the selected model explicitly lists /v1/responses or /v1/chat/completions; similar model names do not imply the same endpoint:

export BASE_URL="https://api.tu-zi.com"
export API_KEY="your site API key"
export RESPONSES_MODEL_NAME="exact marketplace model ID that supports /v1/responses"
export CHAT_MODEL_NAME="exact marketplace model ID that supports /v1/chat/completions"

BASE_URL has no trailing /v1. The two structured-output examples below target different endpoints and their outer request shapes must not be mixed.

Minimal tool request

tool_response=$(curl -sS "$BASE_URL/v1/responses" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d @- <<JSON
  {
    "model": "$RESPONSES_MODEL_NAME",
    "input": "Get the delivery status for order A123",
    "tools": [{
      "type": "function",
      "name": "get_order_status",
      "description": "Read an order visible to the current user",
      "parameters": {
        "type": "object",
        "properties": {"order_id": {"type": "string"}},
        "required": ["order_id"],
        "additionalProperties": false
      },
      "strict": true
    }]
  }
JSON
)

response_id=$(printf '%s' "$tool_response" | jq -r '.id')
call_id=$(printf '%s' "$tool_response" | jq -r '.output[] | select(.type == "function_call") | .call_id' | head -n 1)
test -n "$response_id" && test "$response_id" != "null"
test -n "$call_id" && test "$call_id" != "null"

A response can contain a function-call item, call ID, and JSON arguments. Accept only registered tools, validate arguments again, and verify that the order belongs to the caller. The example uses jq to extract the response ID and original call_id. After the application performs the lookup, return its result to the same Responses turn:

curl -sS "$BASE_URL/v1/responses" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d @- <<JSON
{
  "model": "$RESPONSES_MODEL_NAME",
  "previous_response_id": "$response_id",
  "input": [{
    "type": "function_call_output",
    "call_id": "$call_id",
    "output": "Order A123 has shipped"
  }]
}
JSON

Only the second response can continue from the tool result toward a final answer, and it may request another tool. Repeat validation, execution, and return until a final answer or the step limit. Treat every model-generated identifier, URL, command, and amount as untrusted input.

Structured output: Responses API

Responses API requires the structured-output definition under text.format. This is a complete minimal request:

curl "$BASE_URL/v1/responses" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d @- <<JSON
{
  "model": "$RESPONSES_MODEL_NAME",
  "input": "Customer says: I paid, but the balance has not arrived. Turn this into a ticket.",
  "text": {
    "format": {
      "type": "json_schema",
      "name": "ticket",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "category": {"type": "string", "enum": ["billing", "technical"]},
          "summary": {"type": "string"},
          "needs_human_review": {"type": "boolean"}
        },
        "required": ["category", "summary", "needs_human_review"],
        "additionalProperties": false
      }
    }
  }
}
JSON

Structured output: Chat Completions

Chat Completions uses response_format.json_schema, which has a different outer wrapper:

curl "$BASE_URL/v1/chat/completions" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d @- <<JSON
{
  "model": "$CHAT_MODEL_NAME",
  "messages": [{"role": "user", "content": "Customer says: I paid, but the balance has not arrived. Turn this into a ticket."}],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "ticket",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "category": {"type": "string", "enum": ["billing", "technical"]},
          "summary": {"type": "string"},
          "needs_human_review": {"type": "boolean"}
        },
        "required": ["category", "summary", "needs_human_review"],
        "additionalProperties": false
      }
    }
  }
}
JSON

Even valid JSON still needs validation for date ranges, numeric precision, entity existence, and authorization. Handle refusal, safety blocking, truncation, protocol failure, and valid JSON as separate outcomes.

Safe execution and idempotency

Give every tool its own permission, timeout, input limit, network egress policy, and rate limit. Use parameterized database queries, an HTTP destination allowlist with SSRF protection, and a least-privilege sandbox for code. Write operations need an application idempotency key and confirmation policy. A repeated call ID must return the existing result instead of charging or ordering again. Tool output can itself contain prompt injection, so structure, truncate, and label it as untrusted before sending it back to the model.

Errors, loops, and observability

Return tool failures as structured {code,message,retryable} data without exposing stacks or secrets. Bound total steps, per-tool calls, tokens, spend, and wall-clock time, and detect repeated arguments. Log trace, response ID, call ID, tool name, argument hash, authorization decision, latency, state, and side-effect ID with sensitive fields redacted. Function arguments can arrive as streamed fragments; execute only after the completion event.

Expert: schema evolution and agent governance

Version tools and output schemas. Adding an optional property can remain compatible, while removing or changing semantics requires a new version and canary. Replay recorded requests, tool results, and final answers against authorization, duplicate execution, partial failure, timeout, refusal, and model-upgrade cases. Put a policy engine and human approval around high-risk writes, separating what the model wants to call from what the system permits. Recover from a persisted state machine, never from the assumption that the model remembers prior side effects.

Use cases

  • Let a model call business functions safely
  • Produce JSON that conforms to a schema
  • Build recoverable and auditable multi-step agents

API protocols

  • /v1/responses
  • /v1/chat/completions

FAQ

Does a tool call execute automatically?

No. The model emits a tool name and arguments. Your application must authorize, validate, execute, and return the result. Never concatenate arguments into shell, SQL, or URLs.

Does strict=true remove business validation?

No. Schema validates shape, not inventory, ownership, price, date range, or current business state.

Should an agent retry a failed tool forever?

No. Set total steps, per-tool attempts, a deadline, and a budget. Retry only transient failures with a finite backoff.

Official sources

  1. OpenAI Function Calling Guide Official
  2. OpenAI Structured Outputs Guide Official
  3. OpenAI Responses API Reference Official