Create Webhook
curl --request POST \
--url https://api.autosnap.com/v1/webhooks \
--header 'Content-Type: application/json' \
--data '
{
"api_key": "<string>",
"url": "<string>",
"event_types": [
"<string>"
],
"secret": "<string>",
"dealership_id": 123,
"max_failures": 10
}
'import requests
url = "https://api.autosnap.com/v1/webhooks"
payload = {
"api_key": "<string>",
"url": "<string>",
"event_types": ["<string>"],
"secret": "<string>",
"dealership_id": 123,
"max_failures": 10
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
api_key: '<string>',
url: '<string>',
event_types: ['<string>'],
secret: '<string>',
dealership_id: 123,
max_failures: 10
})
};
fetch('https://api.autosnap.com/v1/webhooks', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.autosnap.com/v1/webhooks",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'api_key' => '<string>',
'url' => '<string>',
'event_types' => [
'<string>'
],
'secret' => '<string>',
'dealership_id' => 123,
'max_failures' => 10
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.autosnap.com/v1/webhooks"
payload := strings.NewReader("{\n \"api_key\": \"<string>\",\n \"url\": \"<string>\",\n \"event_types\": [\n \"<string>\"\n ],\n \"secret\": \"<string>\",\n \"dealership_id\": 123,\n \"max_failures\": 10\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.autosnap.com/v1/webhooks")
.header("Content-Type", "application/json")
.body("{\n \"api_key\": \"<string>\",\n \"url\": \"<string>\",\n \"event_types\": [\n \"<string>\"\n ],\n \"secret\": \"<string>\",\n \"dealership_id\": 123,\n \"max_failures\": 10\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.autosnap.com/v1/webhooks")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"api_key\": \"<string>\",\n \"url\": \"<string>\",\n \"event_types\": [\n \"<string>\"\n ],\n \"secret\": \"<string>\",\n \"dealership_id\": 123,\n \"max_failures\": 10\n}"
response = http.request(request)
puts response.read_body{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Webhooks
Create Webhook
Register a webhook endpoint for real-time event notifications.
POST
/
v1
/
webhooks
Create Webhook
curl --request POST \
--url https://api.autosnap.com/v1/webhooks \
--header 'Content-Type: application/json' \
--data '
{
"api_key": "<string>",
"url": "<string>",
"event_types": [
"<string>"
],
"secret": "<string>",
"dealership_id": 123,
"max_failures": 10
}
'import requests
url = "https://api.autosnap.com/v1/webhooks"
payload = {
"api_key": "<string>",
"url": "<string>",
"event_types": ["<string>"],
"secret": "<string>",
"dealership_id": 123,
"max_failures": 10
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
api_key: '<string>',
url: '<string>',
event_types: ['<string>'],
secret: '<string>',
dealership_id: 123,
max_failures: 10
})
};
fetch('https://api.autosnap.com/v1/webhooks', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.autosnap.com/v1/webhooks",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'api_key' => '<string>',
'url' => '<string>',
'event_types' => [
'<string>'
],
'secret' => '<string>',
'dealership_id' => 123,
'max_failures' => 10
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.autosnap.com/v1/webhooks"
payload := strings.NewReader("{\n \"api_key\": \"<string>\",\n \"url\": \"<string>\",\n \"event_types\": [\n \"<string>\"\n ],\n \"secret\": \"<string>\",\n \"dealership_id\": 123,\n \"max_failures\": 10\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.autosnap.com/v1/webhooks")
.header("Content-Type", "application/json")
.body("{\n \"api_key\": \"<string>\",\n \"url\": \"<string>\",\n \"event_types\": [\n \"<string>\"\n ],\n \"secret\": \"<string>\",\n \"dealership_id\": 123,\n \"max_failures\": 10\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.autosnap.com/v1/webhooks")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"api_key\": \"<string>\",\n \"url\": \"<string>\",\n \"event_types\": [\n \"<string>\"\n ],\n \"secret\": \"<string>\",\n \"dealership_id\": 123,\n \"max_failures\": 10\n}"
response = http.request(request)
puts response.read_body{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}POST /v1/webhooks
v1.0 schema lock. The response shape on this page, the webhook envelope, the per-event
data payload, and the HMAC signature algorithm are all part of the locked v1.0 contract. We will never remove a documented field, never rename a field, and never change a field’s type without bumping to v2 (see Versioning). New event types may be added additively; your receiver must tolerate unknown event types and unknown keys.X-Autosnap-Signature header for verification. Failed deliveries are retried with exponential backoff.
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
api_key | string | Yes | — | Your API key. Accepted in body (canonical) or ?api_key=… query string. |
url | string | Yes | — | The HTTPS endpoint URL that will receive webhook payloads |
event_types | array[string] | Yes | — | List of event types to subscribe to. See values below. |
secret | string | Yes | — | A secret string you provide (minimum 16 characters) used to generate the X-Autosnap-Signature header for payload verification |
dealership_id | string | No | all | Scope the webhook to a specific dealership (public ID, e.g. dlr_8cfc0b00a98b). If omitted, events for all dealerships under your account are sent. |
max_failures | integer | No | 10 | Number of consecutive delivery failures before the webhook is automatically disabled |
Event Types
All event names use period as the segment separator:<scope>.<event> or <scope>.<subscope>.<event>. Underscores appear only inside multi-word event names (e.g. dealer.config.auto_updated).
Website Inventory Events
| Event | Description |
|---|---|
setup.complete | A dealer setup job finishes successfully |
setup.failed | A dealer setup job fails |
import.complete | A scheduled or on-demand website-scrape inventory import finishes for a dealership |
vehicle.created | A new vehicle is added to a dealership’s inventory (website-scrape source) |
vehicle.updated | An existing vehicle’s data changes (price, photos, mileage, status, etc.) |
vehicle.removed | A vehicle is removed from a dealership’s inventory |
IMS Feed Events
| Event | Description |
|---|---|
ims.import.complete | An IMS feed file was successfully imported (new/updated/removed vehicle counts in payload) |
ims.vehicle.created | A new vehicle appears in an IMS feed |
ims.vehicle.updated | A vehicle’s IMS-feed row changes |
ims.vehicle.removed | A vehicle is removed from an IMS feed (marked inactive) |
ims.feed.stale | An IMS feed file hasn’t been updated within the configured staleness threshold |
Dealer Health Check Events
| Event | Description |
|---|---|
dealer.config.auto_updated | A dealership’s scraper configuration was automatically repaired (e.g. changed site ID) |
dealer.provider.changed | A dealership switched to a different website provider |
Example
curl -X POST "https://api.autosnap.com/v1/webhooks" \
-H "Content-Type: application/json" \
-d '{
"api_key": "YOUR_API_KEY",
"url": "https://your-server.com/webhooks/autosnap",
"event_types": ["setup.complete", "setup.failed", "import.complete", "vehicle.created", "vehicle.updated", "vehicle.removed"],
"secret": "whsec_your_secret_string_here",
"dealership_id": "dlr_8cfc0b00a98b"
}'
Response
{
"success": true,
"created": [
{ "webhook_id": 42, "event_type": "setup.complete" },
{ "webhook_id": 43, "event_type": "setup.failed" },
{ "webhook_id": 44, "event_type": "import.complete" },
{ "webhook_id": 45, "event_type": "vehicle.created" },
{ "webhook_id": 46, "event_type": "vehicle.updated" },
{ "webhook_id": 47, "event_type": "vehicle.removed" }
],
"skipped": [],
"url": "https://your-server.com/webhooks/autosnap",
"dealership_id": "dlr_8cfc0b00a98b"
}
The API creates one webhook record per event type. If you subscribe to 6 event types, the
created array will have 6 entries, each with its own webhook_id.Signature Verification
Every webhook delivery includes anX-Autosnap-Signature header. Verify it by computing an HMAC-SHA256 of the raw request body using the secret you provided:
import hmac, hashlib
def verify_signature(payload_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), payload_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
Error Codes
| Status | Condition |
|---|---|
400 | event_types is empty or contains invalid event types |
400 | url is not an HTTPS URL |
400 | secret is shorter than 16 characters |
404 | dealership_id was provided but no matching dealership was found |
409 | All requested event types already have active subscriptions for this URL |