All VoicePact API errors follow a consistent JSON format:
{
"detail" : "Error message describing what went wrong"
}
In debug mode, additional error information may be included:
{
"detail" : "Internal server error" ,
"error" : "Detailed exception message"
}
Detailed error messages are only shown when DEBUG=true. In production, generic messages are returned for security.
HTTP Status Codes
The VoicePact API uses standard HTTP status codes:
Success Codes
Code Meaning Usage 200OK Successful GET, PUT requests 201Created Successful POST creating a resource
Client Error Codes
Code Meaning Common Causes 400Bad Request Invalid input data, malformed JSON 404Not Found Contract, payment, or recording not found 422Unprocessable Entity Validation error in request body 503Service Unavailable External service (SMS, payment) unavailable
Server Error Codes
Code Meaning Description 500Internal Server Error Unexpected server error
Common Error Scenarios
1. Contract Not Found
Status Code: 404
{
"detail" : "Contract not found"
}
Causes:
Invalid contract ID
Contract was deleted
Contract hasn’t been created yet
Example:
curl http://localhost:8000/api/v1/contracts/INVALID-ID
Solution:
# List available contracts
curl http://localhost:8000/api/v1/contracts/
2. Validation Error
Status Code: 422
{
"detail" : [
{
"loc" : [ "body" , "total_amount" ],
"msg" : "ensure this value is greater than 0" ,
"type" : "value_error.number.not_gt"
}
]
}
Causes:
Missing required fields
Invalid data types
Values outside allowed ranges
Example:
curl -X POST http://localhost:8000/api/v1/contracts/create/manual \
-H "Content-Type: application/json" \
-d '{
"product": "Maize",
"total_amount": -100
}'
Solution:
curl -X POST http://localhost:8000/api/v1/contracts/create/manual \
-H "Content-Type: application/json" \
-d '{
"product": "Maize",
"quantity": "100",
"unit": "bags",
"total_amount": 50000,
"currency": "KES",
"parties": [
{"phone": "+254712345678", "role": "seller", "name": "John"},
{"phone": "+254723456789", "role": "buyer", "name": "Jane"}
]
}'
3. Voice Processing Failed
Status Code: 400
{
"detail" : "Voice processing failed: Unsupported audio format"
}
Causes:
Unsupported audio file format
Audio file too large (>50MB)
Audio duration too long (>1200 seconds)
Corrupted audio file
Audio URL not accessible
Supported Formats:
Example Error:
Solution:
curl -X POST http://localhost:8000/api/v1/voice/upload \
-F "[email protected] " \
-F "contract_type=agricultural_supply"
4. Payment Service Unavailable
Status Code: 503
{
"detail" : "SMS service unavailable. Check AT_API_KEY."
}
Causes:
Africa’s Talking API key not configured
Africa’s Talking service down
Network connectivity issues
Invalid API credentials
Configuration Check:
# Check SMS service status
curl http://localhost:8000/api/v1/sms/status
Response:
{
"service" : "SMS" ,
"username" : "sandbox" ,
"api_key_set" : true ,
"service_available" : true ,
"message" : "SMS service ready"
}
Solution:
# Set environment variable
export AT_API_KEY = your-api-key-here
# Restart application
systemctl restart voicepact
5. Contract Creation Failed
Status Code: 500
{
"detail" : "Contract creation failed: Database error"
}
Causes:
Database connection lost
Invalid party phone numbers
Duplicate contract ID
Terms validation failed
Example Error:
curl -X POST http://localhost:8000/api/v1/contracts/create \
-H "Content-Type: application/json" \
-d '{
"transcript": "Test contract",
"parties": [{"phone": "invalid"}],
"terms": {}
}'
Solution:
curl -X POST http://localhost:8000/api/v1/contracts/create \
-H "Content-Type: application/json" \
-d '{
"transcript": "Agreement to supply 100 bags of maize",
"parties": [
{"phone": "+254712345678", "role": "seller"},
{"phone": "+254723456789", "role": "buyer"}
],
"contract_type": "agricultural_supply",
"terms": {
"product": "Maize",
"quantity": "100",
"unit": "bags",
"total_amount": 50000,
"currency": "KES"
}
}'
6. Mobile Checkout Failed
Status Code: 500
{
"detail" : "Mobile checkout failed: Insufficient balance"
}
Causes:
Payer has insufficient funds
Invalid phone number format
Payment amount exceeds limits
Africa’s Talking payment service error
Payment Amount Limits:
Minimum: KES 100 (1.00 KES)
Maximum: KES 1,000,000 (10,000.00 KES)
Example:
curl -X POST http://localhost:8000/api/v1/payments/checkout \
-H "Content-Type: application/json" \
-d '{
"contract_id": "AG-2024-001",
"amount": 50000,
"currency": "KES",
"phone_number": "+254712345678",
"payment_type": "escrow"
}'
7. Signature Not Found
Status Code: 404
{
"detail" : "Signature record not found"
}
Causes:
Phone number not a contract party
Contract doesn’t exist
Signature already confirmed
Check Contract Parties:
curl http://localhost:8000/api/v1/contracts/AG-2024-001
Confirm with Correct Phone:
curl -X POST http://localhost:8000/api/v1/contracts/AG-2024-001/confirm \
-H "Content-Type: application/json" \
-d '{"phone_number": "+254712345678"}'
Error Handling Best Practices
1. Always Check Status Codes
import requests
response = requests.post(
"http://localhost:8000/api/v1/contracts/create" ,
json = contract_data
)
if response.status_code == 200 :
contract = response.json()
print ( f "Contract created: { contract[ 'contract_id' ] } " )
elif response.status_code == 404 :
print ( "Resource not found" )
elif response.status_code == 422 :
errors = response.json()[ 'detail' ]
print ( f "Validation errors: { errors } " )
else :
print ( f "Error: { response.json()[ 'detail' ] } " )
2. Implement Retry Logic
import time
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
session = requests.Session()
retry = Retry(
total = 3 ,
backoff_factor = 1 ,
status_forcelist = [ 500 , 502 , 503 , 504 ]
)
adapter = HTTPAdapter( max_retries = retry)
session.mount( 'http://' , adapter)
session.mount( 'https://' , adapter)
response = session.post(url, json = data)
def validate_phone_number ( phone : str ) -> bool :
"""Validate Kenyan phone number format"""
import re
pattern = r ' ^ \+ 254 \d {9} $ '
return bool (re.match(pattern, phone))
def validate_contract_data ( data : dict ) -> tuple :
"""Validate contract data before API call"""
errors = []
if data.get( 'total_amount' , 0 ) <= 0 :
errors.append( "Total amount must be greater than 0" )
if not data.get( 'parties' ) or len (data[ 'parties' ]) < 2 :
errors.append( "At least 2 parties required" )
for party in data.get( 'parties' , []):
if not validate_phone_number(party.get( 'phone' , '' )):
errors.append( f "Invalid phone: { party.get( 'phone' ) } " )
return len (errors) == 0 , errors
4. Log Errors for Debugging
import logging
logger = logging.getLogger( __name__ )
try :
response = requests.post(url, json = data)
response.raise_for_status()
except requests.HTTPError as e:
logger.error( f "API Error: { e.response.status_code } " )
logger.error( f "Response: { e.response.json() } " )
raise
except requests.RequestException as e:
logger.error( f "Request failed: { e } " )
raise
Global Exception Handler
The VoicePact API includes a global exception handler (main.py:73-88):
@app.exception_handler ( Exception )
async def global_exception_handler ( request : Request, exc : Exception ):
"""Global exception handler"""
logger.error( f "Unhandled exception in { request.method } { request.url } : { exc } " )
if settings.debug:
import traceback
logger.error( f "Traceback: { traceback.format_exc() } " )
return JSONResponse(
status_code = 500 ,
content = {
"detail" : "Internal server error" ,
"error" : str (exc) if settings.debug else "An error occurred"
}
)
Enable debug mode to see detailed error messages during development: DEBUG=true
Debugging Tips
Set environment variable for detailed errors: export DEBUG = true
export LOG_LEVEL = DEBUG
Check logs:
Test SMS service: curl http://localhost:8000/api/v1/sms/status
Test payment service: curl http://localhost:8000/api/v1/payments/wallet/balance
Test database: curl http://localhost:8000/health
View application logs: # If using systemd
journalctl -u voicepact -f
# If using Docker
docker logs -f voicepact
# If running directly
tail -f logs/voicepact.log
Health Check Endpoint
Monitor system health:
curl http://localhost:8000/health
Healthy Response:
{
"status" : "healthy" ,
"services" : {
"database" : "connected" ,
"redis" : "connected"
},
"timestamp" : 1710505800.123
}
Unhealthy Response:
{
"status" : "unhealthy" ,
"services" : {
"database" : {
"error" : "Connection timeout"
},
"redis" : "connected"
},
"timestamp" : 1710505800.123
}
Support and Resources
API Introduction Back to API overview and quick start
GitHub Issues Report bugs and request features
Need Help?
If you encounter an error not covered here:
Check the application logs
Verify your configuration settings
Test with the interactive API docs at /docs
Review the source code at the referenced file locations
Open an issue on GitHub with error details