Skip to content

EmiliaVision Webhook API Reference

Version: 2.0
Base URL: https://api.emiliavision.com/webhooks/v1/pos
Protocol: HTTPS only
Format: JSON


๐Ÿ”‘ Authentication

Webhook Token

Each integration receives a unique UUID token that must be included in the URL path.

Format: {base_url}/{token}

Example:

https://api.emiliavision.com/webhooks/v1/pos/019ab6ed-150a-7be1-920f-ab82305e5d41

Token Security

  • Tokens are validated on each request
  • Invalid tokens return 401 Unauthorized
  • Tokens can be deactivated remotely if compromised
  • Request new tokens via [email protected]

๐ŸŒ Environments

Production (Default)

https://api.emiliavision.com/webhooks/v1/pos/{token}

Development/Testing

https://api.emiliavision.com/webhooks/v1/pos/{token}?env=dev

Query Parameters: | Parameter | Values | Default | Description | |-----------|---------|---------|-------------| | env | dev, prod | prod | Target environment for data storage |


๐Ÿ“จ Request Specification

HTTP Method

POST

Headers

Header Required Value Description
Content-Type Yes application/json Request body format
Content-Length Yes <size> Payload size in bytes
X-Request-ID No <uuid> Your tracking ID for debugging

Request Limits

Limit Value Description
Max Payload Size 10 MB Returns 413 Payload Too Large if exceeded
Request Timeout 30 seconds Connection timeout
Rate Limit 100 req/sec Per token rate limiting
Burst Limit 200 req/10sec Short burst allowance

๐Ÿ“Š Event Types

1. session_opened

Sent when a new table/check is opened.

Required Fields: | Field | Type | Description | Example | |-------|------|-------------|---------| | event | string | Event type identifier | "session_opened" | | timestamp | ISO 8601 | When the session started | "2025-11-24T18:30:00-03:00" | | order_id | string | Your unique order identifier | "ORD-2025-0124" | | table_number | string | Table identifier | "Mesa 8" |

Optional Fields: | Field | Type | Description | Example | |-------|------|-------------|---------| | server.id | integer | Employee ID who opened | 72 | | server.name | string | Employee name | "Joรฃo Silva" | | party_size | integer | Number of guests | 4 |

Example Payload:

{
  "event": "session_opened",
  "timestamp": "2025-11-24T18:30:00-03:00",
  "order_id": "ORD-2025-0124",
  "table_number": "Mesa 8",
  "server": {
    "id": 72,
    "name": "Joรฃo Silva"
  },
  "party_size": 4
}

2. items_added

Sent when items are added to an order.

Required Fields: | Field | Type | Description | Example | |-------|------|-------------|---------| | event | string | Event type identifier | "items_added" | | timestamp | ISO 8601 | When items were added | "2025-11-24T18:35:00-03:00" | | order_id | string | Order identifier | "ORD-2025-0124" | | table_number | string | Table identifier | "Mesa 8" | | items | array | List of items added | See items structure |

Items Array Structure: | Field | Type | Required | Description | Example | |-------|------|----------|-------------|---------| | timestamp | ISO 8601 | Yes | When this specific item was ordered | "2025-11-24T18:35:00-03:00" | | employee_id | integer | Yes | Who took the order | 72 | | name | string | Yes | Item name | "Focaccia" | | quantity | integer | Yes | Number ordered | 1 | | price | decimal | Yes | Unit price | 19.00 | | employee_name | string | No | Employee name | "Joรฃo Silva" | | item_id | string | No | Your item SKU | "ITEM-001" | | category | string | No | Item category | "Appetizers" | | modifiers | array | No | Customizations | ["Extra cheese"] |

Example Payload:

{
  "event": "items_added",
  "timestamp": "2025-11-24T18:35:00-03:00",
  "order_id": "ORD-2025-0124",
  "table_number": "Mesa 8",
  "items": [
    {
      "timestamp": "2025-11-24T18:35:00-03:00",
      "employee_id": 72,
      "employee_name": "Joรฃo Silva",
      "item_id": "ITEM-001",
      "name": "Focaccia",
      "category": "Appetizers",
      "quantity": 1,
      "price": 19.00,
      "modifiers": []
    },
    {
      "timestamp": "2025-11-24T18:35:00-03:00",
      "employee_id": 72,
      "employee_name": "Joรฃo Silva",
      "item_id": "ITEM-002",
      "name": "Agua com Gas",
      "category": "Beverages",
      "quantity": 2,
      "price": 11.00,
      "modifiers": ["Lemon"]
    }
  ]
}

3. items_removed

Sent when items are cancelled or removed.

Required Fields: | Field | Type | Description | Example | |-------|------|-------------|---------| | event | string | Event type identifier | "items_removed" | | timestamp | ISO 8601 | When items were removed | "2025-11-24T18:40:00-03:00" | | order_id | string | Order identifier | "ORD-2025-0124" | | table_number | string | Table identifier | "Mesa 8" | | items | array | List of items removed | See structure above |

