---
term: 'API Orchestration'
seoTitle: 'API Orchestration: Sagas, Latency Math, Gateway vs. BFF'
headline: 'What is API Orchestration?'
slug: api-orchestration
category: backend-compute
shortDefinition: 'API orchestration is a pattern where one coordinating layer calls multiple APIs in sequence and returns a single combined result.'
relatedTerms:
  - api-gateway-architecture
  - event-driven-architecture
  - webhooks
  - microservices-vs-monolith
contrastsWith:
  - api-gateway-architecture
aboutTerms:
  - 'Orchestrator'
  - 'Saga / Compensation'
  - 'API Composition'
faq:
  - question: 'What is API orchestration?'
    answer: 'Coordinating multiple API calls into one managed workflow: a single request arrives, the orchestrator calls each backend service in the right order with the right data — handling dependencies, transformations, retries, and errors — and one combined response goes out. The conductor of the orchestra, with the APIs as sections.'
  - question: 'What is the difference between orchestration and choreography?'
    answer: 'Orchestration has a central coordinator that commands services and tracks state — visible, debuggable, and a coupling point. Choreography has no coordinator: services react to each other''s events — maximally decoupled, but the workflow exists nowhere explicitly. The rule: orchestrate when someone must own the outcome; choreograph when producers genuinely don''t care what happens next.'
  - question: 'Is an API gateway an orchestrator?'
    answer: 'No — a gateway is a reverse proxy handling cross-cutting concerns per request: auth, rate limits, routing. Orchestration manages multi-step workflow logic with state between steps. Gateways can do light response aggregation; the moment step two depends on step one''s output, you''ve left gateway territory.'
  - question: 'What is the difference between orchestration and aggregation?'
    answer: 'State. Aggregation (API composition) fans out independent calls — usually in parallel — and merges the responses; no call depends on another. Orchestration is sequential and conditional: step N''s input comes from step N−1''s output, failures need compensation, and the flow itself is logic.'
  - question: 'What is an example of API orchestration?'
    answer: 'Checkout, canonically: validate the cart, reserve inventory, charge the card, create the shipment, send confirmation — five APIs, strict order, and a failure at any step must undo what came before. Travel booking and user onboarding follow the same shape.'
  - question: 'How do you handle a failure in the middle of an orchestrated flow?'
    answer: 'There is no rollback across services — a charged card doesn''t un-charge because shipping errored. The saga pattern answers with compensating actions: explicitly undo completed steps (refund the charge, release the reservation), retry transient failures with idempotency keys so a retried charge can''t double-bill, and bound every step with a timeout.'
  - question: 'Should orchestrated calls run sequentially or in parallel?'
    answer: 'Parallel wherever no data dependency exists, sequential only where one forces it. The math is the argument: three independent 200 ms calls cost 600 ms in sequence and about 200 ms in parallel — the orchestrator''s cheapest optimization is reading the dependency graph honestly.'
  - question: 'When should you use API orchestration?'
    answer: 'When a client would otherwise make three or more dependent calls, when steps need ordering and shared error handling, or when mobile networks make chatty round trips expensive. Skip it for single-service calls and pure parallel reads — that''s aggregation, which is simpler.'
  - question: 'Do I need a workflow engine, or is a function enough?'
    answer: 'A plain server-side function is enough for short, synchronous flows — a few steps, a few seconds, failure returns an error to the client. Durable workflow engines earn their weight when flows are long-running, must survive restarts, or need scheduled retries and human approval steps.'
  - question: 'How does the backend-for-frontend pattern relate?'
    answer: 'A BFF is a per-client backend that aggregates and reshapes downstream calls — the most common home for lightweight orchestration code. The discipline: BFFs orchestrate and transform, but business decisions stay in the downstream services that own the data.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'API Composition pattern — microservices.io'
    url: 'https://microservices.io/patterns/data/api-composition.html'
  - name: 'Saga pattern — microservices.io'
    url: 'https://microservices.io/patterns/data/saga.html'
  - name: 'Process Manager — Enterprise Integration Patterns'
    url: 'https://www.enterpriseintegrationpatterns.com/patterns/messaging/ProcessManager.html'
  - name: 'Backends For Frontends — Sam Newman'
    url: 'https://samnewman.io/patterns/architectural/bff/'
cta:
  title: 'The orchestrator you already have'
  text: 'A Back4app Cloud Code function is lightweight orchestration in one file: call payment, inventory, and shipping with server-held keys, compensate on failure, and hand the client one clean response.'
  linkText: 'Start building free'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-07-30'
translationKey: api-orchestration
---

