Webhooks

Webhooks

Integration guide for receiving real-time event notifications (lead, order, supplier order, etc.)

Webhook notification is a POST request with JSON in body

List of available events

TireConnect sends webhook notifications for the following event types:

Leads

Event

Description

Event

Description

lead.appointment

Customer scheduled an appointment.

lead.call

Customer initiated a call to the dealer.

lead.compare.email

Customer requested to receive a compare quote by email.

lead.compare.email.follow_up

Customer received a compare quote by email and requested a follow-up.

lead.compare.print

Customer printed a compare quote.

lead.compare.print.follow_up

Customer printed a compare quote and requested a follow-up.

lead.compare.sms

Customer requested to receive a compare quote by SMS.

lead.compare.sms.follow_up

Customer received a compare quote by SMS and requested a follow-up.

lead.email

Customer requested to receive a quote by email.

lead.email.follow_up

Customer received a quote by email and requested a follow-up.

lead.print

Customer printed a quote.

lead.print.follow_up

Customer printed a quote and requested a follow-up.

lead.quote

Customer requested a quote (general quote request).

lead.sms

Customer requested to receive a quote by SMS.

lead.sms.follow_up

Customer received a quote by SMS and requested a follow-up.

E-commerce orders

Event

Description

Event

Description

order.created

Customer created a new order (order initialized in the system).

order.checked_out

Customer completed checkout (order submitted for processing).

order.paid

Customer successfully paid for the order.

Supplier direct orders

Event

Description

Event

Description

supplier_order.created

Dealer placed a supplier order (order created and sent to supplier).

Setup

  1. Go to Settings → Webhooks in your TireConnect Admin Panel.

  2. Click Add Webhook.

  3. Fill in the following fields:

    • Is Active - toggle to enable or disable the webhook.

    • URL - endpoint that will receive webhook POST requests.

    • Description - short note to identify the webhook.

  4. Click Save to proceed with the webhook setup.

After saving, more fields become available:

  • UUID - unique identifier of the webhook.

  • Secret - secret key used to sign webhook requests. Keep this value private.

  • Events - list of events that trigger this webhook.

Calls

Each webhook record displays recent delivery attempts made by TireConnect.

You can view up to 15 latest webhook calls for each configured webhook.

Note: The call history is temporary and is automatically cleared every 30 minutes.

Signature Verification

Each webhook request includes a Base64-encoded HMAC signature in the following header:

X-Tireconnect-Signature

The signature is generated using the SHA256 algorithm and the Secret value associated with your webhook.

How the signature is created

  1. Retrieve the raw request body exactly as received (as a string). This will be your signature data.

  2. Create an HMAC message using the signature data, the SHA256 algorithm, and your webhook secret.

  3. Encode the result using Base64.

How to validate the signature

To verify that a webhook request came from TireConnect:

  1. Recreate the signature on your end by repeating steps 1–3 above.

  2. Compare your generated signature to the value in the X-Tireconnect-Signature header using a constant-time comparison function (for example, hash_equals in PHP).

⚠️ Important:

Do not compare signatures using a simple string comparison (==), as this may expose your application to timing attacks. Make sure your server does not modify or re-encode the body before validation.

Implementation example

