Apex Web Services
Expose custom REST endpoints at https://<my-domain>.my.salesforce.com/services/apexrest/<urlMapping>:
28.1 Apex REST
Expose custom REST endpoints at https://<my-domain>.my.salesforce.com/services/apexrest/<urlMapping>:
Notes: the class must be global, methods global static; clients authenticate with OAuth 2.0 (Connected App or External Client App) and send Authorization: Bearer <token>; parameters of the POST method are mapped from JSON body keys; query parameters are in req.params; set headers with RestContext.response.addHeader(...) and raw bodies with RestContext.response.responseBody = Blob.valueOf(jsonString).
Test Apex REST by populating RestContext.request and RestContext.response manually:
@RestResource(urlMapping='/support/cases/*')
global with sharing class CaseRestService {
// GET /services/apexrest/support/cases/500xx0000000001
@HttpGet
global static CaseDto getCase() {
RestRequest req = RestContext.request;
String caseId = req.requestURI.substringAfterLast('/');
List<Case> found = [SELECT Id, CaseNumber, Subject, Status FROM Case WHERE Id = :caseId WITH USER_MODE];
if (found.isEmpty()) {
RestContext.response.statusCode = 404;
return null;
}
return new CaseDto(found[0]);
}
// POST with JSON body {"subject": "...", "priority": "High"}
@HttpPost
global static Id createCase(String subject, String priority) {
Case c = new Case(Subject = subject, Priority = priority, Origin = 'Web');
insert as user c;
RestContext.response.statusCode = 201;
return c.Id;
}
// PATCH using the raw body
@HttpPatch
global static void updateStatus() {
RestRequest req = RestContext.request;
String caseId = req.requestURI.substringAfterLast('/');
Map<String, Object> body = (Map<String, Object>) JSON.deserializeUntyped(req
…@isTest
static void getsCase() {
Case c = new Case(Subject = 'Test');
insert c;
RestRequest req = new RestRequest();
req.requestURI = '/services/apexrest/support/cases/' + c.Id;
req.httpMethod = 'GET';
RestContext.request = req;
RestContext.response = new RestResponse();
CaseRestService.CaseDto dto = CaseRestService.getCase();
Assert.areEqual('Test', dto.subject);
}28.2 Apex SOAP web services
Generate the WSDL from *Setup → Apex Classes → WSDL*; clients log in via the SOAP API and call the service at /services/Soap/class/AccountSoapService. SOAP services are mostly used for legacy enterprise integrations; prefer REST for new work.
global with sharing class AccountSoapService {
webservice static Id createAccount(String name, String industry) {
Account a = new Account(Name = name, Industry = industry);
insert as user a;
return a.Id;
}
global class AccountInfo {
webservice String name;
webservice Decimal revenue;
}
}28.3 Choosing an API
CRUD on records Standard REST API / Composite API (no custom code)
Complex, multi-object business operation in one call Flexible reads for front ends
Agentforce/Flow consumption of your logic Invocable Apex (§32, §40)