**API orchestration is a pattern where one coordinating layer calls multiple APIs in sequence and returns a single combined result.** The conductor metaphor is universal because it's exact: each API plays its part, but the *score* — ordering, dependencies, what happens when the brass section fails — lives with the orchestrator. One confusion to clear immediately: half the industry uses "orchestration" to mean whatever its product category does (gateways, automation platforms, workflow engines, GraphQL routers all claim the word). This entry means the pattern itself — and disambiguates the products below.

## Key takeaways

| Question | Answer |
| --- | --- |
| The shape | One request in → ordered, conditional calls out → one response back |
| vs. aggregation | Aggregation fans out stateless parallel reads; orchestration is stateful sequence |
| vs. choreography | Central command vs. [distributed reaction to events](/glossary/event-driven-architecture/) |
| The failure truth | No cross-service rollback exists — compensation (sagas) is the answer |
| The latency lever | Parallelize everything the dependency graph doesn't forbid |

## The checkout, orchestrated

**JavaScript:**

```javascript
// JavaScript — Cloud Code (cloud/main.js)
// Lightweight orchestration: one function owns the checkout flow
Parse.Cloud.define('checkout', async (req) => {
  const { cartId } = req.params;

  // Independent lookups run in PARALLEL (~200 ms, not 400)
  const [cart, address] = await Promise.all([
    loadCart(cartId),
    loadAddress(req.user),
  ]);

  const reservation = await reserveInventory(cart);        // step 1
  try {
    const charge = await chargeCard(req.user, cart, {
      idempotencyKey: cartId,                              // safe to retry
    });
    const shipment = await createShipment(charge, address); // step 3
    return { orderId: shipment.orderId };                  // one response out
  } catch (e) {
    await releaseInventory(reservation); // compensate — no rollback exists
    throw e;
  }
});
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// The client sees ONE call — the orchestrator owns the sequence
final result = await ParseCloudFunction('checkout')
    .execute(parameters: {'cartId': cartId});
showConfirmation(result.result['orderId']);
// Without orchestration this screen would call payment, inventory,
// and shipping itself — three round trips, and the error handling too.
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// The client sees ONE call — the orchestrator owns the sequence
let result: [String: String] = try await Cloud.run(
    name: "checkout", parameters: ["cartId": cartId])
showConfirmation(result["orderId"] ?? "")
// Without orchestration this screen would call payment, inventory,
// and shipping itself — three round trips, and the error handling too.
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// The client sees ONE call — the orchestrator owns the sequence
val params = mapOf("cartId" to cartId)
val result = ParseCloud.callFunction<Map<String, Any>>("checkout", params)
showConfirmation(result["orderId"] as String)
// Without orchestration this screen would call payment, inventory,
// and shipping itself — three round trips, and the error handling too.
```

Everything the pattern is, in one function: independent lookups parallelized, dependent steps sequenced, an idempotency key making the dangerous step retryable, and a compensation in the catch block — because the inventory reservation doesn't un-reserve itself when the card declines.

```mermaid
flowchart LR
  accTitle: Orchestrated checkout flow with compensation on failure
  accDescr: A single client request reaches the orchestrator, which runs independent lookups in parallel, then sequences dependent steps: reserve inventory, charge the card, create the shipment. A failure after the reservation triggers a compensating release of the inventory before the error returns, since no automatic rollback exists across services.
  C["Client<br/>one call"] --> O["Orchestrator"]
  O -->|"parallel"| L1["Load cart"]
  O -->|"parallel"| L2["Load address"]
  L1 --> S1["1 · Reserve inventory"]
  L2 --> S1
  S1 --> S2["2 · Charge card<br/>(idempotency key)"]
  S2 -->|"ok"| S3["3 · Create shipment"] --> R["One response"]
  S2 -.->|"fails"| X["Compensate:<br/>release reservation"] -.-> E["Error to client"]
```

## Gateway vs. BFF vs. aggregation vs. orchestration vs. choreography

The five-way table no single competitor provides:

