YeboID Error Handling
Consistent error responses across all endpoints.
Error Response Format
All errors follow this structure:
json
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message",
"details": { }
}
}| Field | Type | Description |
|---|---|---|
| code | string | Machine-readable error code (UPPER_SNAKE_CASE) |
| message | string | Human-readable description |
| details | object | Additional context (optional) |
Error Categories
4xx Client Errors
Errors caused by the client request.
5xx Server Errors
Errors caused by server issues.
Complete Error Code Reference
Authentication Errors (AUTH_*)
| Code | HTTP | Message | When |
|---|---|---|---|
| AUTH_INVALID_PHONE | 400 | Invalid phone number format | Phone doesn't match E.164 |
| AUTH_INVALID_PIN | 400 | PIN must be 4-6 digits | PIN format wrong |
| AUTH_INVALID_OTP | 400 | Invalid verification code | Wrong OTP entered |
| AUTH_OTP_EXPIRED | 400 | Verification code has expired | OTP past expiry |
| AUTH_INVALID_CREDENTIALS | 401 | Invalid phone or PIN | Wrong login credentials |
| AUTH_INVALID_TOKEN | 401 | Invalid access token | Token malformed/tampered |
| AUTH_TOKEN_EXPIRED | 401 | Access token has expired | Token past expiry |
| AUTH_INVALID_REFRESH_TOKEN | 401 | Invalid refresh token | Refresh token invalid |
| AUTH_REFRESH_TOKEN_EXPIRED | 401 | Refresh token has expired | Refresh token past expiry |
| AUTH_TEMP_TOKEN_INVALID | 400 | Invalid or expired verification | Temp token from OTP verify invalid |
| AUTH_ACCOUNT_LOCKED | 403 | Account temporarily locked | Too many failed PIN attempts |
| AUTH_ACCOUNT_NOT_FOUND | 404 | Account not found | Phone not registered |
| AUTH_PHONE_EXISTS | 409 | Phone number already registered | Signup with existing phone |
| AUTH_OTP_RATE_LIMITED | 429 | Too many OTP requests | Exceeded OTP send limit |
| AUTH_SIGNIN_RATE_LIMITED | 429 | Too many sign in attempts | Exceeded signin attempts |
User Errors (USER_*)
| Code | HTTP | Message | When |
|---|---|---|---|
| USER_NOT_FOUND | 404 | User not found | User doesn't exist |
| USER_PROFILE_INVALID | 400 | Invalid profile data | Profile update validation failed |
| USER_DELETE_PIN_REQUIRED | 400 | PIN required to delete account | Missing PIN for deletion |
| USER_DELETE_CONFIRMATION | 400 | Confirmation text required | Missing "DELETE MY ACCOUNT" |
Handle Errors (HANDLE_*)
| Code | HTTP | Message | When |
|---|---|---|---|
| HANDLE_INVALID_FORMAT | 400 | Handle must be 3-30 lowercase letters, numbers, or underscores | Bad format |
| HANDLE_INVALID_EDGES | 400 | Handle cannot start or end with underscore | handle or handle |
| HANDLE_TAKEN | 409 | This handle is already taken | Someone else has it |
| HANDLE_RESERVED | 409 | This handle is reserved | In reserved list |
| HANDLE_COOLDOWN | 429 | You can change your handle again on | 30-day cooldown |
| HANDLE_CHECK_RATE_LIMITED | 429 | Too many handle checks | Exceeded check limit |
Session Errors (SESSION_*)
| Code | HTTP | Message | When |
|---|---|---|---|
| SESSION_NOT_FOUND | 404 | Session not found | Invalid session ID |
| SESSION_ALREADY_REVOKED | 400 | Session already revoked | Double revocation |
KYC Errors (KYC_*)
| Code | HTTP | Message | When |
|---|---|---|---|
| KYC_ALREADY_VERIFIED | 400 | Account is already verified | Re-initiating KYC |
| KYC_PENDING | 400 | Verification already in progress | Re-initiating while pending |
| KYC_REJECTED | 403 | Verification was rejected | Access after rejection |
General Errors
| Code | HTTP | Message | When |
|---|---|---|---|
| INVALID_REQUEST | 400 | Invalid request body | Malformed JSON |
| MISSING_FIELD | 400 | Missing required field: | Required field absent |
| INVALID_FIELD | 400 | Invalid value for field: | Field validation failed |
| UNAUTHORIZED | 401 | Authentication required | No token provided |
| FORBIDDEN | 403 | You don't have permission | Insufficient permissions |
| NOT_FOUND | 404 | Resource not found | Generic 404 |
| RATE_LIMITED | 429 | Too many requests | Generic rate limit |
| INTERNAL_ERROR | 500 | Something went wrong | Server error |
| SERVICE_UNAVAILABLE | 503 | Service temporarily unavailable | Maintenance/overload |
Error Details
Some errors include additional context in the details field:
Validation Errors
json
{
"success": false,
"error": {
"code": "INVALID_REQUEST",
"message": "Validation failed",
"details": {
"fields": [
{
"field": "phone",
"message": "Must be in E.164 format"
},
{
"field": "pin",
"message": "Must be 4-6 digits"
}
]
}
}
}Rate Limit Errors
json
{
"success": false,
"error": {
"code": "AUTH_OTP_RATE_LIMITED",
"message": "Too many OTP requests",
"details": {
"limit": 3,
"window": "1 hour",
"retry_after": 2400
}
}
}Account Locked
json
{
"success": false,
"error": {
"code": "AUTH_ACCOUNT_LOCKED",
"message": "Account temporarily locked",
"details": {
"locked_until": "2026-03-18T21:00:00Z",
"unlock_method": "wait or reset PIN via OTP"
}
}
}Handle Cooldown
json
{
"success": false,
"error": {
"code": "HANDLE_COOLDOWN",
"message": "You can change your handle again on April 18, 2026",
"details": {
"next_change_available": "2026-04-18T20:00:00Z",
"days_remaining": 30
}
}
}HTTP Status Code Summary
| Status | Meaning | Common Codes |
|---|---|---|
| 200 | OK | Success |
| 201 | Created | Account created |
| 400 | Bad Request | Validation errors |
| 401 | Unauthorized | Auth required/failed |
| 403 | Forbidden | Locked/no permission |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Already exists |
| 429 | Too Many Requests | Rate limited |
| 500 | Server Error | Internal error |
| 503 | Unavailable | Maintenance |
Client Handling
JavaScript Example
javascript
async function signIn(phone, pin) {
try {
const res = await fetch('/auth/signin', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, pin })
});
const data = await res.json();
if (!data.success) {
switch (data.error.code) {
case 'AUTH_INVALID_CREDENTIALS':
showError('Wrong phone or PIN');
break;
case 'AUTH_ACCOUNT_LOCKED':
const until = data.error.details.locked_until;
showError(`Account locked until ${formatDate(until)}`);
break;
case 'AUTH_ACCOUNT_NOT_FOUND':
showError('No account with this phone. Sign up?');
break;
default:
showError(data.error.message);
}
return null;
}
return data.data;
} catch (e) {
showError('Network error. Try again.');
return null;
}
}React Hook Example
jsx
function useYeboIDError() {
const handleError = (error) => {
const messages = {
'AUTH_INVALID_CREDENTIALS': 'Wrong phone or PIN',
'AUTH_ACCOUNT_LOCKED': 'Account locked. Try again later.',
'HANDLE_TAKEN': 'This handle is already taken',
'RATE_LIMITED': 'Too many attempts. Slow down.',
};
return messages[error.code] || error.message;
};
return { handleError };
}Logging & Monitoring
Error Logging (Server-side)
All errors are logged with:
- Request ID
- User ID (if authenticated)
- IP address
- Error code
- Stack trace (5xx only)
Alerting
| Condition | Alert |
|---|---|
| 5xx rate > 1% | PagerDuty |
| 429 rate > 10% | Slack |
| AUTH_ACCOUNT_LOCKED spike | Slack |
Last updated: March 18, 2026