All error responses from the POS Kasir API follow a consistent structure:
{
"message" : "Human-readable error message" ,
"error" : {
// Optional: Additional error details
},
"data" : null
}
Error Response Fields
Field Type Description messagestring A human-readable error message describing what went wrong errorobject Optional object containing additional error details or context datanull Always null for error responses
HTTP Status Codes
The API uses standard HTTP status codes to indicate the success or failure of requests:
2xx Success
Code Status Description 200 OK Request completed successfully 201 Created Resource created successfully
4xx Client Errors
Code Status Description Common Causes 400 Bad Request Invalid request parameters or body Validation errors, malformed JSON, missing required fields 401 Unauthorized Authentication required or failed Missing/invalid token, expired session 403 Forbidden Insufficient permissions Accessing resource without proper role 404 Not Found Resource does not exist Invalid ID, deleted resource 409 Conflict Resource conflict Duplicate entries, invalid state transition
5xx Server Errors
Code Status Description 500 Internal Server Error Unexpected server error
5xx errors indicate server-side issues. If you encounter persistent 500 errors, contact support.
Common Error Scenarios
Authentication Errors
Status : 401 Unauthorized
{
"message" : "Authorization header required" ,
"error" : {},
"data" : null
}
Solution : Include the Authorization: Bearer <token> header in your request.
Invalid or Expired Token
Status : 401 Unauthorized
{
"message" : "Invalid or expired token" ,
"error" : {},
"data" : null
}
Solution : Refresh your access token using the /auth/refresh endpoint or re-authenticate.
Invalid Credentials
Status : 401 Unauthorized
{
"message" : "Invalid username or password" ,
"error" : {},
"data" : null
}
Solution : Verify your email and password are correct.
Insufficient Permissions
Status : 403 Forbidden
{
"message" : "Forbidden - higher role assignment attempt" ,
"error" : {},
"data" : null
}
Solution : Ensure your user role has permission to perform this action.
Validation Errors
Invalid Request Body
Status : 400 Bad Request
{
"message" : "Invalid request body or validation failed" ,
"error" : {
"field" : "email" ,
"reason" : "Invalid email format"
},
"data" : null
}
Common validation issues :
Missing required fields
Invalid data types
String length constraints
Invalid email/UUID format
Numeric range violations
Invalid Query Parameters
Status : 400 Bad Request
{
"message" : "Invalid query parameters" ,
"error" : {},
"data" : null
}
Solution : Check parameter names, types, and values match the API specification.
Resource Errors
Resource Not Found
Status : 404 Not Found
{
"message" : "Product not found" ,
"error" : {},
"data" : null
}
Common causes :
Invalid UUID/ID format
Resource has been deleted
Accessing a resource from another tenant
Resource Already Exists
Status : 409 Conflict
{
"message" : "Category with this name already exists" ,
"error" : {},
"data" : null
}
Solution : Use a different name or update the existing resource.
Resource In Use
Status : 409 Conflict
{
"message" : "Category cannot be deleted because it is in use" ,
"error" : {},
"data" : null
}
Solution : Remove dependencies before deleting the resource.
Business Logic Errors
Invalid State Transition
Status : 409 Conflict
{
"message" : "Order cannot be cancelled" ,
"error" : {},
"data" : null
}
Example : Attempting to cancel an order that’s already paid.
Payment Processing Errors
Status : 400 Bad Request
{
"message" : "Cash received must be greater than or equal to net total" ,
"error" : {},
"data" : null
}
Solution : Validate payment amounts before submitting.
Stock Availability
Status : 400 Bad Request
{
"message" : "Insufficient stock for product" ,
"error" : {
"product_id" : "uuid-here" ,
"available" : 5 ,
"requested" : 10
},
"data" : null
}
Solution : Reduce order quantity or restock the product.
Server Errors
Internal Server Error
Status : 500 Internal Server Error
{
"message" : "Internal server error" ,
"error" : {},
"data" : null
}
When this occurs :
Unexpected server-side exceptions
Database connection issues
Third-party service failures
If you encounter persistent 500 errors, note the timestamp and affected endpoint, then contact support.
Error Handling Best Practices
1. Check HTTP Status Codes
Always check the HTTP status code before parsing the response:
const response = await fetch ( endpoint , options );
if ( ! response . ok ) {
const error = await response . json ();
console . error ( `API Error ( ${ response . status } ):` , error . message );
throw new Error ( error . message );
}
const data = await response . json ();
2. Implement Retry Logic
Retry failed requests with exponential backoff for transient errors:
async function fetchWithRetry ( url , options , maxRetries = 3 ) {
for ( let i = 0 ; i < maxRetries ; i ++ ) {
try {
const response = await fetch ( url , options );
if ( response . ok ) {
return await response . json ();
}
// Don't retry client errors (4xx)
if ( response . status >= 400 && response . status < 500 ) {
const error = await response . json ();
throw new Error ( error . message );
}
// Retry server errors (5xx)
if ( i === maxRetries - 1 ) {
throw new Error ( `Failed after ${ maxRetries } retries` );
}
// Exponential backoff
await new Promise ( resolve =>
setTimeout ( resolve , Math . pow ( 2 , i ) * 1000 )
);
} catch ( error ) {
if ( i === maxRetries - 1 ) throw error ;
}
}
}
3. Handle Token Expiration
Automatically refresh expired tokens:
async function authenticatedRequest ( endpoint , options ) {
let response = await fetch ( endpoint , {
... options ,
headers: {
... options . headers ,
'Authorization' : `Bearer ${ accessToken } `
}
});
// Token expired, refresh and retry
if ( response . status === 401 ) {
await refreshAccessToken ();
response = await fetch ( endpoint , {
... options ,
headers: {
... options . headers ,
'Authorization' : `Bearer ${ accessToken } `
}
});
}
return response ;
}
4. Validate Before Sending
Validate data client-side before making API requests:
function validateProduct ( product ) {
const errors = [];
if ( ! product . name || product . name . length < 3 ) {
errors . push ( 'Name must be at least 3 characters' );
}
if ( product . price <= 0 ) {
errors . push ( 'Price must be greater than 0' );
}
if ( product . stock < 0 ) {
errors . push ( 'Stock cannot be negative' );
}
if ( errors . length > 0 ) {
throw new Error ( errors . join ( ', ' ));
}
}
5. Log Errors Properly
Implement comprehensive error logging:
function logError ( error , context ) {
console . error ( 'API Error:' , {
timestamp: new Date (). toISOString (),
message: error . message ,
status: error . status ,
endpoint: context . endpoint ,
method: context . method ,
userId: context . userId ,
stackTrace: error . stack
});
}
Error Handling Examples
JavaScript/TypeScript
class APIError extends Error {
constructor (
message : string ,
public status : number ,
public details ?: any
) {
super ( message );
this . name = 'APIError' ;
}
}
async function handleAPIRequest < T >(
endpoint : string ,
options : RequestInit
) : Promise < T > {
try {
const response = await fetch ( endpoint , options );
const data = await response . json ();
if ( ! response . ok ) {
throw new APIError (
data . message || 'Request failed' ,
response . status ,
data . error
);
}
return data . data as T ;
} catch ( error ) {
if ( error instanceof APIError ) {
// Handle known API errors
switch ( error . status ) {
case 401 :
// Redirect to login
window . location . href = '/login' ;
break ;
case 403 :
// Show permission error
showNotification ( 'You do not have permission' , 'error' );
break ;
case 404 :
// Show not found error
showNotification ( 'Resource not found' , 'error' );
break ;
case 409 :
// Show conflict error
showNotification ( error . message , 'warning' );
break ;
case 500 :
// Show server error
showNotification ( 'Server error, please try again' , 'error' );
break ;
default :
showNotification ( error . message , 'error' );
}
throw error ;
}
// Handle network errors
showNotification ( 'Network error, check your connection' , 'error' );
throw error ;
}
}
// Usage
try {
const product = await handleAPIRequest (
'https://api-pos.agprastyo.me/api/v1/products/123' ,
{ method: 'GET' }
);
console . log ( 'Product:' , product );
} catch ( error ) {
console . error ( 'Failed to fetch product:' , error );
}
Python
import requests
from typing import Dict, Any
class APIError ( Exception ):
def __init__ ( self , message : str , status_code : int , details : Dict = None ):
self .message = message
self .status_code = status_code
self .details = details or {}
super (). __init__ ( self .message)
def handle_api_request (
method : str ,
endpoint : str ,
** kwargs
) -> Dict[ str , Any]:
try :
response = requests.request(method, endpoint, ** kwargs)
data = response.json()
if not response.ok:
raise APIError(
message = data.get( 'message' , 'Request failed' ),
status_code = response.status_code,
details = data.get( 'error' , {})
)
return data.get( 'data' )
except APIError as e:
# Handle known API errors
if e.status_code == 401 :
# Refresh token or redirect to login
raise
elif e.status_code == 403 :
print ( f "Permission denied: { e.message } " )
raise
elif e.status_code == 404 :
print ( f "Not found: { e.message } " )
raise
elif e.status_code == 409 :
print ( f "Conflict: { e.message } " )
raise
elif e.status_code >= 500 :
print ( f "Server error: { e.message } " )
raise
else :
print ( f "API error: { e.message } " )
raise
except requests.exceptions.RequestException as e:
print ( f "Network error: { e } " )
raise
# Usage
try :
product = handle_api_request(
'GET' ,
'https://api-pos.agprastyo.me/api/v1/products/123' ,
headers = { 'Authorization' : f 'Bearer { token } ' }
)
print ( 'Product:' , product)
except APIError as e:
print ( f 'Failed to fetch product: { e.message } (Status: { e.status_code } )' )
Debugging Tips
Log all API requests and responses during development: const originalFetch = window . fetch ;
window . fetch = async ( ... args ) => {
console . log ( 'Request:' , args );
const response = await originalFetch ( ... args );
const clone = response . clone ();
const body = await clone . json ();
console . log ( 'Response:' , body );
return response ;
};
Use Swagger UI for Testing
Check Request/Response Headers
Use a JSON validator to check request bodies: echo '{"name": "test"}' | jq .
Support
If you encounter errors not covered in this documentation:
Check the Swagger documentation for endpoint-specific details
Review the GitHub issues for known issues
Open a new issue with:
Error message and status code
Request details (endpoint, method, body)
Expected vs actual behavior
Steps to reproduce
Report an Issue Open a GitHub issue for bugs or API problems
Next Steps
API Overview Return to API introduction
Authentication Learn about authentication