afv-library/skills/integration-connectivity-generate/references/external-services-guide.md

8.4 KiB

External Services Guide

Overview

External Services in Salesforce automatically generate Apex classes from OpenAPI (Swagger) specifications, enabling type-safe REST API integrations without writing HTTP code.

How It Works

┌─────────────────────────────────────────────────────────────────┐
│  EXTERNAL SERVICE FLOW                                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. OpenAPI Spec (JSON/YAML)                                    │
│     ↓                                                           │
│  2. External Service Registration (Metadata)                    │
│     ↓                                                           │
│  3. Auto-generated Apex Classes                                 │
│     ↓                                                           │
│  4. Type-safe API calls from Apex                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Prerequisites

  1. Named Credential configured for authentication
  2. OpenAPI Specification (2.0 or 3.0) for the API
  3. API Version 48.0+ (Winter '20)

Creating External Service

Via Setup UI

  1. Go to Setup → External Services
  2. Click New External Service
  3. Provide name and description
  4. Select Named Credential
  5. Upload or paste OpenAPI spec
  6. Review operations
  7. Save

Via Metadata API

Template: assets/external-services/openapi-registration.externalServiceRegistration-meta.xml Reference: assets/external-services/external-service-operations.md

<?xml version="1.0" encoding="UTF-8"?>
<ExternalServiceRegistration xmlns="http://soap.sforce.com/2006/04/metadata">
    <label>Stripe API</label>
    <description>Stripe payment processing API</description>
    <namedCredential>Stripe_API</namedCredential>
    <schemaType>OpenApi3</schemaType>
    <schema>
{
  "openapi": "3.0.0",
  "info": { "title": "Stripe API", "version": "1.0" },
  "paths": { ... }
}
    </schema>
    <status>Complete</status>
</ExternalServiceRegistration>

Generated Classes

For an External Service named "StripeAPI":

Class Purpose
ExternalService.StripeAPI Main service class with operation methods
ExternalService.StripeAPI_createCustomer_Request Request wrapper
ExternalService.StripeAPI_createCustomer_Response Response wrapper
ExternalService.StripeAPI_Customer DTO matching schema

Usage Patterns

Basic GET Request

// Instantiate service
ExternalService.StripeAPI stripe = new ExternalService.StripeAPI();

// Call GET operation
ExternalService.StripeAPI_getCustomer_Response response =
    stripe.getCustomer('cus_ABC123');

// Access response data
String email = response.email;

POST with Request Body

// Create request object
ExternalService.StripeAPI_createCustomer_Request request =
    new ExternalService.StripeAPI_createCustomer_Request();
request.email = 'customer@example.com';
request.name = 'John Doe';

// Make call
ExternalService.StripeAPI_createCustomer_Response response =
    stripe.createCustomer(request);

// Get created resource ID
String customerId = response.id;

Handling Nested Objects

// Access nested data
ExternalService.StripeAPI_Address address = response.address;
String city = address.city;
String postalCode = address.postalCode;

Error Handling

try {
    ExternalService.StripeAPI stripe = new ExternalService.StripeAPI();
    response = stripe.getCustomer('invalid_id');

} catch (ExternalService.ExternalServiceException e) {
    // API returned error response
    System.debug('Status Code: ' + e.getStatusCode());
    System.debug('Error Body: ' + e.getBody());
    System.debug('Error Message: ' + e.getMessage());

} catch (CalloutException e) {
    // Network/connection error
    System.debug('Connection failed: ' + e.getMessage());
}

Async Calls (Queueable)

Use Queueable for calls from triggers:

public class StripeCustomerSync implements Queueable, Database.AllowsCallouts {

    private Account account;

    public StripeCustomerSync(Account account) {
        this.account = account;
    }

    public void execute(QueueableContext context) {
        ExternalService.StripeAPI stripe = new ExternalService.StripeAPI();

        ExternalService.StripeAPI_createCustomer_Request req =
            new ExternalService.StripeAPI_createCustomer_Request();
        req.email = account.Email__c;
        req.name = account.Name;

        try {
            ExternalService.StripeAPI_createCustomer_Response resp =
                stripe.createCustomer(req);

            account.Stripe_Customer_Id__c = resp.id;
            update account;
        } catch (Exception e) {
            System.debug('Sync failed: ' + e.getMessage());
        }
    }
}

OpenAPI Schema Tips

Supported Features

  • GET, POST, PUT, PATCH, DELETE methods
  • Path and query parameters
  • Request and response bodies
  • JSON schema types (string, number, boolean, object, array)
  • References ($ref)
  • Basic authentication headers

Limitations

  • No file uploads (multipart/form-data limited)
  • No WebSockets (HTTP only)
  • No streaming responses
  • Some complex schemas may not parse
  • Maximum schema size limited

Schema Best Practices

{
  "openapi": "3.0.0",
  "info": {
    "title": "My API",
    "version": "1.0.0"
  },
  "paths": {
    "/customers/{id}": {
      "get": {
        "operationId": "getCustomer",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Customer" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Customer": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "email": { "type": "string" }
        }
      }
    }
  }
}

Updating External Service

When API changes:

  1. Get updated OpenAPI spec
  2. Go to Setup → External Services
  3. Edit service
  4. Upload new schema
  5. Review changes to operations
  6. Save and validate
  7. Update calling code if signatures changed

Testing

@isTest
private class StripeIntegrationTest {

    @isTest
    static void testCreateCustomer() {
        // Set mock
        Test.setMock(HttpCalloutMock.class, new StripeMock());

        Test.startTest();

        ExternalService.StripeAPI stripe = new ExternalService.StripeAPI();
        ExternalService.StripeAPI_createCustomer_Request req =
            new ExternalService.StripeAPI_createCustomer_Request();
        req.email = 'test@example.com';

        ExternalService.StripeAPI_createCustomer_Response resp =
            stripe.createCustomer(req);

        Test.stopTest();

        System.assertEquals('cus_test123', resp.id);
    }

    private class StripeMock implements HttpCalloutMock {
        public HttpResponse respond(HttpRequest request) {
            HttpResponse response = new HttpResponse();
            response.setStatusCode(201);
            response.setBody('{"id": "cus_test123", "email": "test@example.com"}');
            return response;
        }
    }
}

Use with Agentforce

External Services are ideal for Agent Actions:

  1. Create External Service from API spec
  2. Create Flow that calls External Service
  3. Reference Flow in Agent Script action
actions:
    lookup_customer:
        description: "Looks up customer in payment system"
        inputs:
            customer_email: string
        outputs:
            customer_id: string
        target: "flow://Lookup_Stripe_Customer"