Skip to main content

Error Response Structure

All errors return a consistent JSON object:
FieldTypeDescription
errorstringMachine-readable error code for programmatic handling
messagestringHuman-readable description
detailsobject|nullField-level validation messages or additional context
Example Error
{
  "error": "validation_error",
  "message": "The given data was invalid.",
  "details": {
    "email": ["The email field is required."],
    "steps": ["The steps field must be an array."]
  }
}

Error Types

Error CodeHTTP StatusRetryableDescription
validation_error422NoField validation failed. Fix the request body.
authentication_error401NoInvalid or missing API key.
not_found404NoResource does not exist or was deleted.
rate_limit_exceeded429YesToo many requests. Wait for Retry-After.
conflict409NoResource state conflict.
server_error500, 503YesInternal error. Safe to retry with backoff.

Retry Strategy

Never retry 4xx errors (except 429). Client errors like 400, 401, 403, 404, and 422 will not resolve on retry. Fix the request first.
For retryable errors (429, 5xx), use exponential backoff with jitter:
AttemptDelayStrategy
1st1 secondBase delay
2nd2 secondsDouble previous
3rd4 secondsDouble previous
4th8 secondsMax backoff
Add random jitter (0-500ms) to each delay to prevent thundering herd when multiple clients retry simultaneously.

Implementation Example

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

$client = new Client([
    'base_uri' => 'https://verilock.io/api/v1/',
    'headers'  => [
        'Authorization' => 'Bearer ' . env('VERILOCK_API_KEY'),
        'Accept'    => 'application/json',
    ],
]);

$maxRetries = 3;

for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
    try {
        $response = $client->post('sessions', [
            'json' => $payload,
        ]);
        $data = json_decode($response->getBody(), true);
        break; // Success

    } catch (RequestException $e) {
        $status = $e->getResponse()?->getStatusCode();
        $body   = json_decode($e->getResponse()?->getBody(), true);

        if ($status === 422) {
            Log::error('Validation failed', $body['details'] ?? []);
            break; // Do not retry
        }
        if ($status === 429) {
            $wait = $e->getResponse()->getHeaderLine('Retry-After') ?: 5;
            sleep((int) $wait);
            continue;
        }
        if ($status >= 500) {
            sleep(pow(2, $attempt) + rand(0, 500) / 1000);
            continue;
        }
        Log::error($body['error'] ?? 'unknown', $body);
        break;
    }
}