Optional Fields: | Field | Type | Description | Example | |-------|------|-------------|---------| | reason | string | Cancellation reason | "Customer changed mind" | | cancelled_by | object | Employee who cancelled | {"id": 72, "name": "Joรฃo Silva"} |

4. session_closed

Sent when check is closed/paid.

Required Fields: | Field | Type | Description | Example | |-------|------|-------------|---------| | event | string | Event type identifier | "session_closed" | | timestamp | ISO 8601 | When session ended | "2025-11-24T20:15:00-03:00" | | order_id | string | Order identifier | "ORD-2025-0124" | | table_number | string | Table identifier | "Mesa 8" |

Recommended Fields: | Field | Type | Description | Example | |-------|------|-------------|---------| | session_start | ISO 8601 | Original open time | "2025-11-24T18:30:00-03:00" | | session_end | ISO 8601 | Close time | "2025-11-24T20:15:00-03:00" | | duration_minutes | integer | Total duration | 105 | | totals.subtotal | decimal | Pre-tax/tip amount | 194.00 | | totals.tip | decimal | Tip amount | 25.22 | | totals.tax | decimal | Tax amount | 19.40 | | totals.total | decimal | Final amount | 238.62 | | payment.method | string | Payment type | "VISA_CREDIT" | | closed_by | object | Employee who closed | {"id": 96, "name": "Maria Santos"} |

Full Example:

{
  "event": "session_closed",
  "timestamp": "2025-11-24T20:15:00-03:00",
  "order_id": "ORD-2025-0124",
  "table_number": "Mesa 8",
  "session_start": "2025-11-24T18:30:00-03:00",
  "session_end": "2025-11-24T20:15:00-03:00",
  "duration_minutes": 105,
  "totals": {
    "subtotal": 194.00,
    "tip": 25.22,
    "tax": 19.40,
    "total": 238.62
  },
  "payment": {
    "method": "VISA_CREDIT",
    "fiscal_receipt": "NFe35251133111140001168650020000762241540579084"
  },
  "closed_by": {
    "id": 96,
    "name": "Maria Santos"
  }
}

5. session_complete

Complete session data in a single webhook (alternative to multiple events).

This is ideal when you want to send everything at once after payment.

Example Payload:

{
  "event": "session_complete",
  "timestamp": "2025-11-24T20:15:00-03:00",
  "order_id": "ORD-2025-0124",
  "table_number": "Mesa 8",
  "session_start": "2025-11-24T18:30:00-03:00",
  "session_end": "2025-11-24T20:15:00-03:00",
  "duration_minutes": 105,
  "opened_by": {
    "id": 72,
    "name": "Joรฃo Silva"
  },
  "closed_by": {
    "id": 96,
    "name": "Maria Santos"
  },
  "items": [
    {
      "timestamp": "2025-11-24T18:35:00-03:00",
      "employee_id": 72,
      "name": "Focaccia",
      "quantity": 1,
      "price": 19.00
    },
    {
      "timestamp": "2025-11-24T18:35:00-03:00",
      "employee_id": 72,
      "name": "Agua com Gas",
      "quantity": 2,
      "price": 11.00
    },
    {
      "timestamp": "2025-11-24T18:45:00-03:00",
      "employee_id": 72,
      "name": "Carbonara Classica",
      "quantity": 1,
      "price": 85.00
    },
    {
      "timestamp": "2025-11-24T19:50:00-03:00",
      "employee_id": 96,
      "name": "Tiramisu",
      "quantity": 2,
      "price": 36.00
    }
  ],
  "totals": {
    "subtotal": 187.00,
    "tip": 24.31,
    "tax": 18.70,
    "total": 230.01
  },
  "payment": {
    "method": "VISA_CREDIT",
    "transaction_id": "TXN-2025-5678",
    "fiscal_receipt": "NFe35251133111140001168650020000762241540579084"
  }
}


โœ… Response Codes

Success Responses

200 OK

Webhook successfully received and stored.

Response Body:

{
  "status": "success",
  "event_id": "019ab6ed-150a-7be1-920f-ab82305e5d41",
  "message": "Webhook received and stored",
  "received_at": "2025-11-24T18:30:00.123Z"
}

Response Fields: | Field | Type | Description | |-------|------|-------------| | status | string | Always "success" | | event_id | UUID | Unique identifier for this webhook event | | message | string | Confirmation message | | received_at | ISO 8601 | Server timestamp (UTC) |

Error Responses

401 Unauthorized

Invalid or inactive webhook token.

Response Body:

{
  "detail": "Invalid or inactive webhook token"
}

413 Payload Too Large

Request body exceeds 10MB limit.

Response Body:

{
  "detail": "Payload too large. Max size: 10MB"
}

429 Too Many Requests

Rate limit exceeded.

Response Body:

{
  "detail": "Rate limit exceeded. Please retry after 1 second"
}

500 Internal Server Error

Server-side processing error.

Response Body:

{
  "detail": "Failed to process webhook"
}

503 Service Unavailable

Service temporarily unavailable.

Response Body:

{
  "detail": "Service temporarily unavailable"
}


๐Ÿ”„ Retry Strategy

import time
import requests

