Dealing With the Answer
Module 3 of 8 in the chapter Salesforce as an API Consumer, chapter 9 of the Integration Architect course. Each module stands on its own. Previous: The Six Lines of a Callout · Next: Speed Limits
The question this module answers: Once the other system answers, how does Salesforce turn that answer into something it can safely use?
The six lines that make the callout are the straightforward part. Dealing with the answer is actually the biggest lift in integrations. Three questions, in order: did it work, what shape is it in, and how do I turn it into Apex?
Status First
Section titled “Status First”You should always, always, always test the status code. Before you read a single character of the body, res.getStatusCode() tells you which kind of answer you have. Chapter 3 taught the families, and our HTTP Status Codes page lists every code you’re likely to meet. Here the families decide what your code does:
- 2xx: it worked. Read the body, if there is one. A
204has none. - 4xx: you sent something wrong. Sending it again won’t help. Log it, and get a person to look.
- 5xx: their side failed. It may work later. This is the one to retry, carefully (chapter 14).
- Anything you didn’t expect: treat it as a failure, not a success.
And wrap the whole thing in a try and catch. The other system can fail in ways that never reach a status code: a timeout, a closed connection, a body that won’t parse.
Then the Format
Section titled “Then the Format”Don’t assume. On one integration, I sent JSON out and got XML back. The contract said one thing, and the answer did another. So for every call, know the serialization going out and the serialization coming back, and check the answer’s Content-Type header before you parse.
Three shapes are common:
- JSON: most of what you’ll see today.
- XML: rarer, and usually a legacy system. Apex has
XmlStreamWriterto write it andXmlStreamReaderto read it, one element at a time. Know the names; you can always look up the methods. - A file: a PDF, an image, a document. Don’t read it as text.
res.getBodyAsBlob()gives you the binary large object, a blob, as it came.
Then the Parse: From One Line to a Parser
Section titled “Then the Parse: From One Line to a Parser”Turning JSON into Apex is the transporter from chapter 3 working in reverse: the stream comes in, and you rematerialize it as objects. There’s a spectrum of ways to do it, and the architect’s job is to pick the right point on it.
One line, into a class that matches. You write a small Apex class, a model, that matches the JSON exactly. Not an sObject; just an Apex structure. Then one line rematerializes the whole answer:
RedrockShipment shipment = (RedrockShipment) JSON.deserialize(res.getBody(), RedrockShipment.class);It’s simple, it’s type safe, and it’s easy to maintain. This is the fixed deserialization from my JSON Jamboree post, and it’s the mirror of chapter 8’s contract classes. For simple, stable structures, I’d use it every time.
It works great until it doesn’t. And when it stops working, it comes crashing down. Three things bring it down:
- A field named like an Apex reserved word. You can’t declare a variable with that name, so the class can’t match. The answer a student gave in my class is the one I’d use in production: deserialize it untyped,
JSON.deserializeUntyped(...), into a map of names to values, and pick out what you need. - A shape that won’t fit a class. One flight-data service I called sent each plane as a list of values where the meaning was the position in the list: no names, just first, second, third. No class matches that. I wrote my own parser with
JSONParser, walking the tokens one by one, about 165 lines of code. It works, but it’s fragile code that can break when they change anything. - A payload too big for memory. Everything you deserialize lives in the Apex heap. Winter ‘27 raised it from 6 MB to 10 MB for synchronous transactions and from 12 MB to 25 MB for asynchronous ones (The Salesforce Developer’s Guide to the Winter ‘27 Release (opens in a new tab), checked 2026-10-31). Bigger answers than that don’t belong in a callout. Ask for less, page through it, or move the data another way (chapter 10).
And when the fields keep changing, the far end of the spectrum is a mapping kept as data: a table in custom metadata that says which field in the answer goes to which field on the record, so a new field is a change to a record, not a deployment. Chapter 3 shows where that mapping lives.
From one line to a parser. Move right only when the answer makes you.
So which one? As an architect, you need to look into the future. If there’s one object and 10 fields, a class and one line, and you’re done. If they’re telling you there will be 20 objects with 100 fields each, and more coming, I’d invest the time to build the dynamic parser at the beginning. Rewriting a fixed integration under pressure, a class and a deployment for every new field, costs far more than building it right once.
Don’t Log What You Shouldn’t
Section titled “Don’t Log What You Shouldn’t”While you’re building, you’ll print the whole answer to the debug log to see what came back. Take that out before production. If the service returns personal data, a patient’s details, a recipient’s address, then the moment someone turns on a debug log, that data is sitting in the log for anyone who can read it. If you really need the payload in a log, mask the sensitive fields first. And huge payloads in debug logs slow Salesforce down too. Chapter 14 designs the integration log that should hold what you need instead.
Watch it on video
Section titled “Watch it on video”In 2023 I made a video on three ways to read a JSON answer in Apex, including a mapping kept in custom metadata. The note under the video says what’s still right and what’s changed since.
What an Architect Decides Here
Section titled “What an Architect Decides Here”- What the code does for each status family, written down: read, log for a person, retry, or fail.
- The format each call sends and receives, checked, not assumed.
- The point on the spectrum: a matching class, untyped, a hand-written parser, or a mapping in custom metadata; chosen by how much the answer will change, before the first line is written.
- The size of the answer against the heap, and what happens when it’s too big.
- What is never logged, and what is masked if it must be.
Explore
Section titled “Explore”- HTTP Status Codes: our reference page, every code you’re likely to meet, by family
- JSON Jamboree: Fixed vs. Dynamic Deserialization: SteveTechArc, my 2023 post on the three approaches
- The Salesforce Developer’s Guide to the Winter ‘27 Release (opens in a new tab): Salesforce Developers Blog, the new heap limits (checked 2026-10-31)
- Schemas and Transformation: chapter 3, the shape written down and where the mapping lives
