Car Battery Pro
Technical Reference

The backend API

The custom CBP API that powers vehicle lookup, postcodes, address search, and installation booking.

The backend API

🛠 Dev

The custom features are powered by a separate backend service — the eb-cbp-rego-lookup repository — deployed to Cloudflare Workers. The theme only ever talks to this API; the API in turn talks to the external providers (vehicle data, postcodes, Google services, Shopify Admin). It is maintained by Conversion Kings while CBP is on retainer (see Who owns what).

The backend has its own documentation in the eb-cbp-rego-lookup repo: a README.md, a Bruno/ Postman request collection under api-docs/, and the upstream AutoInfo specs under api-reference/. Use those for exact request/response payloads; this page is the operational overview.

Stack

ConcernTechnology
FrameworkHono on Cloudflare Workers
LanguageTypeScript
DatabaseCloudflare D1 (SQLite) via Drizzle ORM
Key/valueCloudflare KV (OAuth state + per-shop Shopify tokens)
AuthRS256 JWT via jose
Scheduled jobsCloudflare Cron Trigger (hourly)

Configuration in the theme

The base URL is the api_base_url theme setting, exposed to JS via snippets/api-init.liquid as window.theme.api.base (with a getEndpoint() helper). When debug mode is on, the api_dev_base_url (dev/staging) is used instead.

Authentication

All customer-facing endpoints require a Bearer JWT from POST /v1/auth:

  • Signed RS256 with the server's private key; the worker verifies with the matching public key.
  • The token embeds the caller's forwarded IP/host and expires after 1 hour.
  • assets/session-manager.js fetches, caches (in sessionStorage), and refreshes it. Use window.theme.sessionManager.getToken() for authenticated calls.

The OAuth and webhook routes are not JWT-protected (they're secured by Shopify HMAC instead).

Endpoints

Used by the theme

EndpointMethodPurpose
/v1/authPOSTIssue a 1-hour JWT.
/v1/vehicle/{state}/{rego}GETLook up vehicle(s) by registration + state (AutoInfo).
/v1/vehicle/{state}/{vehicleId}/partsGETBattery fitment for a vehicle, enriched with Shopify product data.
/v1/postcode/{postcode}GETResolve a postcode to suburb/state (Australia Post).
/v1/places?q=GETAddress autocomplete (Google Places).
/v1/places/{placeId}GETStructured address for a selected place.
/v1/scheduleGETInstallation slot availability (Google Calendar).
/v1/schedule/reservePOSTReserve a slot and provision an Order ID.
/v1/contactPOSTContact-form submission (sends email).

Platform / integration (not called by the storefront JS)

EndpointMethodPurpose
/v1/oauth/start, /v1/oauth/callbackGETShopify app OAuth onboarding (stores the shop's Admin API token).
/v1/webhooks/order_paidPOSTShopify webhook that confirms a booking on payment.
/v1/heartbeatGETHealth check.

The /v1/.../parts response is enriched by querying the Shopify Admin API for each fitment SKU. This requires the shop to be onboarded via OAuth (its token stored in KV). If the shop isn't connected, parts lookup returns a 403 and no batteries appear — see Managing battery products and Common issues.

The booking lifecycle (reserve → pay → confirm)

The Order ID ties a calendar slot, a database record, and a Shopify order together across three steps:

  1. ReservePOST /v1/schedule/reserve takes the slot, customer, vehicle and battery details. It flips the Google Calendar event from "Open Slot" to "[UNCONFIRMED] Installation for …", inserts an unconfirmedOrders row (a UUID = the Order ID, confirmedAt = null), emails a notification, and returns the Order ID. The theme attaches it to both cart line items.
  2. Pay — the customer checks out in Shopify. The order carries the _Order ID line-item property.
  3. Confirm — Shopify fires order_paidPOST /v1/webhooks/order_paid. The worker finds the booking by _Order ID, sets confirmedAt, converts the calendar event to a permanent "Installation for …" event, and emails the customer and office.

Unpaid reservations expire. An hourly cron job releases any unconfirmedOrders older than ~1 hour that were never confirmed — it reverts the calendar event back to "Open Slot" and deletes the row. So a slot a customer reserved but didn't pay for is returned to availability automatically. This also means if the order_paid webhook is not configured or fails, a genuinely paid booking can be auto-released — see Common issues.

External providers

ProviderUsed byConfigured via (secret/var names)
AutoInfo (vehicle + parts)/v1/vehicle/*AUTOINFO_USER_ID, AUTOINFO_AUTH_CODE
Australia Post (postcodes)/v1/postcode/*AUSPOST_API_KEY
Google Calendar (slots)/v1/schedule*, webhookGOOGLE_APPLICATION_CREDENTIALS, GOOGLE_IMPERSONATE_EMAIL
Gmail (notifications)reserve, contact, webhookGOOGLE_APPLICATION_CREDENTIALS, GOOGLE_IMPERSONATE_CONTACT_EMAIL
Google Places (addresses)/v1/places*GOOGLE_MAPS_API_KEY
Shopify Admin API (product enrichment, OAuth)/v1/.../parts, OAuthSHOPIFY_APP_CLIENT_ID, SHOPIFY_APP_SHARED_SECRET, SHOPIFY_DEFAULT_SHOP
JWT keys/v1/auth, middlewareJWT_PRIVATE_KEY, JWT_PUBLIC_KEY

These names come from the backend's .dev.vars.example / .env.example. Values are stored as Cloudflare Worker secrets / wrangler vars — never in this handbook or the theme.

Database (D1)

Two tables (Drizzle schema, src/db/schema.ts in the backend repo):

  • installers — each installer's email and Google Calendar ID. Availability is read from these calendars. See Appointments & scheduling.
  • unconfirmedOrders — a booking record per reservation: the Order ID (UUID), vehicle/battery, customer, slot/calendar IDs, booking date/time, createdAt, and confirmedAt.

Environments & deployment

Two Cloudflare accounts, two wrangler configs (see Who owns what):

ProductionStaging
OwnerCar Battery ProConversion Kings
Wrangler configwrangler.jsoncwrangler-ck.jsonc
Worker nameeb-cbp-apicbp-rego-lookup-api
Deploy commandnpm run deploy:prodnpm run deploy

The theme points at production (the api_base_url setting) in the live store, and at staging when debug mode is on (api_dev_base_url).

For a future handover: the API is a separate repository and infrastructure from this theme. Production Cloudflare resources are owned by Car Battery Pro; the staging account belongs to Conversion Kings. A new maintainer needs the eb-cbp-rego-lookup repo, the Cloudflare accounts, all the provider credentials above, and the Shopify app (client ID/secret + order_paid webhook). Confirm exact access during handover.

On this page