Home · API documentation
REST API for dealer integrations. Pull your orders into your DMS, CRM, or any internal tool. All responses are JSON. All amounts are in USD cents — never floats.
All requests require a bearer token in the Authorization header. Issue and revoke tokens on the API keys page of your dealer account.
Authorization: Bearer lilis_live_<64-hex-chars>
Tokens are shown once on creation and never again. Store yours in a secret manager (Doppler, AWS Secrets Manager, 1Password). Revoke a leaked token immediately — revocation is instant.
Standard HTTP status codes. The JSON body always includes an error field with a short machine code; sometimes also a message for humans.
400 — invalid query params or body401 — missing or invalid bearer token403 — token valid but not allowed to access that resource404 — resource not found429 — rate-limited (see headers for retry timing)5xx — our problem; safe to retry with backoff60 requests per minute per API key. Limits are enforced via a fixed 1-minute window — at the top of each minute the bucket resets.
Every 200 response carries headers so your client can preempt limits:
X-RateLimit-Limit: 60 X-RateLimit-Remaining: 47 X-RateLimit-Reset: 1748808660
When exceeded you get a 429 Too Many Requests with Retry-After (seconds until the next bucket) and the same X-RateLimit-* headers. Body:
{
"error": "rate_limited",
"message": "Exceeded 60 requests per minute. Retry after 24 seconds."
}We’ll raise the limit for high-volume integrations — email api@lilisregistration.com.
/api/v1/ordersList orders for your dealer account. Sorted newest first. Customer PII (name, email, phone) is intentionally never returned.
| Name | Type | Description |
|---|---|---|
status | string | Filter by exact status (paid, submitted, in_review, completed, refunded, …) |
since | ISO date-time | Only orders created at or after this timestamp |
limit | integer 1-100 | Default 50 |
offset | integer ≥ 0 | For pagination |
curl -H "Authorization: Bearer lilis_live_…" \ "https://lilisregistration.com/api/v1/orders?status=paid&limit=20"
{
"orders": [
{
"id": "1a2b3c4d-5e6f-7890-abcd-ef1234567890",
"serviceType": "registration_renewal",
"assetType": "vehicle",
"state": "CA",
"status": "paid",
"totalCents": 22400,
"createdAt": "2026-06-01T18:30:00Z",
"submittedAt": null,
"completedAt": null,
"provider": "partner",
"providerRef": null
}
],
"pagination": {
"total": 1,
"limit": 20,
"offset": 0,
"hasMore": false
}
}Push notifications for order events. Configure endpoints in your dealer dashboard. We POST a JSON body to your URL when subscribed events fire — no polling.
order.paid — Stripe checkout completedorder.submitted — sent to the fulfillment providerorder.in_review — DMV / provider is reviewingorder.completed — DMV confirmed the filingorder.refunded — full refund issuedorder.failed — fulfillment failed (see failure_reason)order.cancelled — order cancelled before completionLeave the “Subscribed events” list empty in the dashboard to receive every event type.
Content-Type: application/json. Body shape:
{
"id": "1a2b3c4d-5e6f-7890-abcd-ef1234567890",
"event": "order.paid",
"created_at": "2026-06-02T19:34:27.123Z",
"data": {
"id": "1a2b3c4d-5e6f-7890-abcd-ef1234567890",
"serviceType": "registration_renewal",
"assetType": "vehicle",
"state": "CA",
"status": "paid",
"totalCents": 22400,
"createdAt": "2026-06-02T18:30:00Z",
"submittedAt": null,
"completedAt": null,
"provider": "partner",
"providerRef": null
}
}id doubles as a delivery id — use it to dedupe on your side in case a retry succeeds after your first 2xx accidentally got lost.
X-Lilis-Signature: sha256=<hex> — HMAC-SHA256 of the raw body using your endpoint’s signing secretX-Lilis-Event-Source: lilisregistration — constant, lets you filter at the LBUser-Agent: lili-webhooks/1.0Compute HMAC-SHA256 of the raw request body using your secret, prepend sha256=, and compare in constant time. Read the body as a string before any JSON parsing — re-serializing the parsed object will produce a different byte sequence and the signature will fail.
Node.js (Express):
import crypto from "node:crypto";
import express from "express";
const app = express();
// Crucial: read the body as a Buffer, not auto-JSON-parsed.
app.post("/lilis-webhook", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.header("x-lilis-signature") ?? "";
const expected = "sha256=" + crypto
.createHmac("sha256", process.env.LILIS_WEBHOOK_SECRET)
.update(req.body)
.digest("hex");
if (sig.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(401).send("bad signature");
}
const event = JSON.parse(req.body.toString("utf8"));
// Handle event.event, event.data, dedupe on event.id …
res.sendStatus(200);
});Python (Flask):
import hmac, hashlib, os
from flask import Flask, request, abort
app = Flask(__name__)
@app.post("/lilis-webhook")
def lilis_webhook():
raw = request.get_data() # bytes, NOT request.json
secret = os.environ["LILIS_WEBHOOK_SECRET"].encode()
expected = "sha256=" + hmac.new(secret, raw, hashlib.sha256).hexdigest()
received = request.headers.get("x-lilis-signature", "")
if not hmac.compare_digest(expected, received):
abort(401)
event = request.json
# Handle event["event"], event["data"], dedupe on event["id"] …
return "", 200Any response outside the 2xx range, plus connection timeouts and DNS failures, triggers a retry on this schedule:
After 6 attempts the delivery is marked terminal. 4xx responses (other than 408 and 429) are terminal immediately — we assume you’ll never accept the body you just rejected. 10 consecutive failed deliveries auto-disable the endpoint; re-enable it from the dashboard once you’ve fixed the upstream issue.
200 quickly (under 10 s) and queue work behind itid — retries replay the same idstatus as the source of truth, not the event typeGET /health on your endpoint so we can re-enable safelyMachine-readable OpenAPI 3.1 spec is at /api/v1/openapi.json. Import into Postman, Insomnia, or feed into openapi-generator to produce a typed client in your language of choice.
Questions? Email api@lilisregistration.com.