Most teams wire up tool use directly in application code and skip the manual inspection layer. If you test function calling Postman requests against an OpenAI-compatible endpoint, you catch schema mismatches, provider divergences, and serialization bugs before they reach production.
Step 1: Create a base request in Postman
Open Postman and create a new POST request. Point it at your chat completions endpoint. For vanilla OpenAI, that is https://api.openai.com/v1/chat/completions. If you route through a gateway such as n4n.ai, use its single OpenAI-compatible URL and pass the model name in the body—this lets you swap models without touching headers.
Set the Authorization tab to Bearer Token and drop in your API key. In the Headers tab, ensure Content-Type: application/json is present.
Create a collection variable base_url so you can reuse it:
{
"base_url": "https://api.openai.com/v1"
}
The request URL becomes {{base_url}}/chat/completions.
Step 2: Define a tool schema that stresses the parser
Function calling fails most often on ambiguous or nested parameters. Write a tools array with a single function that takes a required string and an enum. Avoid trivial echo tools—use something that forces the model to map natural language to structured fields.
{
"model": "gpt-4o-mini",
"messages": [
{"role": "user", "content": "What's the weather in Paris in celsius?"}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Fetch current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["location", "unit"]
}
}
}
],
"tool_choice": "auto"
}
Send this from Postman. You are now doing the core task: you test function calling Postman style by watching how the model emits tool_calls instead of free text.
Step 3: Inspect the tool call response
A correct response contains a message with empty content and a tool_calls array. The arguments are delivered as a JSON string, not an object—a common gotcha.
{
"choices": [
{
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\":\"Paris\",\"unit\":\"celsius\"}"
}
}
]
}
}
]
}
Add a Postman test script to assert the shape. Click the Tests tab and paste:
pm.test("model returned a tool call", () => {
const body = pm.response.json();
const msg = body.choices[0].message;
pm.expect(msg.tool_calls).to.be.an('array').with.length.of.at.least(1);
pm.expect(msg.tool_calls[0].function.name).to.equal("get_weather");
const args = JSON.parse(msg.tool_calls[0].function.arguments);
pm.expect(args.location).to.be.a('string');
pm.expect(["celsius","fahrenheit"]).to.include(args.unit);
});
Run the request. The test pane should show green. If you see content with natural language instead, the model ignored the tool—tighten the prompt or set tool_choice to {"type":"function","function":{"name":"get_weather"}}.
Verify raw arguments parsing
In the Postman console, log the parsed arguments to confirm types. This catches cases where a provider returns arguments as an object (non-compliant) or escapes quotes twice.
Step 4: Simulate function execution and close the loop
Real code executes the function and returns data. In Postman, mock it. Duplicate the request, rename to “Follow-up with tool result”, and modify the body to include the assistant message and a tool role message.
{
"model": "gpt-4o-mini",
"messages": [
{"role": "user", "content": "What's the weather in Paris in celsius?"},
{
"role": "assistant",
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\":\"Paris\",\"unit\":\"celsius\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "{\"temp\": 22, \"condition\": \"clear\"}"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Fetch current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius","fahrenheit"]}
},
"required": ["location","unit"]
}
}
}
]
}
The model should now produce a human-readable answer referencing the mocked JSON. Add a test:
pm.test("final answer uses tool data", () => {
const body = pm.response.json();
const text = body.choices[0].message.content || "";
pm.expect(text.toLowerCase()).to.include("paris");
pm.expect(text).to.match(/22|clear/i);
});
This two-request pattern is the minimum viable way to test function calling Postman flows end to end.
Step 5: Parameterize the model field for provider comparisons
Hardcoding gpt-4o-mini hides provider differences. Create a collection variable model and reference it as {{model}} in the body. Then use Postman’s runner or just manually switch the value to claude-3-5-sonnet, llama-3.1-70b, etc.
If you use a gateway that exposes 240+ models behind one endpoint, you avoid managing multiple base URLs and auth tokens. n4n.ai does exactly this and adds automatic fallback when a provider is rate-limited, so a degraded upstream doesn’t break your test suite. The request body stays identical; only {{model}} changes.
Example bash curl to confirm the same shape outside Postman:
curl {{base_url}}/chat/completions \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"model":"{{model}}","messages":[{"role":"user","content":"Weather in Paris?"}],"tools":[{"type":"function","function":{"name":"get_weather","parameters":{"type":"object","properties":{"location":{"type":"string"}},"required":["location"]}}}]}'
Use the Postman Collection Runner with a data file listing model names to batch-test across providers. Export results as JUnit and wire into CI.
Step 6: Test error and cache-control paths
Production function calling deals with partial failures. Simulate a tool error by returning a tool message with an error string, then assert the model recovers or escalates.
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "ERROR: upstream timeout"
}
Add a test that checks the next assistant message either retries with corrected args or informs the user. This is where you test function calling Postman robustness, not just happy path.
If your gateway honors client routing directives and forwards provider cache-control hints, set extra_headers or headers in Postman to pass cache preferences (follow provider docs). Verify the response metadata echoes cache hits if supported.
Step 7: Verify success
You have a working test suite when:
- The first request reliably returns
tool_callswith parseableargumentsfor your target models. - The Postman test script in Step 3 passes without manual inspection.
- The follow-up request with mocked tool output yields an answer that incorporates the supplied values.
- Swapping
{{model}}surfaces no schema violations (some open-weight models emit arguments as objects—flag those). - Error-injection requests degrade gracefully.
Keep the collection in version control. Export as JSON and diff changes when the API evolves.
Practical caveats
Postman’s JSON editor does not validate the parameters schema against JSON Schema draft-07. A typo in required will silently pass until the model omits the field. Run a quick python check locally if you author complex tools:
import jsonschema, json
schema = {"type":"object","properties":{"location":{"type":"string"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location","unit"]}
jsonschema.validate({"location":"Paris","unit":"celsius"}, schema)
Never store live keys in shared collections. Use Postman environments and reference {{api_key}} with a secret type.
Function calling is not uniform across providers. OpenAI, Anthropic, and Mistral differ in tool_calls serialization and in whether they support parallel calls. Your Postman tests are the cheapest place to document those divergences per model.
When you test function calling Postman style across dozens of models, you build a regression net. A provider update that breaks argument parsing shows up as a red test before your users see a 500. That is the point.