Error Handling

The OnboardingHub API uses standard HTTP status codes and returns errors in a consistent JSON format.

Error format

All errors follow this structure:

{
  "error": {
    "type": "error_type",
    "message": "Human-readable description of the error."
  }
}

Validation errors include an additional errors object with field-level details:

{
  "error": {
    "type": "validation_error",
    "message": "Email has already been taken, First name can't be blank",
    "errors": {
      "email": ["has already been taken"],
      "first_name": ["can't be blank"]
    }
  }
}

Error types

Type HTTP Status Description
authentication_required 401 Missing or invalid Authorization header
invalid_token 401 The API token is invalid or has been revoked
token_expired 401 The API token or OAuth access token has expired
forbidden 403 API access is not enabled for this account (plan upgrade required)
insufficient_scope 403 The token does not have the required scope for this action
not_found 404 The requested resource does not exist (or is not in your workspace)
validation_error 422 The request body failed validation
invalid_request 400 Missing required parameters
rate_limited 429 Too many requests -- see Rate Limiting

HTTP status codes

Code Meaning
200 Success
201 Resource created
204 Resource deleted (no content)
400 Bad request (missing parameters)
401 Unauthorized (invalid or missing token)
403 Forbidden (insufficient scope or plan)
404 Not found
422 Unprocessable entity (validation failed)
429 Too many requests (rate limited)

Handling errors in code

Ruby

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

case response.code.to_i
when 200..299
  data = JSON.parse(response.body)
when 401
  raise "Authentication failed -- check your API key"
when 403
  error = JSON.parse(response.body)
  raise "Forbidden: #{error.dig('error', 'message')}"
when 404
  raise "Resource not found"
when 422
  error = JSON.parse(response.body)
  field_errors = error.dig("error", "errors")
  raise "Validation failed: #{field_errors}"
when 429
  retry_after = response["Retry-After"].to_i
  sleep(retry_after)
  retry
end

Python

import requests
import time

response = requests.get(
    "https://onboarding-hub.com/api/v1/contacts",
    headers={"Authorization": "Bearer YOUR_ACCESS_TOKEN"},
)

if response.ok:
    data = response.json()
elif response.status_code == 401:
    raise Exception("Authentication failed -- check your API key")
elif response.status_code == 403:
    error = response.json()
    raise Exception(f"Forbidden: {error['error']['message']}")
elif response.status_code == 422:
    error = response.json()
    field_errors = error["error"].get("errors", {})
    raise Exception(f"Validation failed: {field_errors}")
elif response.status_code == 429:
    retry_after = int(response.headers.get("Retry-After", 60))
    time.sleep(retry_after)
    # Retry the request

JavaScript

const response = await fetch("https://onboarding-hub.com/api/v1/contacts", {
  headers: { Authorization: "Bearer YOUR_ACCESS_TOKEN" },
});

if (response.ok) {
  const data = await response.json();
} else if (response.status === 429) {
  const retryAfter = parseInt(response.headers.get("Retry-After") || "60");
  await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
  // Retry the request
} else {
  const error = await response.json();
  throw new Error(`API error: ${error.error.message}`);
}

Next steps