How TimeCinch handles your Lightspeed data
Last updated: June 2, 2026
This page explains exactly what data TimeCinch reads from your Lightspeed Retail (X-Series) account, what we store, how long we keep it, what we transmit back to Lightspeed, and what we never touch. It accompanies our Lightspeed marketplace listing and is updated whenever the integration's data surface changes.
The short version
- We never see, store, or transmit payment card numbers, CVVs, or any other cardholder data.
- We mirror customers, sales, products, and org metadata into an encrypted Supabase Postgres database.
- OAuth tokens are stored encrypted at rest; refresh tokens rotate on every use.
- Webhook signatures are verified with HMAC-SHA256 and a timing-safe compare before any side effect.
- Disconnecting the integration voids your Lightspeed webhook subscriptions and removes mirrored data via foreign-key cascade.
1. OAuth scopes we request, and why
When a merchant connects Lightspeed, we redirect them to Lightspeed's OAuth consent screen with the scopes listed below. The authoritative source for this list is DEFAULT_SCOPES in lib/lightspeed/auth.ts.
| Scope | Purpose |
|---|---|
| customers:read | Mirror customers locally so appointment bookings can be linked to the correct Lightspeed customer record. Also reads loyalty balances stored on the customer object. |
| customers:write | Create new customers in Lightspeed when a client books for the first time via TimeCinch. Update names, contact details, and TimeCinch-managed tags. |
| products:read | Mirror the product catalog so merchants can map services and back-bar products to Lightspeed SKUs. |
| sales:read | Pull historical sales (last 90 days at connect-time) and listen for sale-status changes (paid, voided, refunded). Used to keep appointments in sync. |
| sales:write | Push appointment sales to the merchant's Lightspeed register so they can be paid through Lightspeed's existing checkout flow. |
| outlets:read | Resolve the correct outlet ID when pushing a sale. Required by the Lightspeed Sales API. |
| registers:read | Resolve the correct register ID when pushing a sale so it appears at the right station. |
| users:read | Attribute pushed sales to the right Lightspeed user (the staff member who performed the service). |
| taxes:read | Apply the correct tax rate ID per sale line. Required by the Lightspeed Sales API. |
| inventory:read | Surface stock counts beside service ↔ product mappings in TimeCinch. |
| gift_cards:read | Look up the current balance of a gift card at appointment-checkout time so the customer can redeem against the sale. |
| gift_cards:write | Apply the gift card as a payment line when the sale is pushed (splits the payment onto the gift_card payment type). |
| webhooks | Register webhook subscriptions on the merchant's Lightspeed account so we receive changes (sales, customers, products, inventory) in real time. |
2. Data we read from Lightspeed and store
We maintain local mirrors of a small set of entities in our Supabase Postgres database. Mirroring is required because Lightspeed's rate limits make it impractical to query every appointment-related screen against Lightspeed's API directly. All data is encrypted at rest using Supabase's standard AES-256 disk encryption.
| Entity | Stored as | Retention |
|---|---|---|
| OAuth connection | lightspeed_connections | Until disconnect; tokens encrypted at rest. |
| Customers | lightspeed_customers | Until disconnect; cascade-deleted on disconnect. |
| Sales (mirrored) | lightspeed_sales | Rolling 90-day window; cascade-deleted on disconnect. |
| Products | lightspeed_products | Until disconnect; cascade-deleted on disconnect. |
| Outlets, registers, users, taxes | lightspeed_outlets / _registers / _users / _taxes | Until disconnect; cascade-deleted on disconnect. |
| Sync log (operational) | lightspeed_sync_log | Rolling 30-day window for troubleshooting; never contains tokens. |
All mirror tables have a foreign key onto lightspeed_connections with ON DELETE CASCADE. When a merchant disconnects, every row tied to their connection is removed in a single transaction.
3. Data we send to Lightspeed
Writes only happen through documented, scoped X-Series API endpoints. We never call undocumented routes or scrape the merchant UI.
- Customer create / update — name, email, phone, and TimeCinch-managed tags (
tc:*prefixed). We do not modify non-tc:tags set by the merchant. - Sale create / update— line items, prices, tax IDs, register ID, user ID, outlet ID, and (optionally) gift-card payment split. Used to ring up appointments through the merchant's normal Lightspeed checkout.
- Inventory adjustments — quantity decrements for back-bar products consumed during an appointment.
- Webhook subscriptions — created at connect time, voided on disconnect.
4. Data we never touch
The following are out of scope for the TimeCinch integration. We never read, store, log, or transmit:
- Payment card data— PAN, CVV, expiry, track data, tokenized cards. All payment-related work happens inside Lightspeed's PCI environment and never transits our systems.
- Bank account / payout / settlement data.
- Lightspeed user passwords, MFA secrets, or session tokens.
- Employee timesheet, payroll, or HR data (we don't request the relevant scopes).
- Suppliers, purchase orders, or supplier pricing(we don't request the relevant scopes).
5. Token handling
- Access and refresh tokens are stored in the
lightspeed_connectionstable. The columns are encrypted at rest using Supabase's built-in column encryption (AES-256-GCM via the platform's managed key). - Refresh tokens are single-use. We rotate them on every refresh, write the new token in the same transaction that consumes the old one, and never log token values. See
refreshAccessTokeninlib/lightspeed/auth.ts. - Access tokens are refreshed eagerly when within 60 seconds of expiry, so we never make a Lightspeed call with a near-expired token.
- Tokens are accessible only to our server-side runtime (Vercel Node.js functions). They are never exposed to the browser or returned in any client-facing response.
6. Webhook signature verification
Lightspeed signs every webhook delivery with an X-Signature header containing an HMAC-SHA256 hash of the raw request body, keyed with our OAuth client secret. Our webhook handler:
- Reads the raw body (no JSON parsing first — required for the HMAC to match).
- Computes the expected HMAC and compares it against the provided signature using
node:crypto.timingSafeEqual. - Returns 401 on any signature failure and does not write to the database.
- Returns 2xxfor valid signatures within Lightspeed's 5-second budget, even when the inner payload is for an unknown event type.
Source: verifyWebhookSignature in lib/lightspeed/webhooks.ts; handler in app/api/lightspeed/webhook/route.ts.
We currently subscribe to the following webhook event types: sale.update, customer.update, product.update, inventory.update.
7. Breach notification
In the event we discover a confirmed or suspected breach involving Lightspeed merchant data, we will:
- Notify affected merchants within 72 hoursof confirmation, by email to the business owner's registered address.
- Notify Lightspeed's partner security contact in the same window.
- Revoke and rotate the affected OAuth credentials, force a token refresh on every merchant, and preserve forensic logs.
- Publish a remediation summary within 30 days.
Security contact: hello@timecinch.com (subject line: “SECURITY” for prioritization).
8. Disconnection & right to be forgotten
Merchants can disconnect TimeCinch from Lightspeed at any time, from either side:
- In TimeCinch: Settings → Integrations → Disconnect. This calls our
/api/lightspeed/disconnectendpoint, which (a) revokes every webhook subscription on the merchant's Lightspeed account, (b) deletes the row inlightspeed_connections, which (c) cascades to remove every mirror row tied to that connection. - In Lightspeed: revoke the TimeCinch app from your account's authorized integrations list. Our next API call will receive a 401, after which we mark the connection disconnected, drop our tokens, and stop writing.
- Full account deletion: if you delete your TimeCinch account, all Lightspeed-linked data is removed as part of the account-deletion cascade. Email hello@timecinch.com to request immediate deletion outside the in-app flow.
9. Infrastructure & subprocessors
- Vercel — application hosting (Node.js serverless functions). Data in transit only; no persistent storage.
- Supabase — Postgres database with AES-256 encryption at rest, TLS in transit, row-level security policies on every table.
- Sentry — error reporting. We scrub tokens and PII from error payloads before they leave our runtime.
10. Verifiable source references
For Lightspeed marketplace reviewers, the canonical source of truth for everything on this page lives in these files:
| Claim | Source |
|---|---|
| OAuth scope set | lib/lightspeed/auth.ts → DEFAULT_SCOPES |
| Single-use refresh rotation | lib/lightspeed/auth.ts → refreshAccessToken |
| Webhook signature verification (HMAC-SHA256) | lib/lightspeed/webhooks.ts → verifyWebhookSignature |
| Webhook event subscription set | lib/lightspeed/webhooks.ts → WEBHOOK_EVENT_TYPES |
| Webhook receiver / 401 on bad signature | app/api/lightspeed/webhook/route.ts |
| Disconnect endpoint (revoke + cascade) | app/api/lightspeed/disconnect/route.ts |
| OAuth handshake | app/api/lightspeed/oauth/start/route.ts + callback/route.ts |
| Health endpoint | app/api/health/lightspeed/route.ts |
The runtime health endpoint at /api/health/lightspeed also returns the current scope set and endpoint paths programmatically.
11. Contact
- Data, privacy, or security: hello@timecinch.com
- Marketplace partnership / business: hello@timecinch.com