def send_webhook_with_retry(url, payload, max_retries=3):
    """
    Send webhook with exponential backoff retry
    """
    for attempt in range(max_retries):
        try:
            response = requests.post(
                url,
                json=payload,
                timeout=10
            )

            if response.status_code == 200:
                return response.json()

            # Don't retry client errors
            if 400 <= response.status_code < 500:
                if response.status_code != 429:  # Except rate limit
                    raise Exception(f"Client error: {response.status_code}")

            # Retry server errors and rate limits
            if response.status_code in [429, 500, 502, 503, 504]:
                if attempt < max_retries - 1:
                    # Exponential backoff: 1s, 2s, 4s
                    wait_time = 2 ** attempt
                    time.sleep(wait_time)
                    continue

            raise Exception(f"HTTP {response.status_code}: {response.text}")

        except requests.exceptions.Timeout:
            if attempt < max_retries - 1:
                wait_time = 2 ** attempt
                time.sleep(wait_time)
                continue
            raise
        except requests.exceptions.ConnectionError:
            if attempt < max_retries - 1:
                wait_time = 2 ** attempt
                time.sleep(wait_time)
                continue
            raise

    raise Exception(f"Max retries ({max_retries}) exceeded")

Retry Guidelines

Error Type Retry? Strategy
401 Unauthorized No Check token validity
413 Payload Too Large No Reduce payload size
429 Rate Limited Yes Wait 1 second minimum
500 Server Error Yes Exponential backoff
503 Unavailable Yes Exponential backoff
Network timeout Yes Exponential backoff
Connection error Yes Exponential backoff

๐Ÿ“ Field Data Types

String Fields

  • UTF-8 encoded
  • Maximum 65,535 characters
  • Examples: table_number, order_id, name

Numeric Fields

  • Decimals: Up to 2 decimal places for currency
  • Integers: 32-bit signed integers
  • Examples: price (19.00), employee_id (72)

Timestamp Fields

  • ISO 8601 format with timezone
  • Format: YYYY-MM-DDTHH:MM:SSยฑHH:MM
  • Example: 2025-11-24T18:30:00-03:00

Array Fields

  • JSON arrays
  • Can be empty []
  • Maximum 1000 items per array

Object Fields

  • JSON objects
  • Can be null or empty {}
  • Nested up to 5 levels deep

๐ŸŒ Supported Timezones

Include timezone information in all timestamps. Supported formats:

  • Offset format: 2025-11-24T18:30:00-03:00 (recommended)
  • UTC format: 2025-11-24T21:30:00Z
  • Named timezone: Not recommended, use offset instead

Common timezone offsets: - Sรฃo Paulo: -03:00 - New York: -05:00 (EST) / -04:00 (EDT) - London: +00:00 (GMT) / +01:00 (BST) - Tokyo: +09:00


๐Ÿงช Testing

Test Endpoint

Add ?env=dev to test without affecting production data:

https://api.emiliavision.com/webhooks/v1/pos/{token}?env=dev

Test with cURL

# Minimal test
curl -X POST \
  "https://api.emiliavision.com/webhooks/v1/pos/YOUR_TOKEN?env=dev" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "test",
    "table_number": "Test Table",
    "timestamp": "2025-11-24T12:00:00-03:00"
  }'

# Complete session test
curl -X POST \
  "https://api.emiliavision.com/webhooks/v1/pos/YOUR_TOKEN?env=dev" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "session_complete",
    "order_id": "TEST-001",
    "table_number": "Mesa Test",
    "session_start": "2025-11-24T12:00:00-03:00",
    "session_end": "2025-11-24T13:00:00-03:00",
    "items": [
      {
        "timestamp": "2025-11-24T12:10:00-03:00",
        "employee_id": 1,
        "name": "Test Item",
        "quantity": 1,
        "price": 10.00
      }
    ],
    "totals": {
      "subtotal": 10.00,
      "tip": 1.30,
      "tax": 1.00,
      "total": 12.30
    }
  }'

Validation Tool

Coming soon: Online webhook tester at https://test.emiliavision.com/webhook


๐Ÿ“‹ Integration Checklist

Minimum Required Fields

  • event - Event type
  • table_number - Table identifier (CRITICAL!)
  • timestamp - When event occurred
  • order_id - Your unique identifier
  • session_start - When table opened
  • session_end - When check closed
  • employee_id - Who performed actions
  • items[].timestamp - When each item ordered
  • totals - Financial summary

Nice to Have

  • employee_name - Human-readable names
  • item_category - Menu categorization
  • fiscal_receipt - Tax document number
  • payment.method - How customer paid

๐Ÿ†˜ Support

Getting Help

Response Times

  • Critical issues: < 2 hours
  • Integration support: < 4 hours
  • Token generation: < 24 hours

Common Issues

Issue Solution
401 errors Verify token is correct and active
Timezone confusion Always include timezone offset in timestamps
Missing table numbers This is critical - ensure every payload has it
No employee IDs Check if your POS tracks who takes orders

Version History - v2.0 (2025-11) - Added environment parameter, enhanced examples - v1.0 (2025-10) - Initial release