OpenAI Responses API for beginners: classify ten messages with strict JSON

Ten fictional customer messages became ten JSON objects in the original order. The schema passed immediately. The first publication-day run still scored only 9 out of 10 in our semantic check: a broken course link for tomorrow had received normal priority.

The JSON format was fine. Our high-priority rule was vague. We rewrote it to say "an event starts within 24 hours and access is blocked," ran the same test again, and got 10 out of 10 matching category and priority pairs.

That is why this guide uses two checks. Structured Outputs constrains the model's response to a JSON schema that software can read. A separate comparison with expected labels tests whether the classification also makes sense.

Source: Structured model outputs – OpenAI API.

Who is this for?

This guide is for people who have used AI chat and now want a predictable, machine-readable result from an API. You should be comfortable running a short Python file and reading JSON, but you do not need production integration experience.

This is not a customer-service automation guide. We stop before Gmail, a CRM, Zapier, customer replies, and autonomous agents. Hammer has already covered that downstream workflow in Stop pasting AI answers by hand. Here, we test the API contract first.

What you will build

In 15–20 minutes, you will create:

  • A local file with ten fictional messages and explicit rules.
  • A separate file containing the expected category and priority.
  • A strict JSON schema with required fields and allowed values.
  • A real response from POST /v1/responses.
  • A receipt that checks both structure and meaning.

Use invented messages for the first test. You can then debug the contract without involving customer data or permissions.

Before you start: keep the API key out of the code

You manage the OpenAI API at platform.openai.com. API billing and ChatGPT subscriptions are separate. Put the key in the OPENAI_API_KEY environment variable or a secret manager. Never hard-code it in the Python file, browser code, screenshots, or Git.

Source: OpenAI explains how ChatGPT subscriptions and API accounts differ.

Source: OpenAI lists best practices for API key safety.

Step 1: write the rules and expected answers first

Our test uses four categories: sales, support, billing, and feedback. Priority is either high or normal.

Write the rules before you call the model. Then create expected-labels.json by hand. That file is the answer key. The model should not grade its own test.

Be specific. After the first 9/10 run, we changed the high rule to:

An event starts within 24 hours and access is blocked,
or a payment is blocking access right now.

That wording separates the broken course link from an ordinary refund request.

Step 2: build a strict JSON schema

Each result needs id, category, urgency, and reason. The enum lists constrain category and priority. The array must contain exactly ten items, and additionalProperties: false blocks extra fields.

schema = \{
    "type": "object",
    "properties": \{
        "items": \{
            "type": "array",
            "minItems": 10,
            "maxItems": 10,
            "items": \{
                "type": "object",
                "properties": \{
                    "id": \{"type": "string"\},
                    "category": \{
                        "type": "string",
                        "enum": ["sales", "support", "billing", "feedback"],
                    \},
                    "urgency": \{
                        "type": "string",
                        "enum": ["high", "normal"],
                    \},
                    "reason": \{"type": "string"\},
                \},
                "required": ["id", "category", "urgency", "reason"],
                "additionalProperties": False,
            \},
        \}
    \},
    "required": ["items"],
    "additionalProperties": False,
\}

The schema checks shape. It does not check whether the model understood your business rule.

Step 3: call the Responses API

The verified publication run used the gpt-5.6 model alias; the API reported the effective model as gpt-5.6-sol. Models and aliases change, so check the current model overview before using this example in a real system.


payload = \{
    "model": "gpt-5.6",
    "input": [
        \{
            "role": "system",
            "content": [\{
                "type": "input_text",
                "text": "Follow the rules only. Preserve input order and do not invent facts."
            \}],
        \},
        \{
            "role": "user",
            "content": [\{
                "type": "input_text",
                "text": json.dumps(input_data, ensure_ascii=False)
            \}],
        \},
    ],
    "text": \{
        "format": \{
            "type": "json_schema",
            "name": "support_triage",
            "strict": True,
            "schema": schema,
        \}
    \},
    "store": False,
\}

request = urllib.request.Request(
    "https://api.openai.com/v1/responses",
    data=json.dumps(payload).encode(),
    headers=\{
        "Authorization": f"Bearer \{os.environ['OPENAI_API_KEY']\}",
        "Content-Type": "application/json",
    \},
    method="POST",
)

store: false disables storage of Responses application state, but it does not mean zero retention. OpenAI documents separate rules for abuse monitoring and approved Zero Data Retention controls.

Source: Data controls in the OpenAI platform.

Step 4: validate the structure before reading the result

Handle HTTP errors, refusals, and incomplete responses before extracting output_text. For a completed response, check that:

  • The status is completed.
  • The array contains exactly ten objects.
  • All ten IDs appear in the same order as the input.
  • Only allowed category and priority values are present.
  • No unexpected fields were added.

A strict schema makes this validation much easier. It does not make validation optional.

Step 5: test the meaning separately

Compare each id with the category and priority in expected-labels.json. Our final run produced:

  • 10 objects in the correct ID order.
  • Only allowed enum values.
  • M02 and M06 marked as high priority.
  • 10 out of 10 category and priority pairs matching the answer key.
  • 0 mismatches.

We did not score the wording in reason, and the schema did not enforce the requested word limit there. Do not claim that "everything was correct" when your test only checked specific fields.

What strict JSON does not solve

Rules can be ambiguous. Customer language changes. A schema does not decide which data may go to an external service, when a person must approve an action, or how the system should handle 401, 429, and 5xx errors.

Once the local test passes, the next step can be a bounded integration with secret management, scoped permissions, an approval point, and a run log. Hammer Automation's Tool Forge can help a team build that part without skipping the test contract.

FAQ

What are Structured Outputs in OpenAI Responses API?

Structured Outputs constrain a model response to a JSON schema. That makes the result easier to process in code, but it does not guarantee that the classification follows your business rule.

Do I need a separate API key if I already have ChatGPT?

Yes. OpenAI API access and billing are separate from a ChatGPT subscription. Keep the API key in an environment variable or secret manager, not in the code.

Why do I need an answer key if the JSON schema is strict?

The schema checks shape, field count, and allowed values. An answer key tests whether each message received the expected category and priority under your written rules.

Can I use real customer messages immediately?

Start with fictional test cases. Before using real data, review legal basis, data minimization, permissions, retention settings, and where a person must approve the next step.

The Forge newsletter

Get new articles in your inbox

Pick the topics you care about. No noise, at most one email a week.

Get new articles in your inbox

We follow GDPR. Unsubscribe anytime.