POS Webhook Integration - Basic Guide¶
Last Updated: 2025-11-24
Version: 1.0
Audience: POS System Integrators
Overview¶
This guide provides instructions for integrating your POS system with EmiliaVision via webhooks. Our webhook receiver accepts any valid JSON payload and stores it for processing.
Webhook Endpoint¶
URL Format: https://api.emiliavision.com/webhooks/v1/pos/{token}?env={environment}
- Method:
POST - Content-Type:
application/json - Authentication: Token in URL path (provided by EmiliaVision)
- Environment: Optional query parameter to target dev/prod (defaults to prod)
Environment Parameter¶
Use the optional env query parameter to control which environment receives your data:
- Production (default):
https://api.emiliavision.com/webhooks/v1/pos/{token}or?env=prod - Development/Testing:
https://api.emiliavision.com/webhooks/v1/pos/{token}?env=dev
When to use ?env=dev: - Testing integration - Development environments - Sandbox/staging data - Non-production orders
When to use ?env=prod or no parameter: - Live production data - Real customer orders - Production systems
Authentication¶
Each integration receives a unique webhook token (UUID format). This token: - Must be included in the URL path - Is validated on each request - Can be deactivated remotely if needed - Should be kept secure and not shared
Example: 019ab6ed-150a-7be1-920f-ab82305e5d41
Request Format¶
Headers¶
Body¶
Send any valid JSON payload. We store it exactly as received.
Example Payload:
{
"event": "order_created",
"timestamp": "2025-11-24T18:30:00-03:00",
"order_id": "ORD-2025-0124",
"table": "Mesa 8",
"items": [
{
"id": "ITEM-001",
"name": "Focaccia",
"quantity": 1,
"price": 19.00
}
],
"total": 41.00
}
Response Codes¶
| Code | Meaning | Action |
|---|---|---|
200 | Success | Webhook received and stored |
401 | Unauthorized | Token invalid or inactive - contact EmiliaVision |
413 | Payload Too Large | Reduce payload size (max 10MB) |
500 | Server Error | Retry with exponential backoff |
503 | Service Unavailable | Retry with exponential backoff |
Success Response¶
{
"status": "success",
"event_id": "019ab6ed-150a-7be1-920f-ab82305e5d41",
"message": "Webhook received and stored",
"received_at": "2025-11-24T18:30:00.123Z"
}
Error Response¶
Rate Limits¶
- Limit: 100 requests per second per token
- Burst: Up to 200 requests in 10 seconds
- Over-limit: Returns
429 Too Many Requests
Payload Guidelines¶
Size Limits¶
- Maximum: 10 MB per request
- Recommended: < 1 MB for optimal performance
Format¶
- Required: Valid JSON
- Flexible: Any structure accepted
- Encoding: UTF-8
Best Practices¶
- Include timestamps: Use ISO 8601 format with timezone
- Include identifiers: Order IDs, table numbers, transaction IDs
- Use consistent field names: Helps with future processing
- Include event type: Helps with categorization
Testing¶
Using curl¶
# Production (default)
curl -X POST \
-H "Content-Type: application/json" \
-d '{"event": "order_created", "timestamp": "2025-11-24T18:30:00Z", "order_id": "ORD-001"}' \
https://api.emiliavision.com/webhooks/v1/pos/YOUR_TOKEN_HERE
# Development/Testing environment
curl -X POST \
-H "Content-Type: application/json" \
-d '{"event": "test", "timestamp": "2025-11-24T18:30:00Z", "test": true}' \
"https://api.emiliavision.com/webhooks/v1/pos/YOUR_TOKEN_HERE?env=dev"
# With file
curl -X POST \
-H "Content-Type: application/json" \
-d @payload.json \
"https://api.emiliavision.com/webhooks/v1/pos/YOUR_TOKEN_HERE?env=dev"
Using Python¶
import requests
import json
from datetime import datetime, timezone
# Your webhook token
TOKEN = "019ab6ed-150a-7be1-920f-ab82305e5d41"
# For production
URL_PROD = f"https://api.emiliavision.com/webhooks/v1/pos/{TOKEN}"
# For development/testing
URL_DEV = f"https://api.emiliavision.com/webhooks/v1/pos/{TOKEN}?env=dev"
# Sample payload
payload = {
"event": "order_created",
"timestamp": datetime.now(timezone.utc).isoformat(),
"order_id": "TEST-001",
"table": "Mesa 8",
"total": 41.00
}
# Send to development environment for testing
response = requests.post(
URL_DEV,
json=payload,
timeout=10
)
print(f"Status: {response.status_code}")
print(f"Response: {response.json()}")
# Send to production (when ready)
# response = requests.post(URL_PROD, json=payload, timeout=10)
Error Handling¶
Retry Strategy¶
Implement exponential backoff for failed requests:
import time
import requests
def send_webhook_with_retry(url, payload, max_retries=3):
for attempt in range(max_retries):
try:
response = requests.post(url, json=payload, timeout=10)
if response.status_code == 200:
return response.json()
if response.status_code == 401:
# Don't retry authentication errors
raise Exception("Invalid token")
if response.status_code in [500, 503]:
# Retry server errors
wait_time = (2 ** attempt) # 1s, 2s, 4s
time.sleep(wait_time)
continue
# Other errors
raise Exception(f"HTTP {response.status_code}")
except requests.exceptions.Timeout:
wait_time = (2 ** attempt)
time.sleep(wait_time)
raise Exception("Max retries exceeded")
Logging¶
Log webhook attempts for debugging:
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def send_webhook(url, payload):
logger.info(f"Sending webhook to {url}")
logger.debug(f"Payload: {json.dumps(payload, indent=2)}")
try:
response = requests.post(url, json=payload)
logger.info(f"Response: {response.status_code}")
return response
except Exception as e:
logger.error(f"Webhook error: {e}")
raise
Security¶
Token Protection¶
- Store tokens in environment variables or secure vaults
- Never commit tokens to version control
- Rotate tokens periodically (contact EmiliaVision)
HTTPS¶
All webhook endpoints use HTTPS with TLS 1.2+. HTTP is not supported.
IP Whitelisting¶
Contact EmiliaVision if you need IP whitelisting for additional security.
Support¶
Getting Help¶
- Email: [email protected]
- Documentation: https://docs.emiliavision.com
- Status Page: https://status.emiliavision.com
Token Management¶
To request a new token or manage existing tokens: 1. Contact your EmiliaVision account manager 2. Provide: Restaurant name, location, and use case 3. Receive: Token and configuration details
Troubleshooting¶
| Issue | Solution |
|---|---|
| 401 Unauthorized | Verify token is correct and active |
| 413 Payload Too Large | Reduce payload size or split into multiple requests |
| Timeout | Check network connectivity and retry |
| 500/503 Errors | Wait and retry with exponential backoff |
Appendix: Example Payloads¶
Order Created¶
{
"event": "order_created",
"timestamp": "2025-11-24T18:30:00-03:00",
"order_id": "ORD-2025-0124",
"table": "Mesa 8",
"server": "João Silva",
"items": [
{
"id": "ITEM-001",
"name": "Focaccia",
"quantity": 1,
"price": 19.00
},
{
"id": "ITEM-002",
"name": "Agua com Gas",
"quantity": 2,
"price": 11.00
}
],
"subtotal": 41.00,
"tax": 4.10,
"total": 45.10
}
Payment Completed¶
{
"event": "payment_completed",
"timestamp": "2025-11-24T20:15:00-03:00",
"order_id": "ORD-2025-0124",
"payment_method": "VISA_CREDIT",
"amount": 41.00,
"tip": 4.10,
"total_paid": 45.10,
"transaction_id": "TXN-2025-5678",
"nfe": "35250933111140001168650020000692041393134125"
}
Table Status Change¶
{
"event": "table_status_change",
"timestamp": "2025-11-24T17:00:00-03:00",
"table": "Mesa 8",
"old_status": "available",
"new_status": "occupied",
"party_size": 4
}
Note: This is a generic webhook guide. Specific data format requirements and processing logic are handled separately by EmiliaVision. You can send any valid JSON structure.