Pro tier · $249/mo

API & Webhooks

Programmatic access to your VinSnap Business shop. REST endpoints for customers, jobs, invoices, parts, plus outbound webhooks for every state change. JSON-only, HTTPS-only, predictable.

Overview

The API is REST over HTTPS. All requests and responses are JSON. Base URL: https://vinsnap.net/.netlify/functions/api.

Endpoints are scoped to the authenticated shop — you'll only ever see your own customers, jobs, etc. There is no concept of cross-shop access. Multi-location organizations on the Pro tier can mint separate API keys per location.

Pro-tier feature. The API is included with the Business Pro plan ($249/mo). Solo and Shop tiers can read this doc but key generation is gated to Pro.

Authentication

Every request must include a bearer token in the Authorization header. Generate keys at Settings → API Keys.

Authorization: Bearer vsk_live_xxxxxxxxxxxxxxxxxxxxxxxx

Tokens are prefixed: vsk_test_ for sandbox keys (don't hit production data) and vsk_live_ for production. Keep live keys server-side — never embed them in browser JavaScript or mobile bundles.

Rate limits

Endpoint typeLimitWindow
Reads (GET)600 requestsper minute
Writes (POST/PUT/DELETE)120 requestsper minute
Parts search60 requestsper minute
VIN decode30 requestsper minute

Responses include rate-limit headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (Unix timestamp). On exceed, you get a 429 with Retry-After seconds. Burst above the limit and your key is throttled for 60 seconds.

Errors & status codes

Standard HTTP semantics. Errors return JSON:

{
  "success": false,
  "error": {
    "code": "customer_not_found",
    "message": "No customer with id 12345",
    "request_id": "req_3KBYZ8"
  }
}
StatusMeaning
200 OKSuccess
201 CreatedResource created
400 Bad RequestInvalid input — see error.message
401 UnauthorizedMissing / invalid bearer token
403 ForbiddenToken lacks permission for this endpoint
404 Not FoundResource doesn't exist (or doesn't belong to your shop)
429 Too Many RequestsRate-limited — see Retry-After
5xxServer error — safe to retry with backoff

Customers

GET/customers
GET/customers/:id
POST/customers

Update / delete

PUT/customers/:id
DELETE/customers/:id

Create a customer:

curl -X POST https://vinsnap.net/.netlify/functions/api/customers \
  -H "Authorization: Bearer vsk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Sarah Chen",
    "phone": "555-0142",
    "email": "s.chen@email.com"
  }'

Customer vehicles

GET/customers/:id/vehicles
POST/customers/:id/vehicles

Vehicle POST with a vin field will auto-decode the VIN and populate year/make/model/trim. Pass auto_decode: false to skip that step.

Jobs

GET/jobs
GET/jobs/:id
POST/jobs
PUT/jobs/:id
DELETE/jobs/:id

GET /jobs accepts query params: ?status=pending|in-progress|completed, ?customer_id=..., ?limit=50&offset=0.

Invoices

GET/invoices
POST/invoices
PUT/invoices/:id
POST/invoices/:id/send

POST /invoices/:id/send generates the branded PDF and emails it to the customer. Returns the PDF URL + the email's message_id.

Parts

GET/parts/search?vin=:vin&type=:type

Searches the 1.2M-part TecDoc catalog filtered to the exact vehicle decoded from the VIN. type is optional (e.g. brake-pads, oil-filter). Results include OEM cross-references and stock availability.

Webhooks

Configure outbound webhooks at Settings → Webhooks. Each event POSTs JSON to your URL with the resource as the payload and the event type in the X-VinSnap-Event header.

EventFires when
customer.createdNew customer added
customer.updatedCustomer record changed
job.createdNew job opened
job.status_changedKanban move (e.g. pending → in-progress)
job.completedJob marked done
invoice.createdNew invoice generated
invoice.sentInvoice emailed to customer
invoice.paidInvoice marked paid

Signing

Every payload is signed with HMAC-SHA256. Verify the X-VinSnap-Signature header against your endpoint secret:

const crypto = require('crypto');

function verify(rawBody, signatureHeader, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader)
  );
}
Always verify signatures. Without verification, anyone who guesses your URL could send fake events. The HMAC check takes <1ms and protects against replay if you reject events older than 5 minutes (check the X-VinSnap-Timestamp header).

Retries

We retry failed deliveries with exponential backoff: 1m, 5m, 30m, 2h, 12h, 24h. Total 6 attempts over ~40 hours. Your endpoint should return a 2xx as soon as the event is queued — do the actual work asynchronously.

Webhook delivery logs (last 30 days, with payloads + your response status) are visible at Settings → Webhooks → Activity.


Found a bug or want a new endpoint? Email us.