POST /api/Raise/give (which charges payment methods and can’t be retried naively).
Classifying errors
The first and most important decision: is this error transient or permanent? The right response is wildly different.
The classifier is the foundation of all retry logic. Get it right, and everything else falls into place. Get it wrong, and you either hammer the API with futile retries or fail to retry transient errors that would succeed.
A reference classifier
JavaScript
Retry patterns for transient errors
Transient errors are the bread-and-butter case for retry logic. The patterns:Exponential backoff
The basic pattern: each retry waits longer than the previous, eventually giving up.JavaScript
- Exponential growth means most retries happen quickly, but persistent failures don’t loop tightly.
- Jitter prevents synchronized retries across many integrations from hammering the API simultaneously.
- Bounded attempts ensure the retry loop terminates rather than retrying forever.
Honor Retry-After when present
For rate-limit responses, the server may include a Retry-After header indicating how long to wait. Honor it instead of computing a backoff:
JavaScript
The Raise OpenAPI spec doesn’t explicitly document the
Retry-After header on rate-limit responses. The pattern assumes it’s present (following common HTTP convention) and falls back to exponential backoff if not. See Rate Limits for what’s known.Don’t retry forever
A5xx that persists for hours isn’t transient — it’s a sustained issue worth surfacing for human review. Bound retries at a reasonable number (typically 5 attempts) and move to a different handling strategy after that. See Dead-letter queues below.
Errors that should never be retried
Three classes of errors that retry only makes worse:401 Unauthorized: the credential is bad
A 401 means the token is invalid, expired, or revoked. Retrying produces the same 401 indefinitely. The fix isn’t a retry — it’s a credential refresh.
JavaScript
400 validation failures: the request is bad
A 400 typically means the request body has a validation issue — a missing required field, an invalid value, a malformed structure. Retrying with the same body produces the same 400. The fix is to correct the request:
JavaScript
400 from POST /api/Raise/give may also indicate a payment failure (card declined, gateway rejection). These also should not be retried with the same payment method — surface them to the donor for a different card.
404 Not Found: the resource doesn’t exist
A 404 from GET /api/Donor/12345 means donor 12345 doesn’t exist (or was deleted). No retry will make it appear. The fix is to handle the absence gracefully:
JavaScript
404 is expected (the integration was checking for existence). Sometimes it indicates a deeper issue (the donor was deleted between the integration learning about them and the lookup). Don’t retry; the right path depends on context.
Special case: POST /api/Raise/give retries
Donation submissions deserve special attention because they charge payment methods. A naive retry on a network error can produce double charges if the original request succeeded but the response didn’t reach the integration.
The double-charge risk
The integration sees one successful response. The donor sees two charges. The customer has to issue a refund for one of them. Avoid this.The defensive pattern
ForPOST /api/Raise/give specifically, never retry network errors blindly. Instead:
JavaScript
Reconciling uncertain donations
When a network error leaves the outcome uncertain, the integration shouldn’t auto-retry. Instead, surface the uncertain donation for reconciliation:JavaScript
Circuit breakers
For workloads that touch many records, a sustained failure can produce a cascade — many in-flight requests all hitting the same issue, all retrying, all eventually failing. A circuit breaker stops the cascade by short-circuiting requests after a threshold of failures.A basic circuit breaker
JavaScript
JavaScript
When to use circuit breakers
For partner integrations operating at scale (hundreds of customers, thousands of requests per minute), circuit breakers prevent localized issues from cascading into widespread degradation.
Dead-letter queues
When all retries fail, the operation can’t continue. Two options: drop it silently (bad — lost work) or move it to a dead-letter queue for human review (good).A dead-letter pattern
1
Operation fails permanently or exhausts retries
A
400 validation error, a sustained 5xx, or a network error that doesn’t recover.2
Move the operation to the dead-letter queue
Capture the full operation payload, the last error, and the attempt history.
3
Continue processing other operations
One bad operation doesn’t block the queue.
4
Surface the dead-letter entry for review
Alert or daily digest to ops; expose in a UI for support staff.
5
Investigate and resolve
Either fix the underlying issue and replay, or mark the operation as permanently lost.
Replaying from the dead-letter queue
For operations that failed due to a transient issue that’s now resolved, replay them:JavaScript
Surfacing errors to humans
Not every error needs to wake someone up. A reasonable severity model:
The right thresholds depend on the integration’s SLA. For a major-donor-focused integration, even a single failed
POST /api/Raise/give may warrant a same-day investigation. For a low-priority analytics sync, the same failure might be a warning aggregated into a daily digest.
Useful alert content
A useful error alert includes:- The customer affected
- The operation that failed
- The error classification (transient, permanent, uncertain)
- The number of attempts made
- The last error message
- A link to the dead-letter entry (or wherever the operation can be inspected and replayed)
- Suggested next steps based on the error type
JavaScript
Idempotency: the underlying defense
Most error-recovery patterns rely on idempotency — the property that repeating an operation produces the same outcome as running it once. Build idempotency into operations from the start.Webhook handlers
See Idempotency and Safe Reprocessing for the full pattern. Summary: every event has a unique key (typicallycontextId + eventType + modifiedDate), and the dedup store records processed events. Retried deliveries skip re-processing.
Downstream writes
For partner integrations writing to external systems, use the external system’s idempotency mechanisms:- Database upserts keyed by Raise resource IDs.
- Idempotency keys on third-party API calls (Stripe, Slack, many modern APIs support them).
- Conditional logic that checks for existing records before creating new ones.
POST /api/Raise/give — the special case
Donation submissions are the most challenging case because the operation is genuinely not idempotent at the API level (no documented idempotency-key header). The client-side reconciliation pattern (see The defensive pattern above) is the workaround. When an idempotency-key header becomes available, switch to it.
A complete error-handling pipeline
Putting the patterns together:JavaScript
A recovery checklist
When designing a Raise integration, walk through these questions:- Every API call goes through a function that classifies and retries appropriately
POST /api/Raise/giveuses the client-side reconciliation pattern, not naive retry- Webhook handlers are idempotent — retries don’t produce duplicate side effects
- Bulk operations use circuit breakers to prevent cascade failures
- Permanently-failed operations go to a dead-letter queue
- Dead-letter entries are surfaced to ops with enough context to investigate
401responses pause the affected customer’s work and alert ops, rather than retrying- Network errors and
5xxresponses retry with exponential backoff + jitter Retry-Afterheaders are honored when present- Rate-limit
429responses are visible in metrics
Where to go next
Sync Architecture Patterns
The architectural patterns these error-recovery patterns plug into.
API Performance Tips
Performance patterns complementary to recovery — fewer requests means fewer chances to fail.
Rate Limits
The
429 patterns referenced throughout this page.Idempotency and Safe Reprocessing
Webhook-specific idempotency that pairs with API error recovery.