Not verified hands-on. This article was written from primary documentation rather than reproduced in an org, so it carries no tested-release badge. If you run it and something differs, say so in the comments and it gets tested and corrected.

Straight answer: External Services takes an OpenAPI description of somebody else’s REST service and generates invocable actions you can drop into Flow without writing Apex. It works well for small, well-behaved APIs with flat request and response shapes. It falls over on anything with polymorphic schemas, deep nesting or binary payloads, and when it does, the failure arrives at registration time with a message that does not always explain itself.

What registration actually does

You create an External Service in Setup, point it at a named credential, and supply the schema either by uploading it or by having Salesforce fetch it from a URL. Salesforce parses the document, shows you the operations it found, and lets you select which ones to generate.

On save it produces Apex classes representing the request and response types, plus an invocable action per selected operation. Those actions appear in the Flow action picker under the service name. You never edit the generated Apex. Re-registering the schema regenerates it.

The named credential is doing the real work of authentication. External Services does not handle OAuth on its own, so get the named credential working first with a test callout before you register anything. Debugging an authentication failure through a generated action is considerably less pleasant than debugging it directly.

The schema constraints

This is where projects stall. OpenAPI 2.0 and 3.0 are both accepted, and JSON is the format least likely to cause an argument. The constraints that matter:

  • No polymorphism. oneOf, anyOf and allOf are not supported. A schema that composes types this way has to be flattened before registration.
  • No recursion. A type referring to itself, directly or through another type, cannot be generated into Apex classes.
  • Limited nesting depth. Deeply nested objects will be rejected. Flatten the response or wrap the service.
  • JSON only. No multipart, no file upload, no binary responses, no XML.
  • Named types. Inline anonymous schemas generate awkwardly. Define your types in the components section and reference them.
  • Size. There is a ceiling on the schema document, and a full enterprise API description will exceed it. Trim the document to the operations you actually need.

Trimming is the technique that rescues most of these. You do not have to register the provider’s entire specification. Write a reduced document describing only the three operations you want, with flattened types, and register that. It is a file you own and version, and it doubles as documentation of what your org depends on.

Using it in Flow

The generated action takes inputs matching the operation’s parameters and body, and returns outputs typed as Apex-defined variables. Collections come back as collections of those types, which you iterate with a loop like anything else.

Two things to get right. First, put a fault path on the action. Without one, a non-2xx response ends the flow with an unhelpful error for the user. Second, respect the callout rules: a flow that performs DML and then calls out in the same transaction will fail, so put the callout before the DML or move it into an asynchronous path.

Callouts from Flow count against the same per-transaction callout limits as Apex, so a loop calling an external service once per record in a collection of two hundred is a design to avoid.

When to use the HTTP Callout action instead

Flow has a built-in HTTP Callout action where you paste a sample response and Salesforce infers the structure. For a single endpoint, it is faster to set up and easier to change, with no schema document to maintain.

External Services earns its extra effort when you are calling several operations on the same service, when the provider publishes a specification you want to track for changes, or when you want the generated Apex types available to Apex code as well as to Flow. For one GET against one endpoint, use the HTTP Callout action.

Frequently asked

Can I call a generated action from Apex? Yes. The generated classes are Apex, and you can invoke the operation directly rather than through the invocable wrapper. The types are regenerated on re-registration, so avoid depending on internals.

What happens when the provider changes their API? Nothing, until you re-register. Your org keeps calling with the old shape and starts getting errors. Re-register deliberately and retest the flows that use it.

Does it support callbacks or long-running operations? No. It is request and response, synchronous. Anything asynchronous needs a different design.

Why does my schema register but return nothing? Usually the response schema does not match what the service actually sends, so the generated deserialisation produces empty objects. Capture a real response and compare it field by field.