REST First
Module 2 of 7 in the chapter Salesforce as an API Provider, chapter 8 of the Integration Architect course. Each module stands on its own. Previous: All the Locked Doors · Next: Many in One Call
The question this module answers: How does a program outside Salesforce read and write records through the REST API, and what does an architect decide along the way?
Where this module sits on the map.
You’re an outside process. You need to reach in and get data, push data, look at the metadata. Of all the doors from module 1, REST is where most programs start. It’s the everyday call.
Why REST First
Section titled “Why REST First”Over 100 doors, and this module starts with one. Here’s why.
It’s where the industry went. In my class I say it this way: REST is where it’s at. SOAP is the past, and you use it when you need it. Chapter 3 told how REST won: it travels on the same port as every web page, so it gets through firewalls that block almost everything else, and its answers are small enough for a phone. Whatever language the calling program is written in, it already knows how to make a REST call. It’s about six lines of code: the address, the verb, the format, send it, check for a 200. SOAP is still there, and a few things still need it; chapter 17 (coming soon) covers it.
The other doors are built on it. Look at the addresses. The query, the describe and the records in this module, Composite in module 3, GraphQL and the UI API in module 4, and the Bulk API 2.0 jobs in chapter 10 all start the same way: /services/data/v68.0/, then the resource. The same token goes in the header, the same version is pinned, the same verbs and status codes come back. Each of those doors is REST with one thing added: many records in one call, only the fields you ask for, the screen the user sees, a job that runs in the background. Learn how one REST call to Salesforce works, and you’ve learned most of how the next ones work.
It’s the right size for most calls. One record, or a page of records, right now, with an answer the program can act on straight away. That’s what most integrations do most of the day: a partner checks one delivery, a portal changes one address, a script reads today’s list. When the count grows, the Number of Zeros tells you when to move: to Composite when you have many records for one call, to Bulk when you’re past about 2,000.
So REST is the starting point, and the rest of this chapter is about when to leave it, and for what.
Chapter 3 took a REST request apart: the verb, the address, the headers, the body. Now we point it at Salesforce. Every call in this module goes to Steve Trucking’s org, with the token from module 1 in the Authorization: Bearer header, and every address starts the same way:
https://steve-trucking.my.salesforce.com/services/data/v68.0/Pin the Version
Section titled “Pin the Version”That v68.0 is a choice. Salesforce also accepts latest in its place, and routes the call to the newest version the org supports. Salesforce’s own advice on when to use which is clear: “Use it for scripts and internal tools. Keep a pinned version for production integrations that depend on a specific contract” (The Salesforce Developer’s Guide to the Winter ‘27 Release (opens in a new tab), checked 2026-10-31).
That fits everything in module 1. A pinned version keeps its promise, and you know which number to look for when Salesforce announces a retirement. latest changes under you three times a year. For Redrock’s system, pin it.
Query: Ask for a List
Section titled “Query: Ask for a List”The query is the call you’ll make most. It’s a GET, so the question goes in the address. You pass a SOQL statement in a parameter called q, with the spaces encoded:
GET /services/data/v68.0/query?q=SELECT+Id,Status+FROM+WorkOrder+WHERE+Status='Scheduled'What comes back is a page of records, not always all of them. Salesforce returns “up to 2,000 records… at a time”, and sometimes fewer, depending on how big the records are (Query (opens in a new tab), REST API Developer Guide, Winter ‘27, checked 2026-10-31). So the answer tells you where you are:
{ "totalSize": 3300, "done": false, "nextRecordsUrl": "/services/data/v68.0/query/01gRO0000016PIAYA2-2000", "records": [ ... ]}done is false, so there’s more. Call nextRecordsUrl, and you get the next page, and the next, until done is true. REST is stateless, so this is about the only place where one call depends on the one before. The server holds your place for you.
Three things architects get wrong with the query:
- Reading only the first page. A program that never checks
doneworks perfectly in a sandbox with 200 deliveries, and quietly misses most of them in production. A day of Steve Trucking’s deliveries, 3,300 of them, is already two pages. - Asking for too much. You can ask for a smaller page with a header,
Sforce-Query-Options: batchSize=500, between 200 and 2,000. Salesforce says there’s “no guarantee” you’ll get exactly that size (Query Options Header (opens in a new tab), checked 2026-10-31), so the program still followsnextRecordsUrl. - Forgetting the query has to be fast. An outside query is still a query on Salesforce’s database. Add
explaininstead ofq, and Salesforce tells you how it would run it, the same feedback the Query Plan tool gives inside the org. In my video on the query, I asked it about a query with no filter on 74,000 records, and it answered with a table scan: every row read. Add a selective filter and ask again, and the plan and its cost change. You can test the shape of a query from outside before anyone runs it for real. Data Architect’s How Salesforce Finds Data explains what to look for.
One more variation: queryAll instead of query. It “returns records that are soft deleted due to a merge or delete”, while they’re still in the recycle bin, and archived tasks and events (QueryAll (opens in a new tab), checked 2026-10-31). A copy that has to know what was deleted asks with queryAll.
And if you’re paging through tens of thousands of rows, the query is telling you something. If you really need large amounts of data, you should be looking at the bulk API (chapter 10).
And the address has a size limit. Chapter 3 showed why a long question in a GET can outgrow it. When a query gets that big, that’s usually a sign to ask a better question, or to use a different door.
Describe: Ask About the Shape
Section titled “Describe: Ask About the Shape”Sometimes the program needs to know the shape of the data, not the data. You can be outside Salesforce and still read the metadata:
GET /services/data/v68.0/sobjects/WorkOrder/describe/That returns everything about the object: every field, its type, its picklist values, its relationships. A program that builds its own screens, or checks that a field still exists before it writes to it, starts here.
And it’s live. In my video on the describe, I added a third value to a picklist in Setup, described the object again straight away, and the new value was there. The describe also hands you links to follow: the object’s layouts, its list views, its compact layouts. And leave the object off, GET /services/data/v68.0/sobjects/, and you get the global describe: the list of objects. But only the objects the signed-in user can see. The integration user’s describe is the integration user’s world, not the whole org.
It’s a big answer, so don’t ask for it on every call. Send an If-Modified-Since header with the time you last asked. If nothing has changed, Salesforce answers 304 Not Modified with no body (sObject Describe (opens in a new tab), checked 2026-10-31).
One Record, by Its ID
Section titled “One Record, by Its ID”When you have a record’s ID, you can get the record:
GET /services/data/v68.0/sobjects/WorkOrder/0WOxx0000004C92GAEAll I had to send was the one ID, and it gives me everything. I didn’t write a SOQL statement listing fields; I got every field the integration user can see. That’s handy when you’re exploring. In production, it’s usually too much. Add ?fields=Status,Partner_Delivery_Key__c and you get only what you asked for (Get Records Using sObject Rows (opens in a new tab), checked 2026-10-31). Less data on the wire, and less to change when someone adds a field.
Create, Update and Upsert
Section titled “Create, Update and Upsert”Writing follows the verbs from chapter 3. POST is new, PATCH is update.
- Create:
POST /sobjects/WorkOrder/with the fields in the body. The answer is201 Createdand the new record’s ID. - Update:
PATCH /sobjects/WorkOrder/0WOxx0000004C92GAEwith only the fields that change. The answer is204 No Content: it worked, and there’s nothing to send back. - Delete:
DELETEon the same address.
Look at what update needs: Salesforce’s ID. Redrock doesn’t have it. Redrock knows its own delivery number, 8472914. If Redrock has to look up our ID before every update, that’s two calls instead of one, and Redrock now has to keep a map of our IDs.
That’s what the external ID is for. Steve Trucking’s Work Orders carry the partner’s key in one field, Partner_Delivery_Key__c, as Data Architect’s keys module designs it. Then Redrock calls with its own key:
PATCH /services/data/v68.0/sobjects/WorkOrder/Partner_Delivery_Key__c/REDROCK-8472914-1That’s an upsert. If no Work Order has that key, Salesforce creates one and answers 201 Created. If one does, it updates it and answers 200 OK. If more than one has it, Salesforce answers 300 and changes nothing (sObject Rows by External ID (opens in a new tab) and the upsert example, checked 2026-10-31). Redrock never needs our IDs, and a retry after a timeout doesn’t make a second delivery. For an integration, upsert by an external ID is usually the right first answer.
Your Automations Still Run
Section titled “Your Automations Still Run”From outside, inserting a record is fast and easy. But it isn’t a back door. When Redrock’s call creates a Work Order, every trigger, every flow and every validation rule on Work Order runs, exactly as if a person had clicked Save. The integration user’s permissions and sharing apply too.
That’s good: your rules protect the data whichever door it came through. It also means a slow flow is now slow for Redrock, and a validation rule written for people (“please fill in the dispatcher’s notes”) can stop an integration cold. When you design a call-in, read what runs on the object it writes to.
What Changed Since?
Section titled “What Changed Since?”One more call, for programs that keep a copy. Ask which records changed in a time window:
GET /services/data/v68.0/sobjects/WorkOrder/updated/?start=2026-10-30T00:00:00Z&end=2026-10-31T00:00:00ZYou get back the IDs, not the records, and a matching deleted call gives you what was removed. Salesforce built it for “data replication applications”, with two limits to design around: it looks back no more than 30 days, and it returns up to 600,000 IDs before it fails (sObject Get Updated (opens in a new tab), checked 2026-10-31). A copy that falls more than 30 days behind can’t catch up this way.
One Record at a Time
Section titled “One Record at a Time”Everything in this module works on one record per call: one query page, one create, one update. That’s simple and fast, and for Redrock sending a few updates an hour, it’s all you need.
But every call counts against the org’s daily API allocation (module 7), and some work isn’t one record. A Work Order with its line items is several records that belong together. That’s the next module: many in one call. And when it’s thousands of records, it’s chapter 10, the bulk door.
Watch it on video
Section titled “Watch it on video”In 2023 I made three short videos on the REST API, live in Postman: the query, the describe and retrieve, and insert and update. The notes under the videos say what’s still right and what’s changed since.
What an Architect Decides Here
Section titled “What an Architect Decides Here”- Pinned version or
latest. Pin it for production integrations; write the number in the landscape inventory. - The key the caller uses. An external ID on the object, so callers upsert with their own key instead of keeping a map of yours.
- How much each call asks for. Only the fields it needs, with
fieldsor a SOQL field list; a page size the caller can handle;nextRecordsUrlfollowed to the end. - What runs when the call writes. The triggers, flows and validation rules on the object, read with the integration in mind.
- One at a time, or more. A few records an hour: REST, one call each. Records that belong together: module 3. Thousands: chapter 10.
Explore
Section titled “Explore”- REST API Developer Guide (opens in a new tab): Salesforce Developers, Winter ‘27; the Query, sObject Describe, sObject Rows, sObject Rows by External ID and sObject Get Updated resources are in its reference (checked 2026-10-31)
- The Salesforce Developer’s Guide to the Winter ‘27 Release (opens in a new tab): Salesforce Developers Blog, 2026-10-05, on
latestand pinned versions - Keys and Write Ownership: Data Architect, one external ID for every partner
- The API Guide: every REST door, with a link to Salesforce’s guide
- Salesforce Developers, REST API Developer Guide, Query (opens in a new tab), checked 2026-11-07.
- Salesforce Developers, REST API Developer Guide, Query Options Header (opens in a new tab), checked 2026-11-07.
- Salesforce Developers, REST API Developer Guide, QueryAll (opens in a new tab), checked 2026-11-07.
- Salesforce Developers, REST API Developer Guide, sObject Describe (opens in a new tab), checked 2026-11-07.
- Salesforce Developers, REST API Developer Guide, Get Records Using sObject Rows (opens in a new tab), checked 2026-11-07.
- Salesforce Developers, REST API Developer Guide, sObject Rows by External ID (opens in a new tab), checked 2026-11-07.
- Salesforce Developers, REST API Developer Guide, sObject Get Updated (opens in a new tab), checked 2026-11-07.
