Skip to main content
The Volunteer API’s error documentation is the sparsest of the three Virtuous APIs. The OpenAPI spec documents error responses on only three of the twenty endpoints (POST /groups, PUT /groups/{id}, PUT /groups/{id}/members — all with a single 422 validation errors). No endpoint formally documents 401, 403, 404, or 500. This gap doesn’t mean those errors don’t occur — they do. It means partner integrations must build defensive error handling without spec guidance about exact response shapes or status codes. This page covers what to expect from the live API, the classification framework that handles each case appropriately, and the patterns that produce reliable production code. ⚠️ Spec gap: The Volunteer OpenAPI spec documents error responses on only three endpoints (all 422 validation errors). The rest of the spec is silent on error responses entirely. The patterns on this page reflect what the live API likely returns based on common REST conventions; confirm against actual responses for production-critical workflows.

What the spec confirms

The three endpoints that do document error responses: Everything else is undocumented. No endpoint exposes the shape of an error response, the fields available on a problem object, or the headers returned with error responses.

What the live API likely returns

Based on common conventions for Laravel-style APIs (which Volunteer appears to be): These shapes are common in Laravel-based APIs but aren’t formally documented for Volunteer. Don’t switch business logic on specific error message strings — they’re not a contract.

The defensive classification framework

The classifier turns any error response into a category that determines how to handle it:
Each classification determines the right response:

Per-status handling in depth

401 Unauthorized: don’t retry

A 401 means the token isn’t being accepted. Common causes: The response:
Critical: retries against an invalid token just produce more 401s and noise. Pause the customer’s work; alert ops. See Authentication: Handling auth failures.

403 Forbidden: also don’t retry

A 403 means the token is valid but doesn’t have permission for the specific endpoint. Causes:
Like 401, don’t retry — the permission isn’t going to change in seconds.

404 Not Found: handle gracefully

A 404 from a single-resource endpoint (e.g., GET /users/12345) means the user with that ID doesn’t exist in the customer’s organization. Causes:
Don’t retry 404 — a non-existent resource will continue to not exist on retry. Return null and let the caller handle the absence.

422 Validation Error: the documented case

The three endpoints that document 422 (POST /groups, PUT /groups/{id}, PUT /groups/{id}/members) return validation errors when the request body doesn’t pass validation. Other endpoints that accept request bodies (POST /projects, POST /users, PUT /projects/{id}) almost certainly return 422 similarly even though it’s undocumented. The body shape (assuming Laravel conventions):
Handling:
Don’t retry 422 — the same payload will fail validation again. Surface the field-level errors to the user (or your integration’s logs) so the bad data can be corrected.

429 Too Many Requests: rate limited

429 isn’t documented in the spec but rate limits likely exist at the platform level. When it happens:
See Rate Limits for the broader rate-limit pattern.

5xx Server errors: retry with backoff

Server errors (500, 502, 503, 504) are typically transient. Retry with exponential backoff:
The exponential backoff (1s, 2s, 4s, 8s, 16s with jitter) is the standard pattern. Bounded retries prevent infinite loops; jitter prevents thundering-herd retries from multiple integrations simultaneously.

A complete error-handling pattern

Putting it together as a reusable client:
Usage:
The client centralizes retry, classification, and alerting — caller code only handles the cases it cares about (not_found, validation_failed, etc.).

Surfacing errors meaningfully

The error pattern produces good developer experience when integrated properly:

Don’t swallow errors silently

Silently returning empty arrays on errors makes problems invisible until they’re catastrophic. Let errors surface so they can be investigated.

Surface field-level validation errors

Field-level errors are the most useful kind — they tell the user (or the calling code) exactly what’s wrong, not just “something failed.”

Distinguish user-facing from internal errors

User-facing messages shouldn’t expose technical details (status codes, stack traces, exact error texts from the API). The mapping function above produces friendly messages while preserving the technical details for logs.

Monitoring error rates

Track these metrics for any production Volunteer integration: Per-customer breakdowns matter. A customer-specific spike in 401s indicates that customer’s token is bad; a platform-wide spike in 503s indicates a VOMO issue not specific to any one customer.

A common-error-cases checklist

For a new Volunteer integration, walk through these cases explicitly:
  • Token is invalid (401) → pause customer; alert
  • Token lacks permission (403) → alert; don’t retry
  • Resource doesn’t exist (404) → return null; caller decides
  • Request body invalid (422) → surface field-level errors
  • Rate limited (429) → honor Retry-After; back off
  • Server error (5xx) → retry with exponential backoff
  • Network error → retry as transient
  • Unexpected 4xx → log and surface; don’t retry
  • All errors logged with sufficient context (customer ID, endpoint, status, body)
  • Metrics emitted for each error class
  • Field-level validation errors surfaced to UI / caller
  • User-facing messages distinct from internal logs

Where to go next

The retry pattern for 429 responses in detail.The broader recovery patterns — circuit breakers, dead-letter queues, classification.The auth-failure handling section in depth.Error patterns specific to multi-page reads.
Last modified on May 22, 2026