Documentation · Appendices
Webhook Event Reference
This appendix describes the outbound webhook events fired by the ADP Car Market Hub plugin.
When to use this document
Use this reference when building a receiver, when verifying signatures, or when documenting an integration. For the conceptual description, see Webhooks.
Overview
The plugin can fire two outbound webhook events:
new_lead— when a contact-form submission is saved as a lead.new_import— when a vehicle is created or updated during an import.
Each event is a JSON POST to a URL configured by the administrator. When a shared secret is configured, the request also carries an HMAC-SHA256 signature header that the receiver can use to verify the payload.
The events are independent: configuring one URL does not enable the other. Leaving a URL empty disables that event entirely.
Current behavior note: In the current plugin version only
new_importis actually dispatched. Thenew_leaddelivery path is present (receiver, signing and theas24ci_webhook_url_new_leadoption all exist) but its trigger is not fired in the current code, so configuring anew_leadURL will not produce requests yet. For lead notifications today, rely on the lead emails (see Leads Reference). The schema below documents the intendednew_leadpayload for receivers that want to be ready for it.
Event summary
| Event | Trigger | Configured by |
|---|---|---|
new_lead | A contact-form submission is saved (as24ci_lead_saved action). | as24ci_webhook_url_new_lead option. |
new_import | A vehicle is created or updated during an import. | as24ci_webhook_url_new_import option. |
A shared signing secret is configured globally via as24ci_webhook_secret.
Payload format
All payloads are JSON objects with event, timestamp (ISO 8601, UTC) and an event-specific data block.
new_lead
{
"event": "new_lead",
"timestamp": "2025-01-01T12:00:00+00:00",
"lead_id": 0,
"data": {
"name": "",
"email": "",
"phone": "",
"message": "",
"vehicle_id": 0,
"vehicle_title": "",
"vehicle_url": ""
}
}
new_import
{
"event": "new_import",
"timestamp": "2025-01-01T12:00:00+00:00",
"post_id": 0,
"listing_id": "",
"data": {
"title": "",
"url": "",
"make": "",
"model": "",
"price": 0
}
}
The new_import payload is fired for both new inserts and updates. The hook callback receives an is_update flag internally; the outbound payload itself does not include this flag in the current plugin version. Verify whether your integration requires distinguishing the two cases against the current source before publishing.
Signature verification
When as24ci_webhook_secret is set, the plugin adds the header:
X-AS24CI-Signature: <hmac_sha256(payload_json, secret)>
The signature is computed over the exact JSON body sent in the request. To verify on the receiver:
- Read the raw request body without re-encoding it.
- Compute
hash_hmac('sha256', body, secret)using the same shared secret. - Compare the result with
X-AS24CI-Signatureusing a constant-time comparison (for examplehash_equalsin PHP).
If no secret is configured, the X-AS24CI-Signature header is not sent. Production deployments should always configure a secret.
Delivery, retries and timeouts
- Requests use
wp_remote_post()with a 15-second timeout. - The first attempt is non-blocking ("fire and forget"). The plugin then schedules a follow-up cron event (
as24ci_webhook_retry) about 60 seconds later that performs a blocking re-send so the response code can be inspected. - On connection errors or HTTP
5xx, the plugin schedules additional retries with exponential-style backoff (~2 minutes, then ~4 minutes), up to a maximum of three attempts in total. - HTTP
4xxresponses are treated as terminal and not retried; fix the receiver and re-trigger the source event if needed. - Receivers should respond within 15 seconds and ideally complete heavy work asynchronously.
Configuration
| Option key | Stored value |
|---|---|
as24ci_webhook_url_new_lead | URL invoked for the new_lead event. Empty disables the event. |
as24ci_webhook_url_new_import | URL invoked for the new_import event. Empty disables the event. |
as24ci_webhook_secret | Shared secret used to sign payloads. Sensitive. Empty disables signing. |
URLs are validated with filter_var( ..., FILTER_VALIDATE_URL ). Invalid URLs are silently skipped — set valid https:// URLs in production.
Operational notes
- The plugin only fires webhooks; it does not maintain a delivery queue, replay log or dead-letter store. If reliable, ordered delivery is critical for your use case, run the receiver behind a queueing layer.
- Each webhook is fired from the same WordPress request that generated the source event. Slow receivers will not stall the user-facing request because the first attempt is non-blocking.
- The
new_leadpayload only includes the small set of fields shown above. Additional lead metadata is available by querying theas24ci_leadpost type with normal WordPress functions. - The
new_importpayload intentionally stays small. To retrieve the full vehicle record, fetch/wp-json/as24ci/v1/vehicles/<post_id>(when the public REST API is enabled).
Troubleshooting
- Webhook never arrives. Verify that the URL is HTTPS, reachable from the WordPress server and validates as a URL. Check the plugin logs and the webserver error log.
- Webhook arrives once but no retries. The first attempt is intentionally fire-and-forget; verify that the
as24ci_webhook_retryevent is being scheduled (it relies on WP-Cron or your external cron). - Signature mismatch on the receiver. Confirm both sides use the exact same secret and that the receiver verifies the raw request body without re-serialising or pretty-printing it before hashing.
- Duplicate
new_importdeliveries. Change detection skips vehicles whose payload is unchanged, but a forced full sync may re-emit events for already-known listings. - Stuck retries. WP-Cron requires regular site traffic to fire. On low-traffic sites, configure a real cron job or the REST cron endpoint described in Cron Hook Reference.