Skip to content
47% off: the complete course for $79 (reg. $149)Order now →
AI Builder
Module 1Full course9 min read

Anatomy of a Model API Call

Messages, roles, system prompts, content blocks, stop reasons, and usage — every field of a real request and response, and what each one is for.

Most AI feature bugs aren't model bugs. They're integration bugs: code that reads content[0].text and crashes when the first block is a thinking block, a handler that treats a truncated response as complete, a chat feature that puts instructions in the wrong field, or a cost dashboard that never logged usage. Each of these comes from not knowing what one field of the request or response was for.

This lesson walks through a real Messages API call to Claude, field by field: what you send, what comes back, and what your code should do with each part. By the end you'll have a small, typed wrapper you can reuse for every feature in this course, one that handles content blocks, stop reasons, refusals, and usage correctly from day one.

A complete request and response

Here's a Deskmate request at the HTTP level, so nothing is hidden by an SDK. It asks for a one-sentence summary of a ticket:

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2026-10-21" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-5-5",
    "max_tokens": 2000,
    "system": "You summarize support tickets for Deskmate agents in one sentence.",
    "messages": [
      {
        "role": "user",
        "content": "<ticket>CSV export stuck at 0% since this morning, Acme Ops workspace, last 90 days of tickets.</ticket>"
      }
    ],
    "output_config": { "effort": "low" }
  }'

And here's the shape of what comes back (IDs shortened, and usage trimmed to the fields discussed below):

{
  "id": "msg_01...",
  "type": "message",
  "role": "assistant",
  "model": "claude-sonnet-5-5",
  "content": [
    { "type": "thinking", "thinking": "", "signature": "EqQB..." },
    { "type": "text", "text": "Customer's 90-day ticket CSV export in the Acme Ops workspace has been stuck at 0% since this morning." }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "stop_details": null,
  "usage": {
    "input_tokens": 61,
    "output_tokens": 48,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0,
    "output_tokens_details": { "thinking_tokens": 21 }
  }
}

The numbers are illustrative, but the structure is exactly what your code has to handle. Notice that the answer isn't a string. It's a list of content blocks, and the first one here isn't even text. Let's go through it.