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:| Endpoint | Documented error |
|---|---|
POST /groups | 422 validation errors |
PUT /groups/{id} | 422 validation errors |
PUT /groups/{id}/members | 422 validation errors |
What the live API likely returns
Based on common conventions for Laravel-style APIs (which Volunteer appears to be):| HTTP status | When likely returned | Body likely shape |
|---|---|---|
401 Unauthorized | Missing, invalid, or expired Bearer token | { "message": "Unauthenticated." } or similar |
403 Forbidden | Token valid but lacks permission for the resource | { "message": "This action is unauthorized." } |
404 Not Found | Resource ID doesn’t exist or isn’t accessible | { "message": "Resource not found." } |
422 Unprocessable Entity | Request body validation failure | { "message": "The given data was invalid.", "errors": { "field": ["error message"] } } |
429 Too Many Requests | Rate limit exceeded (not documented but possible) | Possibly empty or { "message": "Too Many Requests" } |
500 Internal Server Error | Server-side bug or unexpected failure | Possibly empty or { "message": "Server Error" } |
503 Service Unavailable | Maintenance or overload | Possibly empty |
The defensive classification framework
The classifier turns any error response into a category that determines how to handle it:| Classification | Retry? | Action |
|---|---|---|
success | N/A | Use the response. |
auth_failed | No | Token is bad — pause integration for this customer; alert ops. |
forbidden | No | Permission issue — alert ops; don’t keep trying. |
not_found | No | Resource doesn’t exist — return null to caller; let them decide. |
validation_failed | No | Request body has issues — surface field-level errors to user. |
rate_limited | Yes | Honor Retry-After if present; otherwise back off. |
transient | Yes | Exponential backoff with jitter; bounded attempts. |
permanent_client | No | Unknown 4xx — log and surface; don’t retry. |
unknown | No | Log loudly; surface for manual review. |
Per-status handling in depth
401 Unauthorized: don’t retry
A 401 means the token isn’t being accepted. Common causes:
| Cause | Resolution |
|---|---|
| Token expired (if Volunteer enforces expiration) | Customer’s administrator generates a new token |
| Token was revoked in the admin portal | Same |
Token is malformed in the request (whitespace, missing Bearer prefix) | Inspect the actual request — typically a partner-side bug |
| API access was disabled for the customer’s VOMO account | Customer contacts their VOMO concierge |
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:
| Cause | Resolution |
|---|---|
| The token’s organization doesn’t have access to the requested resource | The resource belongs to another organization — partner has the wrong token loaded |
| The customer’s plan doesn’t include the feature | Coordinate with VOMO concierge |
| The endpoint requires a higher permission tier | Same |
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:
| Cause | Resolution |
|---|---|
| The ID was wrong | Caller bug |
| The user was deleted between the integration learning about them and the lookup | Race condition — handle as “not found” |
| The user exists in a different organization | Partner is using the wrong token |
| The endpoint doesn’t exist (rare — partner constructed a wrong URL) | Partner-side URL bug |
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):
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:
5xx Server errors: retry with backoff
Server errors (500, 502, 503, 504) are typically transient. Retry with exponential backoff:
A complete error-handling pattern
Putting it together as a reusable client:not_found, validation_failed, etc.).
Surfacing errors meaningfully
The error pattern produces good developer experience when integrated properly:Don’t swallow errors silently
Surface field-level validation errors
Distinguish user-facing from internal errors
Monitoring error rates
Track these metrics for any production Volunteer integration:| Metric | Healthy baseline | Alert threshold |
|---|---|---|
4xx rate per customer | <0.5% | >2% sustained |
5xx rate per customer | <0.1% | >0.5% sustained |
401 rate (any) | 0 | Any non-zero — credential issue |
429 rate | 0 to occasional | Sustained spike — rate-limit issue |
503 rate | 0 to occasional | Sustained — VOMO platform issue |
| Retry success rate | >95% | <80% — transient errors aren’t actually transient |
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) → honorRetry-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 for429 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.