Nudg3Docs

Error Handling

Understanding and handling Nudg3 API errors

Error Handling

The Nudg3 API uses conventional HTTP response codes to indicate the success or failure of an API request. This guide covers all error types and how to handle them.

Error Response Format

All errors follow a consistent JSON format:

{
  "success": false,
  "error": {
    "code": "error_code",
    "message": "Human-readable error message",
    "details": {
      // Additional context (optional)
    }
  }
}
FieldTypeDescription
successbooleanAlways false for errors
error.codestringMachine-readable error code
error.messagestringHuman-readable description
error.detailsobjectAdditional error context (optional)

HTTP Status Codes

2xx Success

CodeDescription
200Request succeeded
201Resource created
204No content (successful deletion)

4xx Client Errors

CodeDescription
400Bad Request - Invalid parameters
401Unauthorized - Authentication failed
403Forbidden - Insufficient permissions
404Not Found - Resource doesn't exist
422Unprocessable Entity - Validation error
429Too Many Requests - Rate limit exceeded

5xx Server Errors

CodeDescription
500Internal Server Error
502Bad Gateway
503Service Unavailable
504Gateway Timeout

Common Error Codes

Authentication Errors (401)

{
  "success": false,
  "error": {
    "code": "invalid_api_key",
    "message": "The provided API key is invalid or malformed"
  }
}
CodeMessageSolution
invalid_api_keyAPI key is invalid or malformedCheck key format starts with nudg3_
expired_api_keyAPI key has been revokedGenerate a new key
missing_api_keyNo API key providedInclude Authorization header

Permission Errors (403)

{
  "success": false,
  "error": {
    "code": "insufficient_scope",
    "message": "API key lacks required scope: export:data",
    "details": {
      "required_scope": "export:data",
      "key_scopes": ["read:analytics"]
    }
  }
}
CodeMessageSolution
insufficient_scopeKey lacks required scopeCreate key with required scopes
ip_not_allowedRequest from non-allowlisted IPUpdate IP allowlist or use VPN
workspace_access_deniedNo access to this workspaceCheck workspace membership

Validation Errors (400/422)

{
  "success": false,
  "error": {
    "code": "validation_error",
    "message": "Request validation failed",
    "details": {
      "errors": [
        {
          "field": "start_date",
          "message": "Invalid date format. Expected YYYY-MM-DD"
        },
        {
          "field": "brand_id",
          "message": "brand_id is required for this endpoint"
        }
      ]
    }
  }
}
CodeMessageSolution
validation_errorOne or more fields are invalidCheck details.errors for specifics
invalid_uuidInvalid UUID formatUse valid UUID format
invalid_date_rangeend_date before start_dateFix date order
missing_required_fieldRequired field is missingInclude required parameters

Rate Limit Errors (429)

{
  "success": false,
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Try again in 45 seconds.",
    "details": {
      "retry_after": 45,
      "limit_type": "per_minute",
      "limit": 60
    }
  }
}
CodeMessageSolution
rate_limit_exceededToo many requestsWait and retry with backoff
export_limit_exceededExport rate limit hitWait 1 hour or reduce exports

Resource Errors (404)

{
  "success": false,
  "error": {
    "code": "resource_not_found",
    "message": "Brand not found",
    "details": {
      "resource_type": "brand",
      "resource_id": "550e8400-e29b-41d4-a716-446655440000"
    }
  }
}
CodeMessageSolution
resource_not_foundResource doesn't existVerify resource ID is correct
workspace_not_foundWorkspace doesn't existCheck workspace_id parameter

Server Errors (5xx)

{
  "success": false,
  "error": {
    "code": "internal_error",
    "message": "An unexpected error occurred. Please try again later.",
    "details": {
      "request_id": "req_abc123xyz"
    }
  }
}
CodeMessageSolution
internal_errorServer errorRetry with backoff, contact support if persistent
service_unavailableService temporarily downRetry after a few minutes
export_service_errorExport service unavailableRetry export later

Error Handling Best Practices

Implement Retry Logic

For transient errors (429, 5xx), implement exponential backoff:

async function apiCallWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);
 
      if (response.ok) {
        return await response.json();
      }
 
      const error = await response.json();
 
      // Don't retry client errors (except rate limits)
      if (response.status >= 400 && response.status < 500 && response.status !== 429) {
        throw new ApiError(error.error.code, error.error.message);
      }
 
      // Calculate backoff
      const retryAfter = response.headers.get('Retry-After');
      const backoff = retryAfter
        ? parseInt(retryAfter) * 1000
        : Math.min(1000 * Math.pow(2, attempt), 30000);
 
      await new Promise(resolve => setTimeout(resolve, backoff));
 
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
    }
  }
}

Handle Specific Errors

try {
  const data = await api.getDashboard(filters);
} catch (error) {
  switch (error.code) {
    case 'invalid_api_key':
      // Redirect to settings to update API key
      router.push('/settings/api-keys');
      break;
 
    case 'insufficient_scope':
      // Show upgrade prompt
      showUpgradeModal(error.details.required_scope);
      break;
 
    case 'rate_limit_exceeded':
      // Show rate limit message
      toast.warning(`Rate limit reached. Retry in ${error.details.retry_after}s`);
      break;
 
    case 'validation_error':
      // Show field-level errors
      error.details.errors.forEach(e => {
        setFieldError(e.field, e.message);
      });
      break;
 
    default:
      // Generic error handling
      toast.error(error.message);
  }
}

Log Errors for Debugging

Include request IDs in error logs for support tickets:

catch (error) {
  console.error('API Error:', {
    code: error.code,
    message: error.message,
    requestId: error.details?.request_id,
    timestamp: new Date().toISOString()
  });
}

Brute Force Protection

The API implements brute force protection for authentication:

ConditionAction
10 failed auth attemptsIP blocked for 1 hour
Repeated blocksProgressive blocking periods

If blocked, you'll receive:

{
  "success": false,
  "error": {
    "code": "ip_blocked",
    "message": "Too many failed authentication attempts. Try again later.",
    "details": {
      "blocked_until": "2026-01-12T11:30:00Z"
    }
  }
}

Getting Help

If you encounter persistent errors:

  1. Check the error code and message
  2. Review this documentation for solutions
  3. Include the request_id when contacting support
  4. Provide the full error response and request details