| | What it is | State between steps | Where logic lives | Example |
| --- | --- | --- | --- | --- |
| [API gateway](/glossary/api-gateway-architecture/) | Reverse proxy for cross-cutting concerns | None | Config: auth, rate limits, routing | One entry point for all APIs |
| [BFF](/glossary/api-gateway-architecture/) | Per-client backend | Per request | Reshaping for one frontend | Mobile BFF trimming payloads |
| Aggregation / [composition](https://microservices.io/patterns/data/api-composition.html) | Parallel fan-out + merge | **None — stateless** | An in-memory join | Dashboard reading 4 services |
| **Orchestration** | Commanded multi-step workflow | **Yes — step N feeds step N+1** | The orchestrator's sequence + error logic | Checkout, onboarding |
| Choreography | Services reacting to events | Distributed, implicit | Each consumer, [no central brain](/glossary/event-driven-architecture/) | Order events fanning out |

The last two rows are the deep pairing, mirrored in the [EDA entry](/glossary/event-driven-architecture/) with the same rule: **orchestrate when someone must own the outcome of a workflow; choreograph when producers genuinely don't care what happens next.** Orchestrators speak *commands* (imperative, refusable); choreography speaks *events* (past-tense facts) — same distinction, architectural scale.

## When step 3 of 5 fails

The section the SERP skips, and the reason orchestration is engineering rather than plumbing. Distributed steps have **no shared transaction**: a completed charge cannot be rolled back by the database that never knew about it. The [saga pattern](https://microservices.io/patterns/data/saga.html) is the honest answer — each step is a local action paired with a *compensating* action (charge ↔ refund, reserve ↔ release), and failure runs the compensations for everything already done. Three disciplines make it work: **idempotency keys** on dangerous steps, so a timeout-and-retry can't double-charge (the payment retried with the same key is recognized, not repeated); **timeouts per step**, so one hung dependency doesn't hang the flow; and **explicit terminal states**, because a flow that half-completed and compensated is a *known outcome* to record, not an exception to swallow. Orchestration's gift is that all of this lives in one visible place — which is also its cost: that place must be scaled, monitored, and kept honest.

## The latency math

Three calls at 200 ms each: sequential = **600 ms**; parallel = **~200 ms**. The orchestrator's first optimization is not caching or clever transport — it's reading the dependency graph truthfully: cart and address loads (independent) run together; the charge (needs the cart) waits; the shipment (needs the charge) waits for that. Most orchestrated flows are a short sequential spine with parallel branches hanging off it, and every step wrongly placed on the spine is user-visible latency donated to nothing.

## Code or engine?

A **plain function is enough** when the flow is short and synchronous: a handful of steps, seconds of total budget, and "it failed" is an acceptable answer to return to a waiting client — which describes most app backends' orchestration needs, and is exactly what a [server-side function](/glossary/cloud-code-serverless-functions/) provides. A **durable workflow engine** (open-source examples: Temporal, Camunda) earns its operational weight when flows are long-running (minutes to days), must survive process restarts mid-flow, or need scheduled retries, human approval steps, and replayable history. The upgrade path is real but rarely urgent; the anti-pattern is deploying an engine for a three-call checkout — or hand-rolling durable state in a function that has quietly become an engine.

## Common use cases

- **Checkout and payment flows** — the canonical dependent sequence with money on the line.
- **User onboarding** — create account, verify identity, provision resources, send welcome: ordered and compensatable.
- **Mobile screen assembly** — one orchestrated call replacing three chatty round trips over a [high-latency network](/glossary/api-payload-optimization/).
- **Third-party bundles** — KYC checks, shipping quotes, enrichment: several vendors, one answer, keys held [server-side](/glossary/api-key-security/).
- **Migration seams** — an orchestrator hiding old-system/new-system splits behind one stable API during a [monolith breakup](/glossary/microservices-vs-monolith/).

## Should you orchestrate? A decision matrix

| Situation | Reach for |
| --- | --- |
| 3+ dependent calls behind one user action | Orchestration — a function first |
| Independent parallel reads for one screen | Aggregation — simpler, stateless |
| Cross-cutting concerns (auth, limits) | The [gateway](/glossary/api-gateway-architecture/) — not workflow logic |
| Reactions producers don't care about | [Events / choreography](/glossary/event-driven-architecture/) |
| Days-long flows with human steps | A durable workflow engine |
| One backend call | Nothing — call it |

## Limitations and trade-offs

- **The orchestrator is a dependency magnet.** It knows every service in the flow; changes downstream ripple into it — the visibility and the coupling are the same property.
- **It's on the hot path.** Every flow transits it, so its latency, scaling, and availability budget belong to the product, not the infrastructure footnote.
- **Compensation is not undo.** A refund is a new event with its own failure modes, not a time machine; sagas trade atomicity for explicitness, and the explicitness must be handled.
- **Business logic migrates in.** Left ungoverned, the orchestrator absorbs decisions that belong in the services that own the data — sequence here, semantics there.
- **Sync orchestration inherits sync limits.** A client waiting through five steps is waiting; flows that outgrow the request window belong to [jobs](/glossary/background-jobs-task-schedulers/) with status, not longer timeouts.

## Orchestration on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. Lightweight orchestration is what a [Cloud Code function](/glossary/cloud-code-serverless-functions/) does naturally, and the code tabs show the full pattern: one `checkout` function that parallelizes the independent reads, sequences the dependent steps, holds the third-party keys [server-side](/glossary/api-key-security/), compensates in the catch block, and returns one response to a mobile client that made exactly one call. The function runs next to the database (order state and terminal outcomes are one write away), auth context arrives on `request.user`, and there is no separate orchestration tier to deploy or scale. When a flow someday outgrows the request window — approvals, day-long waits — that's the graduation to a workflow engine; until then, the orchestrator is a file in your repo.
