Webhooks
Webhooks let you receive real-time notifications when events happen in your Tito account. Instead of polling the API, you configure a URL and Tito will send an HTTP POST request to it whenever a subscribed event occurs.
Supported events
| Event | Description |
|---|---|
order.created |
An order has been placed |
order.updated |
An order has been updated |
Setting up a webhook
- Go to your Account settings
- Navigate to the Webhooks section
- Click Create webhook
- Enter the URL where you want to receive events
- Select the events you want to subscribe to
- Copy your signing secret and store it securely
Webhook URLs must use HTTPS in production. HTTP URLs are allowed in test mode.
Payload format
When an event occurs, Tito sends a POST request to your webhook URL with a JSON payload:
The data.object contains the full resource as returned by the corresponding API endpoint.
{
"id": 123456,
"webhook_event": "order.created",
"state": "pending",
"attempts": 1,
"created_at": "2026-02-08T10:30:00Z",
"data": {
"type": "order",
"id": 789,
"object": {
"id": 789,
"reference": "ABCD-1234",
"name": "Jane Smith",
"email": "jane@example.com"
}
}
}
Request headers
Every webhook request includes the following headers:
| Header | Description |
|---|---|
Content-Type |
application/json |
User-Agent |
Tito-Webhook/1.0 |
X-Tito-Webhook-Event |
The event type, e.g. order.created
|
X-Tito-Timestamp |
Unix timestamp of when the request was sent |
X-Tito-Signature |
HMAC-SHA256 signature for verifying authenticity |
Verifying signatures
Each webhook request is signed using your webhook's secret key. You should verify the signature to ensure the request came from Tito.
The signature is computed as an HMAC-SHA256 hex digest of the timestamp and request body, joined with a period.
timestamp = request.headers["X-Tito-Timestamp"]
signature = request.headers["X-Tito-Signature"]
body = request.raw_post
expected = OpenSSL::HMAC.hexdigest(
"SHA256",
webhook_secret,
"#{timestamp}.#{body}"
)
if ActiveSupport::SecurityUtils.secure_compare(expected, signature)
# Request is authentic
else
# Reject the request
end
const crypto = require("crypto");
const timestamp = req.headers["x-tito-timestamp"];
const signature = req.headers["x-tito-signature"];
const body = req.body; // raw body string
const expected = crypto
.createHmac("sha256", webhookSecret)
.update(`${timestamp}.${body}`)
.digest("hex");
if (crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
// Request is authentic
} else {
// Reject the request
}
import hmac
import hashlib
timestamp = request.headers["X-Tito-Timestamp"]
signature = request.headers["X-Tito-Signature"]
body = request.get_data(as_text=True)
expected = hmac.new(
webhook_secret.encode(),
f"{timestamp}.{body}".encode(),
hashlib.sha256
).hexdigest()
if hmac.compare_digest(expected, signature):
# Request is authentic
else:
# Reject the request
$timestamp = $_SERVER["HTTP_X_TITO_TIMESTAMP"];
$signature = $_SERVER["HTTP_X_TITO_SIGNATURE"];
$body = file_get_contents("php://input");
$expected = hash_hmac("sha256", "{$timestamp}.{$body}", $webhookSecret);
if (hash_equals($expected, $signature)) {
// Request is authentic
} else {
// Reject the request
}
Responding to webhooks
Your endpoint should return a 2xx status code to acknowledge receipt. Any other status code is treated as a failure.
Retries and failure handling
If delivery fails, Tito retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | 30 seconds |
| 2 | 2 minutes |
| 3 | 8 minutes |
| 4 | 32 minutes |
| 5 | ~2 hours |
After 5 retry attempts, the delivery is marked as failed. If a webhook accumulates 10 consecutive failures across any events, it is automatically disabled. You can re-enable it from your Account settings.
Retention
Webhook event logs are retained for 7 days and then automatically pruned.
Best practices
- Always verify signatures to ensure requests are from Tito
-
Respond quickly with a
2xxstatus code, then process the payload asynchronously - Handle duplicates — in rare cases the same event may be delivered more than once
- Use HTTPS to protect the payload in transit
- Keep your signing secret safe — regenerate it if you suspect it has been compromised