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)