import crypto from 'crypto'; import express from 'express'; const app = express(); const WEBHOOK_SECRET = 'your_webhook_secret'; // Use raw body for webhook route app.use('/webhook', express.raw({ type: '*/*' })); app.post('/webhook', (req, res) => { const signatureHeader = req.header('X-Tireconnect-Signature'); const rawBody = req.body; // Buffer // Recreate HMAC SHA256 and encode in Base64 const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET); hmac.update(rawBody); const generatedSignature = hmac.digest('base64'); // Constant-time comparison const isValid = crypto.timingSafeEqual( Buffer.from(generatedSignature), Buffer.from(signatureHeader) ); if (isValid) { console.log('Valid webhook'); res.sendStatus(200); } else { console.log('Invalid webhook'); res.sendStatus(401); } }); app.listen(3000, () => console.log('Server running on port 3000'));
package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.*; import org.springframework.util.StreamUtils; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.util.Base64; @RestController @SpringBootApplication public class WebhookController { private static final String WEBHOOK_SECRET = "your_webhook_secret"; @PostMapping("/webhook") public String handleWebhook(@RequestHeader("X-Tireconnect-Signature") String signature, @RequestBody byte[] body) throws Exception { // Recreate HMAC SHA256 Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(WEBHOOK_SECRET.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); byte[] hash = mac.doFinal(body); String generatedSignature = Base64.getEncoder().encodeToString(hash); // Constant-time comparison if (constantTimeEquals(signature, generatedSignature)) { System.out.println("Valid webhook"); return "OK"; } else { System.out.println("Invalid webhook"); return "Unauthorized"; } } public static void main(String[] args) { SpringApplication.run(WebhookController.class, args); } private boolean constantTimeEquals(String a, String b) { if (a.length() != b.length()) return false; int result = 0; for (int i = 0; i < a.length(); i++) { result |= a.charAt(i) ^ b.charAt(i); } return result == 0; } }
<?php $WEBHOOK_SECRET = 'your_webhook_secret'; $signatureHeader = $_SERVER['HTTP_X_TIRECONNECT_SIGNATURE']; $rawBody = file_get_contents('php://input'); // Recreate HMAC SHA256 and encode Base64 $generatedSignature = base64_encode(hash_hmac('sha256', $rawBody, $WEBHOOK_SECRET, true)); // Constant-time comparison if (hash_equals($generatedSignature, $signatureHeader)) { http_response_code(200); echo 'Valid webhook'; } else { http_response_code(401); echo 'Invalid webhook'; }
from flask import Flask, request import hmac import hashlib import base64 app = Flask(__name__) WEBHOOK_SECRET = b'your_webhook_secret' @app.route('/webhook', methods=['POST']) def webhook(): signature_header = request.headers.get('X-Tireconnect-Signature') raw_body = request.get_data() # bytes # Recreate HMAC SHA256 generated_signature = base64.b64encode( hmac.new(WEBHOOK_SECRET, raw_body, hashlib.sha256).digest() ).decode() # Constant-time comparison if hmac.compare_digest(generated_signature, signature_header): print('Valid webhook') return '', 200 else: print('Invalid webhook') return '', 401 if __name__ == '__main__': app.run(port=5000)

Body Example

Lead

"data": { "notification_uuid": "a9fb7223-2470-44ab-a9fb-18a8f1846166", "webhook_uuid": "97a2328b-d3de-41c9-b7ca-a6f2954e1019", "event_name": "lead.appointment", "data_type": "lead", "data": { "lead_number": "P22453", "type": "Display", "device": "Desktop", "source": "website", "dealer_id": 11615, "dealer_name": "TireConnect Demo (Single location)", "location_id": 15245, "location_name": "TireConnect Demo", "city": "Del City", "province": "Tennessee", "address": "3903 SE 10th Pl", "postal_code": "37201", "created_at": "2023-04-28T15:23:29-04:00", "follow_up": false, "contacted": false, "preferred_time": null, "total_price": 4057.74, "sub_total": 3768.92, "total_tax": 263.82, "discount": null, "rebates": [ { "total_value": 90, "name": "Michelin $90 Offer", "description": "With purchase of 4 tires, please see official terms and conditions for further details" }, { "total_value": 70, "name": "$70 Continental", "description": "$70 Continental" } ], "tires": [ { "brand": "Michelin", "part_number": "48162", "model": "Defender® LTX® M/S", "size": "285/50R20 116H XL", "quantity": 4, "supplier": "ATD", "frb": "b", "price": 315.35, "total_price": 1261.4 } ], "wheels": [ { "brand": "Centerline", "part_number": "847BM-2106819", "model": "847BM LT7", "size": "20X10 6x135 ET-19", "quantity": 4, "supplier": "Catalog", "frb": "b", "price": null, "total_price": null } ], "customer": { "name": null, "phone": null, "email": null, "notes": null }, "vehicle": { "year": 2019, "make": "Toyota", "model": "Tacoma", "trim": "TRD Off-Road" }, "services": [ { "key": "light_truck_mounting", "name": "Install + Balance (Light Truck)", "description": "", "link": "", "logo": null, "type": "service", "tax_group": "labor", "total_price": 100, "total_tax": 7 }, { "key": "shop_supply_fee", "name": "Shop Supply Fee", "description": "", "link": "", "logo": null, "type": "fee", "tax_group": "labor", "total_price": 25, "total_tax": 0 }, { "key": "disposal_fee", "name": "Tire Recycling Fee", "description": "", "link": "", "logo": null, "type": "fee", "tax_group": "labor", "total_price": 16, "total_tax": 1.12 }, { "key": "wheel_alignment", "name": "Wheel Alignment", "description": "Wheel Alignment is a very important for optimal fuel economy and steering performance.", "link": "https://www.google.com/?gws_rd=ssl#q=nitrogen+filled+tires", "logo": null, "type": "service", "tax_group": "labor", "total_price": 149, "total_tax": 10.43 }, { "key": "wheel_installation_kit", "name": "Wheel Installation Kit", "description": "Parts required for wheel installation.", "link": "", "logo": null, "type": "service", "tax_group": "material", "total_price": 60, "total_tax": 4.2 }, { "key": "wheel_installation_", "name": "Wheel Installation ", "description": "Wheel installation service includes removal of the old wheel and mounting of the new one.", "link": "", "logo": null, "type": "service", "tax_group": "labor", "total_price": 160, "total_tax": 11.2 } ] } }

E-commerce Order

"data": { "notification_uuid": "a9fb7223-2470-44ab-a9fb-18a8f1846166", "webhook_uuid": "97a2328b-d3de-41c9-b7ca-a6f2954e1019", "event_name": "order.checked_out", "data_type": "order", "data": { "city": "New York", "tires": [ { "frb": "b", "size": "205/55R16 91H", "brand": "Bridgestone", "model": "Blizzak WS80", "price": 25, "quantity": 4, "supplier": "TH", "part_number": "013006", "total_price": 100 } ], "status": "incomplete", "wheels": null, "address": "Tire Shop street ", "rebates": [ { "name": "National test", "description": "test", "total_value": 15 } ], "vehicle": { "make": "BMW", "trim": "Base", "year": 2008, "model": "128i" }, "customer": { "name": "oleh qa", "email": "john@doe.com", "notes": "", "phone": "18333492116" }, "discount": { "name": "account discount offer", "description": "test ", "total_value": 50 }, "province": "New York", "services": [ { "key": "disposal_fee", "link": "", "logo": "", "name": "Tire Recycling Fee", "type": "fee", "tax_group": "custom", "total_tax": 0, "description": "", "total_price": 0 }, { "key": "wheel_alignment", "link": "", "logo": "", "name": "Wheel Alignment", "type": "service", "tax_group": "labor", "total_tax": 2, "description": "", "total_price": 50 } ], "dealer_id": 12347, "sub_total": 150, "total_tax": 4, "created_at": "2023-06-15T07:32:57-04:00", "order_time": null, "updated_at": "2023-06-15T07:35:03-04:00", "dealer_name": "New York Shop", "location_id": 14346, "postal_code": "90001", "total_price": 104, "order_number": "T31306", "submitted_at": null, "location_name": "New York Tire Shop", "preferred_time": "2023-06-28 00:00:00", "deposit_payment": 104, "outstanding_balance": 0 } }

Supplier Direct Order

"data": { "notification_uuid": "a9fb7223-2470-44ab-a9fb-18a8f1846166", "webhook_uuid": "97a2328b-d3de-41c9-b7ca-a6f2954e1019", "event_name": "supplier_order.created", "data_type": "supplier_order", "data": { "id": 2629, "tires": { "data": { "items": [ { "brand": "Goodyear", "model": "Eagle Sport All-Season", "price": 53.99, "quantity": 1, "part_number": "109174366", "retail_price": 53.99, "shipping_cost": 0, "price_on_widget": 53.99, "part_number_ordered": "GY 109174366" } ] } }, "branch": "110", "status": 1, "invoice": "16926573", "is_replenishment": false, "order_id": null, "supplier": "tirehub", "dealer_id": 12347, "po_number": "Test PO", "created_at": "2023-06-15T08:37:20-0400", "updated_at": "2023-06-15T08:37:21-0400", "dealer_name": "New York Shop", "location_id": 14346, "status_text": "Successful", "supplier_cid": "TH", "location_name": "New York Tire Shop", "supplier_name": "TireHub", "dealer_location_id": 14346, "request_appointment_id": null } }

Error Handling & Retries

  • TireConnect retries webhook delivery if your endpoint returns HTTP 4xx or 5xx.

  • Retries use the same notification_uuid.

  • Ensure your server responds with 200 OK once the request is processed successfully.