C
Cotizera Docs

Webhooks

Overview

Webhooks let you receive real-time HTTP POST notifications when events happen in Cotizera. Instead of polling the API, register a URL and Cotizera will push event data to you automatically.

Webhooks are a PRO plan feature.

Available Events

Event Description
quote.created A new quote was created
quote.status_changed A quote's status transitioned (e.g., Generated → Sent → Won)
quote.revised A new version of a quote was created (PRO)
client.created A new client was added
product.created A new product was added
webhook.test Test event sent via the "Probar" (Test) button

Payload Format

Every webhook delivery is a JSON POST with this structure:

{
  "event": "quote.created",
  "timestamp": "2026-03-31T14:30:00.000Z",
  "data": {
    "id": "clx1abc123",
    "quoteNumber": "COT-0042",
    "status": "GENERATED",
    "total": 15000.00,
    "clientId": "clx2def456",
    "createdAt": "2026-03-31T14:30:00.000Z"
  }
}

The data field contains the full resource object that triggered the event.

HTTP Headers

Each delivery includes these headers:

Header Description
Content-Type application/json
X-Cotizera-Signature HMAC-SHA256 signature of the request body
X-Cotizera-Event Event type (e.g., quote.created)

Creating a Webhook

Via the API

curl -X POST https://cotizera.com/api/webhooks \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{
    "url": "https://your-server.com/webhook",
    "events": ["quote.created", "quote.status_changed"]
  }'

The response includes a secret field — save this for signature verification.

Via the UI

  1. Go to "Configuración" (Settings) → "Webhooks"
  2. Click "Nuevo webhook" (New webhook)
  3. Enter your endpoint URL
  4. Select the events you want to subscribe to
  5. Save — note the webhook secret displayed

Signature Verification

Every webhook delivery is signed with your webhook's secret using HMAC-SHA256. Always verify signatures to ensure the request came from Cotizera.

The signature is in the X-Cotizera-Signature header and is computed over the raw request body.

Node.js

const crypto = require("crypto");
 
function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
 
// Express example
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-cotizera-signature"];
  if (!verifyWebhook(req.body, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }
 
  const event = JSON.parse(req.body);
  console.log(`Received ${event.event}:`, event.data);
  res.status(200).send("OK");
});

Python

import hmac
import hashlib
 
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(), payload, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)
 
# Flask example
@app.route("/webhook", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("X-Cotizera-Signature")
    if not verify_webhook(request.data, signature, WEBHOOK_SECRET):
        return "Invalid signature", 401
 
    event = request.json
    print(f"Received {event['event']}: {event['data']}")
    return "OK", 200

Retry Logic

If your endpoint doesn't respond with a 2xx status code (or times out after 5 seconds), Cotizera retries with exponential backoff:

Attempt Delay
1 Immediate
2 1 minute
3 5 minutes
4 30 minutes
5 2 hours

After 5 failed attempts, the delivery is marked as permanently failed and the tenant owner receives an in-app notification.

Testing Webhooks

Send a test event to any registered webhook:

curl -X POST https://cotizera.com/api/webhooks/{id}/test \
  -b cookies.txt

This sends a webhook.test event with a sample payload to your endpoint. The response includes:

{
  "success": true,
  "statusCode": 200
}

You can also click "Probar" (Test) on any webhook in the Cotizera UI.

Delivery Logs

View delivery history for a webhook:

curl https://cotizera.com/api/webhooks/{id}/deliveries \
  -b cookies.txt

Each delivery record includes the event, payload, HTTP status code, response body, and timestamp.

Best Practices

  1. Respond quickly — Return a 200 within 5 seconds. Process the event asynchronously if needed.
  2. Verify signatures — Always validate X-Cotizera-Signature to prevent spoofed requests.
  3. Handle duplicates — In rare cases, events may be delivered more than once. Use the event timestamp and resource id for idempotency.
  4. Use HTTPS — Always use an HTTPS endpoint to protect payload data in transit.
  5. Monitor delivery logs — Check the webhook delivery logs in the Cotizera UI to debug failures.
© 2026 Cotizera. All rights reserved.