Documentation Index Fetch the complete documentation index at: https://mintlify.com/mutuiris/voicepact/llms.txt
Use this file to discover all available pages before exploring further.
Overview
SMS verification enables contract parties to confirm their agreements and provide digital signatures using simple text messages. This approach works on any mobile phone, requiring no smartphone or internet access.
Universal Access Works on any phone - no smartphone or data required
Simple Commands Reply with YES/NO commands to confirm or reject contracts
Auto-Generated System sends contract summaries automatically after voice processing
Secure Codes Cryptographic verification codes ensure authenticity
How It Works
Contract Created
After voice processing, the system generates a contract and assigns a unique ID.
SMS Sent
Each party receives an SMS with contract summary and confirmation instructions.
Party Responds
Users reply with “YES-” to confirm or “NO-” to decline.
Signature Recorded
The system records the confirmation as a digital signature with timestamp.
Contract confirmation messages follow a standard format:
VoicePact Contract:
ID: AG-2024-001234
Product: Maize (100 bags)
Value: KES 350,000
Reply YES-AG-2024-001234 to confirm
Reply NO-AG-2024-001234 to decline
Message Components
Contract ID : Unique identifier for tracking
Product Description : What’s being exchanged
Value : Total contract amount
Reply Instructions : Clear confirmation commands
Sending Contract SMS
Send contract notification to parties:
import httpx
response = httpx.post(
"https://api.voicepact.com/api/v1/sms/send/contract" ,
json = {
"contract_id" : "AG-2024-001234" ,
"recipients" : [
"+254712345678" ,
"+254723456789"
],
"terms" : {
"product" : "Maize" ,
"quantity" : 100 ,
"unit" : "bags" ,
"total_amount" : 350000 ,
"currency" : "KES"
}
}
)
result = response.json()
print ( f "SMS sent to { len (result[ 'recipients' ]) } recipients" )
print ( f "Message: { result[ 'message' ] } " )
{
"contract_id" : "AG-2024-001234" ,
"recipients" : [ "+254712345678" , "+254723456789" ],
"message" : "VoicePact Contract: \n ID: AG-2024-001234 \n Product: Maize (100 bags) \n Value: KES 350,000 \n\n Reply YES-AG-2024-001234 to confirm \n Reply NO-AG-2024-001234 to decline" ,
"sms_result" : {
"status" : "success" ,
"data" : {
"SMSMessageData" : {
"Recipients" : [
{
"statusCode" : 101 ,
"number" : "+254712345678" ,
"status" : "Success" ,
"messageId" : "ATXid_abc123"
}
]
}
}
}
}
Sending Simple SMS
For general notifications or custom messages:
response = httpx.post(
"https://api.voicepact.com/api/v1/sms/send" ,
json = {
"phoneNumber" : "+254712345678" ,
"message" : "Your contract has been confirmed by all parties."
}
)
result = response.json()
if result[ "status" ] == "success" :
print ( "Message sent successfully" )
Bulk SMS
Send the same message to multiple recipients:
response = httpx.post(
"https://api.voicepact.com/api/v1/sms/send/bulk" ,
json = {
"recipients" : [
"+254712345678" ,
"+254723456789" ,
"+254734567890"
],
"message" : "Contract delivery scheduled for tomorrow."
}
)
result = response.json()
print ( f "Status: { result[ 'status' ] } " )
Webhook Handler
VoicePact automatically processes incoming SMS replies:
# Incoming webhook at /api/v1/sms/webhook
{
"from" : "+254712345678" ,
"to" : "40404" ,
"text" : "YES-AG-2024-001234" ,
"date" : "2024-03-06 10:30:00" ,
"id" : "ATXid_xyz789"
}
Processing Logic
The webhook handler at sms.py:294 :
Receives incoming SMS
Parses the message text
Extracts action (YES/NO) and contract ID
Records the signature
Updates contract status
Notifies other parties
# Example webhook processing
if message.startswith( "YES-" ):
contract_id = message.split( "-" , 1 )[ 1 ]
# Record confirmation signature
await record_signature(
contract_id = contract_id,
phone_number = sender,
method = "sms_confirmation" ,
status = "signed"
)
elif message.startswith( "NO-" ):
contract_id = message.split( "-" , 1 )[ 1 ]
# Record rejection
await record_signature(
contract_id = contract_id,
phone_number = sender,
method = "sms_confirmation" ,
status = "rejected"
)
SMS Service Status
Check SMS service availability:
response = httpx.get( "https://api.voicepact.com/api/v1/sms/status" )
status = response.json()
print ( f "Service: { status[ 'service' ] } " )
print ( f "Available: { status[ 'service_available' ] } " )
print ( f "Message: { status[ 'message' ] } " )
{
"service" : "SMS" ,
"username" : "voicepact" ,
"api_key_set" : true ,
"service_available" : true ,
"message" : "SMS service ready" ,
"timestamp" : "2024-03-06T10:30:00Z"
}
Message Templates
VoicePact provides built-in message templates:
Contract Summary
from app.services.africastalking_client import get_fixed_at_client
client = get_fixed_at_client()
message = client.generate_contract_sms(
contract_id = "AG-2024-001234" ,
terms = {
"product" : "Maize" ,
"quantity" : 100 ,
"unit" : "bags" ,
"total_amount" : 350000 ,
"currency" : "KES" ,
"delivery_deadline" : "2024-03-15"
}
)
print (message)
Output:
VoicePact Contract Summary:
ID: AG-2024-001234
Product: Maize (100 bags)
Total: KES 350,000.00, Due: 2024-03-15
Reply YES-AG-2024-001234 to confirm or NO-AG-2024-001234 to decline
See africastalking_client.py:408 for template implementation.
The system automatically formats phone numbers:
client = await get_africastalking_client()
# All these formats are accepted:
formatted = await client.format_phone_number( "0712345678" ) # Local
formatted = await client.format_phone_number( "712345678" ) # Without prefix
formatted = await client.format_phone_number( "+254712345678" ) # International
# All return: "+254712345678"
See africastalking_client.py:397 for formatting logic.
Delivery Reports
Track SMS delivery status through the webhook:
# Delivery report webhook payload
{
"id" : "ATXid_abc123" ,
"status" : "Success" ,
"phoneNumber" : "+254712345678" ,
"retryCount" : 0 ,
"failureReason" : null
}
Status Values
Success : Message delivered
Sent : Message sent to carrier
Buffered : Queued for delivery
Rejected : Invalid number or blocked
Failed : Delivery failed
Error Handling
try :
response = httpx.post( "/api/v1/sms/send" , json = payload)
response.raise_for_status()
result = response.json()
if result[ "status" ] == "error" :
print ( f "SMS failed: { result[ 'message' ] } " )
except httpx.HTTPStatusError as e:
if e.response.status_code == 503 :
print ( "SMS service unavailable - check configuration" )
elif e.response.status_code == 500 :
print ( f "Send failed: { e.response.json()[ 'detail' ] } " )
Cost Optimization
Keep messages under 160 characters to avoid multiple SMS charges:
Use short contract IDs
Abbreviate when possible
Remove unnecessary words
Use bulk SMS endpoint for multiple recipients:
Single API call
Better throughput
Reduced overhead
Enable delivery reports only when needed:
Track critical confirmations
Monitor failed deliveries
Retry failed messages
Security Considerations
SMS Confirmation Codes
The system generates cryptographic verification codes:
from app.services.crypto_service import get_crypto_service
crypto = get_crypto_service()
code = crypto.generate_sms_confirmation_code(
contract_id = "AG-2024-001234" ,
phone_number = "+254712345678"
)
print ( f "Verification code: { code } " ) # 6-digit numeric code
See crypto_service.py:101 for code generation.
Signature Recording
Each SMS confirmation creates a digital signature record:
Timestamp : When the confirmation was received
Phone Number : Who confirmed
IP Address : Source of webhook (if available)
Signature Hash : Cryptographic proof
Signature Data : Original message content
See the ContractSignature model at contract.py:173 .
Best Practices
Clear Instructions : Always include reply format in messages
Unique IDs : Use contract IDs in confirmation commands
Confirmation Messages : Send acknowledgment after receiving confirmation
Retry Logic : Implement exponential backoff for failed sends
Rate Limiting : Respect carrier rate limits
Local Numbers : Use local sender IDs when available
Testing
Test SMS integration without sending real messages:
response = httpx.post(
"https://api.voicepact.com/api/v1/sms/test" ,
params = { "phone_number" : "+254712345678" }
)
result = response.json()
print ( f "Test status: { result[ 'status' ] } " )
print ( f "Message: { result.get( 'message' ) } " )
See test endpoint at sms.py:142 .
Integration with Africa’s Talking
VoicePact uses Africa’s Talking SMS API:
Next Steps
Digital Signatures Understand cryptographic signatures
USSD Integration Alternative confirmation via USSD
Voice Contracts Create contracts from voice calls
Mobile Money Process payments