apex-callout-retry-and-resilience
Strategy layer for resilient Apex HTTP callouts: bounded retry with backoff, queueable async retry chains, circuit-breaker via Platform Cache, idempotency keys, dead-letter pattern. NOT for callout authentication — see apex-named-credentials-patterns. NOT for transaction-boundary rules — see callout-and-dml-transaction-boundaries. NOT a re-write of the HttpClient template — this is the policy and orchestration layer that calls it.
Best use case
apex-callout-retry-and-resilience is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Strategy layer for resilient Apex HTTP callouts: bounded retry with backoff, queueable async retry chains, circuit-breaker via Platform Cache, idempotency keys, dead-letter pattern. NOT for callout authentication — see apex-named-credentials-patterns. NOT for transaction-boundary rules — see callout-and-dml-transaction-boundaries. NOT a re-write of the HttpClient template — this is the policy and orchestration layer that calls it.
Teams using apex-callout-retry-and-resilience should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/apex-callout-retry-and-resilience/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How apex-callout-retry-and-resilience Compares
| Feature / Agent | apex-callout-retry-and-resilience | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Strategy layer for resilient Apex HTTP callouts: bounded retry with backoff, queueable async retry chains, circuit-breaker via Platform Cache, idempotency keys, dead-letter pattern. NOT for callout authentication — see apex-named-credentials-patterns. NOT for transaction-boundary rules — see callout-and-dml-transaction-boundaries. NOT a re-write of the HttpClient template — this is the policy and orchestration layer that calls it.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Apex Callout Retry and Resilience Activate when an Apex integration must survive transient failures from a downstream system: 5xx errors, network timeouts, 429 rate limits, brief endpoint outages. This skill is the **strategy layer** — it tells you when to retry, how many times, on what schedule, when to stop, and where to park failures. The HTTP plumbing itself lives in `templates/apex/HttpClient.cls`. ## Before Starting - Confirm the operation is **idempotent** (safe to repeat) before retrying writes. If not, an Idempotency-Key contract with the downstream is mandatory. - Decide **sync vs async** up front. Synchronous retries fight a 120-second cumulative cap; async retries (Queueable / Platform Event) get a fresh transaction per attempt. - Identify **what's retry-eligible** vs not. 5xx, network timeouts, 408, 429, 503 retry. 400, 401, 403, 404, 422 do NOT retry; these are caller bugs or auth failures. ## Core Concepts ### Retry classification | Status / Failure | Retry? | Why | |---|---|---| | Network timeout, no response | Yes | Transient | | 408 Request Timeout | Yes | Transient | | 429 Too Many Requests | Yes (honor `Retry-After`) | Rate limit | | 500, 502, 503, 504 | Yes | Server-side transient | | 400, 422 | No | Caller payload bug | | 401, 403 | No | Auth / authz; refresh token elsewhere | | 404 | No | Resource doesn't exist | ### The Apex synchronous constraint A synchronous Apex transaction has a **120-second cumulative callout cap** and **no usable `Thread.sleep`** (Apex offers no sleep primitive — busy-wait loops via `System.now()` polling are governor-killers and forbidden). This forces synchronous retries to be: - **Bounded** — typical 3 attempts max. - **Short-backoff** — 100ms / 500ms / 2000ms via the only legal "delay": staying inside the same callout's longer timeout, OR doing minimal CPU work between attempts. - **Aware of the 100-callout-per-transaction limit** — every retry counts against it. For real exponential backoff (seconds-to-minutes), go async. ### Async retry — Queueable chain or Platform Event The right pattern for backoff > a few seconds: 1. First attempt fires sync (or from a Queueable). 2. On retryable failure, the Queueable enqueues itself with `attempt + 1` and a stored `nextAttemptAt`. 3. A Scheduled Apex job (or Platform Event subscriber) picks up the work when `nextAttemptAt <= now`. Each retry runs in a **fresh transaction** with fresh governor limits. Backoff schedules of 1s / 5s / 30s / 5min are achievable. ### Circuit breaker Track per-endpoint failure rate in `Cache.Org` (org-partition Platform Cache). Three states: - **CLOSED** — calls flow normally; failures increment a counter. - **OPEN** — counter exceeds threshold (e.g. 10 failures in 60 seconds); subsequent calls short-circuit and throw immediately. No callout consumed. - **HALF-OPEN** — after a cooldown (e.g. 30 seconds), one probe call is allowed. Success returns state to CLOSED; failure returns to OPEN with a longer cooldown. The cache key MUST be per-endpoint (e.g. `ckt:payment-api`), not global. One bad endpoint shouldn't blackhole every integration. ### Idempotency For retryable **writes**, the downstream must dedup. Two patterns: - **Idempotency-Key HTTP header** — generate a UUID, send it on every retry of the same logical operation. Downstream returns the original response on duplicate. Stripe, Square, and most modern payment APIs support this. - **Salesforce-side dedup table** — `Outbound_Callout_Log__c` keyed by `(endpoint + payload_hash + minute_bucket)`. Before retrying, query the log; if already succeeded, skip. ### Dead-letter pattern When retries exhaust (e.g. 5 attempts over 1 hour), write the failed payload to `Failed_Callout__c` with: endpoint, payload, last response, attempt count, last error. A scheduled job or admin UI can reprocess. **Without this, retries that exhaust silently disappear.** ### Governor budget reminder - 100 callouts per transaction (sync or async). Retries count. - 120s cumulative callout time per transaction. - Platform Cache: 10MB org partition free; 1KB per cache entry recommended. ### Testing `MockHttpResponseGenerator` can return a **sequence** of responses by storing call-count state in the mock class. Test patterns: 3 timeouts then success; 5 5xx triggering circuit-open; 4xx never retried. ## Recommended Workflow 1. Classify the integration: idempotent vs non-idempotent; sync vs async tolerable; latency budget. 2. Pick the retry policy: max attempts, backoff schedule, jitter (10-20% randomization to avoid thundering herd). 3. Decide circuit-breaker thresholds per endpoint and store config in Custom Metadata (`Callout_Resilience_Config__mdt`). 4. Implement Idempotency-Key generation (UUID per logical request, persisted on the source record so retries reuse it). 5. Design the dead-letter sObject and the reprocessing path (scheduled Apex + admin reprocess UI). 6. Write `MockHttpResponseGenerator` returning a sequence — assert retry count, circuit-open behavior, and dead-letter writes. 7. Deploy with circuit-breaker thresholds tuned conservatively; monitor `Failed_Callout__c` volume in week 1. ## Review Checklist - [ ] 4xx responses (except 408/429) NEVER trigger retry - [ ] Synchronous retry bounded to <= 3 attempts and total time < 120s - [ ] Async retries use Queueable chain or Platform Event, not sync polling - [ ] Circuit-breaker state stored per endpoint in `Cache.Org`, not as static var - [ ] Idempotency-Key header sent for write operations, persisted on source record - [ ] Dead-letter sObject populated on exhaustion; reprocessing path documented - [ ] No `System.now()` busy-wait loops anywhere - [ ] `Limits.getCallouts()` checked before issuing retry inside a transaction - [ ] Test class with `MockHttpResponseGenerator` covers: success-on-retry, exhaustion-to-dead-letter, circuit-open, 4xx-not-retried ## Salesforce-Specific Gotchas 1. **120-second cumulative callout cap is HARD.** Three retries with 30s timeouts each plus backoff burns the budget fast. Measure actual P95 latency before sizing retries. 2. **Platform Cache keys are case-sensitive.** `ckt:Payment-Api` and `ckt:payment-api` are different breakers — pick a convention and lint it. 3. **`Limits.getCallouts()` is per-transaction, not per-class.** A trigger that fires three handlers each issuing 40 callouts will hit 100 even though no single class did. 4. **`Test.setMock` returns a single mock instance.** To return a sequence, store call-count state inside the mock class and switch on it. ## Output Artifacts | Artifact | Description | |---|---| | Retry policy spec | Attempts, backoff, jitter, eligible status codes | | Circuit-breaker config | Threshold, window, cooldown, partition key — per endpoint | | Idempotency strategy | Header-based or dedup-table; key persistence rule | | `Failed_Callout__c` schema | Dead-letter object + reprocessing job | | Test class | MockHttpResponseGenerator with response-sequence support | ## Related Skills - `apex/apex-named-credentials-patterns` — auth and endpoint config (NOT this skill) - `apex/callout-and-dml-transaction-boundaries` — DML-then-callout ordering (NOT this skill) - `apex/apex-queueable-patterns` — chaining queueables for async retry - `apex/apex-platform-cache-patterns` — Cache.Org partitioning for circuit state - `templates/apex/HttpClient.cls` — the underlying HTTP plumbing
Related Skills
apex-managed-sharing-patterns
Grant row-level access programmatically via __Share records when declarative sharing rules cannot express the policy. NOT for OWD, role hierarchy, or criteria-based sharing rule design.
lwc-imperative-apex
Call Apex methods imperatively from LWC — on button click, lifecycle hooks, or conditional logic. Covers import syntax, cacheable vs non-cacheable, async/await patterns, error handling, loading states, and Promise.all. NOT for wire service (use wire-service-patterns) and NOT for testing Apex mocks (use lwc-testing).
retry-and-backoff-patterns
Implementing resilient integration retry logic in Salesforce: exponential backoff, jitter, idempotency keys, dead-letter queues, and circuit breaker patterns for Apex callouts. Use when designing callout retry behavior, preventing thundering-herd issues, or handling persistent integration failures. NOT for Apex async patterns without callouts (use apex-queueable-patterns). NOT for callout governor limits (use callout-limits-and-async-patterns).
mutual-tls-callouts
Configure mTLS for Apex callouts using Named Credentials with client certificate authentication. NOT for standard TLS or API key auth.
dataweave-for-apex
Use when transforming structured data inside Apex — CSV → JSON, XML → SObject list, JSON → flattened CSV, or schema-mapping a third-party payload to a Salesforce model — and the existing options (`JSON.deserialize`, `Dom.Document`, hand-written loops) are getting unwieldy. Triggers: 'apex transform csv json xml without external library', 'system.dataweave script', 'salesforce native dataweave apex execute', 'transform xml to sobject apex no mulesoft', 'json reshape salesforce apex script'. NOT for MuleSoft Anypoint DataWeave running off-platform (use mulesoft-anypoint-architecture), NOT for Apex JSON serialization basics (use apex-json-serialization), NOT for Bulk API CSV ingest (use bulk-api-2-patterns).
callout-limits-and-async-patterns
Use when designing or troubleshooting Apex callouts that approach governor limits: choosing between synchronous callouts, @future, Queueable, Continuation, or async chaining strategies. NOT for HTTP request construction or Named Credential setup (use named-credentials-setup).
flow-invocable-from-apex
Author @InvocableMethod Apex classes that Flow can call as Actions. Design the input / output variable contract, bulk semantics (one list in, one list out), null handling, and error surfacing. Also covers the inverse direction: calling a flow from Apex via Flow.Interview. NOT for general Apex authoring (use apex-service-selector-domain). NOT for REST-exposed Apex (use apex-rest-resource-patterns).
flow-http-callout-action
Call external HTTP APIs directly from Flow using HTTP Callout actions (no Apex), handling auth, schema, and errors. NOT for complex Apex-based integration logic.
flow-apex-defined-types
Design and use Apex-Defined Types as Flow variables for structured non-sObject data (HTTP callout payloads, External Service responses, complex configuration). Trigger keywords: apex-defined type, flow variable, @AuraEnabled class, flow http callout response. Does NOT cover building HTTP Callout Actions themselves, External Services schema, or raw Apex invocable methods.
scheduled-apex-failure-detection-and-monitoring
Use when nightly batch / scheduled Apex jobs are failing without anyone noticing — covers why uncaught exceptions in `execute()` go to the debug log instead of email, how to query `AsyncApexJob` for `Status`, `NumberOfErrors`, and `ExtendedStatus`, when to implement `Database.RaisesPlatformEvents` so the platform publishes `BatchApexErrorEvent` on uncaught failures, how to subscribe to that event with an Apex trigger and notify operators, and how to layer a custom watcher schedule on top so silent-failure modes (job that never started, scheduled class deleted, queue stuck on `Queued`) still surface. Triggers: 'nightly batch failed at 2am with no notification', 'how do we know if a scheduled apex job is failing', 'BatchApexErrorEvent vs custom retry logic', 'Setup Apex Jobs only shows last 7 days, where else can I look', 'job is stuck in queued status nobody noticed for a week'. NOT for general Apex exception handling patterns (use apex/apex-exception-handling-and-logging), NOT for Batch Apex authoring or chunking strategy (use apex/batch-apex-design), NOT for Setup → Apex Jobs UI walkthrough as an admin task (use admin/batch-job-scheduling-and-monitoring), NOT for retry logic itself (use apex/scheduled-apex-retry-patterns once authored).
platform-events-apex
Use when publishing or subscribing to Salesforce Platform Events from Apex, comparing Platform Events with Change Data Capture, or designing event-triggered error handling and monitoring. Triggers: 'EventBus.publish', 'platform event trigger', 'CDC vs Platform Events', 'replay ID', 'high-volume event'. NOT for Flow-only publish/subscribe automation.
health-cloud-apex-extensions
Use this skill when extending Health Cloud via Apex: implementing HealthCloudGA managed-package interfaces, automating care plan lifecycle hooks, processing referrals using Industries Common Components invocable actions, or enforcing HIPAA-compliant logging governance for clinical Apex code. Trigger keywords: CarePlanProcessorCallback, HealthCloudGA namespace, ReferralRequest, ReferralResponse, care plan invocable actions, clinical Apex extension, Health Cloud Apex API. NOT for standard Apex triggers or generic Apex development unrelated to Health Cloud managed-package extension points.