Documentation

Everything you need to set up and manage access for your organization.

Getting Started

Get your organization up and running in under 15 minutes:

  1. Sign up — Create an account with your email and a password
  2. Create your organization — Pick a name and URL slug (e.g. north-street-gym)
  3. Add a location — Name it (e.g. "Main Entrance"), set a lock code that matches your physical keypad
  4. Print the QR code — Open the location and go to the QR & signage tab, download PNG/SVG, print and stick on the door
  5. Test it — Scan the QR code with your phone, enter your number, use code 123456 in dev mode

That's it. Visitors can now scan and get access.

Visitor Flow

This is what a visitor experiences when they arrive at your door:

1

Scan QR

Phone camera opens the access page

2

Enter Phone

Any phone number, any country

3

Verify OTP

6-digit code sent via SMS

4

Get Code

Door code displayed, or door unlocks

No app download required. No account needed. Works on any phone with a browser and SMS.

At locations set to "Trust code required", first-time visitors are also asked for a referral code (e.g. TRUST-7X2K4M) before the OTP is sent — see Walk-in Trust & Referrals.

If the visitor's code doesn't work, they can tap "Code didn't work?" to report the issue. You'll get an email and see it in your dashboard.

Dashboard Guide

Your dashboard has these sections:

  • Overview — Today's stats (access attempts, success rate, denials), pending keypad updates, visitor reports
  • Locations — Add/edit/delete access points. Each location has its own QR code, lock code, and settings
  • Members — Manage who has recognized access. Add individually or import via CSV
  • Integrations — API keys, outbound webhooks
  • Access log — Full log with filter chips, a live view, location and date-range filters, and CSV export
  • Billing — Current plan, usage, and upgrade options
  • Settings — Org name/slug, help & support, danger zone

Locations & QR Codes

A location represents a physical door or access point. Each location has:

  • Name — e.g. "Main Entrance", "Side Door"
  • Slug — URL-safe name used in the QR link: /access/your-org/main-entrance
  • Lock Code — The code visitors receive. Must match your physical keypad.
  • Access Mode — Open (anyone), Members Only, or Hybrid
  • Walk-in Trust — how first-time visitors are treated (see Walk-in Trust & Referrals)
  • Lock Tier — Keypad Code, Smart Lock (Seam), or Custom Webhook

QR Codes

Each location gets a unique QR code. Open the location and go to the QR & signage tab to:

  • Download as PNG or SVG
  • Copy the access URL
  • Print and stick on or near the door

The QR encodes: https://yourapp.com/access/your-org-slug/location-slug

Members

Members are people your organization recognizes. They matter when a location is set to Members Only or Hybrid mode.

Adding Members

  • Individual — Click "+ Add member" to open the side panel, then enter phone (required), name and email (optional)
  • CSV Import — Upload a CSV with columns: phone, name, email. The phone column is required.

Access Modes

  • Open — Anyone can access. Members are tracked but not required.
  • Members Only — Only phones in the members list can get a code. Others are denied.
  • Hybrid — Anyone can access, but member status is logged for reporting.

Walk-in Trust & Referrals

Beyond the member list, each location can gate walk-ins(visitors who aren't members) with a trust requirement. (For a scenario-by-scenario overview of every option, see Access Options.) Set it in the location's Rules tab, under Walk-in trust:

  • Everyone — any phone-verified visitor can request access (default)
  • Returning visitors — walk-ins need at least one prior successful visit at a location in your community context. First-timers are denied.
  • Trust code required — first-timers must enter a trust code your organization issued. Returning trusted visitors get in without one.
  • Ask me each time — every walk-in waits at the door while you approve or deny live, from a card on your dashboard Overview (you also get an email). Visitors verify their phone first, and requests expire after 5 minutes. Approved first-timers become recognized returning visitors.

Members always get in (on by default) lets active members skip the walk-in gate entirely. Turn it off to send members through the same trust check as everyone else.

How trust is earned

Every successful visit records trust for that phone number — scoped to your community context(community spaces, fitness, storage, residential). Trust earned at community spaces never grants access at gyms, and vice versa — by design, with no override. Trust goes dormant after 12 months of inactivity in a context and can't be revived just by showing up (protects against recycled phone numbers).

Trust Codes

Issue codes from the Trust codes card on the Members page. Share a code (e.g. TRUST-7X2K4M) to vouch for someone:

  • The visitor enters it on the access page at a "Trust code required" location
  • After they verify by OTP, they're trusted in your community context going forward
  • Every use is recorded against your organization and you get an email each time your code is used
  • Disable a code anytime — existing trust it granted stays, but it can't admit anyone new
  • If someone you vouched for is later blocked by 3+ organizations, you'll get a private email suggesting you review the code. Nothing is automatic — the decision stays yours.

Blocked Numbers

Block a number from the Blocked numberscard on the Members page. Blocked numbers are denied at every location, at every trust level, even if they're members. The visitor sees only "Access restricted" — their block status is never disclosed. Only the last 4 digits are stored for display; the full number is never kept.

Denials show in the Events log as Denied (Blocked) and Denied (First Visit)so you can see exactly why someone didn't get in.

Time-Bound Access

Give members temporary access with automatic start and end dates. No manual cleanup needed.

Use Cases

  • Visiting members — "Ahmed is visiting for Ramadan (March 1-30)"
  • Trial passes — "Sarah has a 30-day gym pass"
  • Seasonal access — "Summer volunteers have access June-August"
  • Short-term rentals — "Guest checks in Friday, out Sunday"

How It Works

When adding or editing a member, set optional date fields:

  • Access From — when their access starts. Before this date, they see "Your access begins on [date]"
  • Access Until — when their access expires. After this date, they see "Your access expired on [date]"
  • Note — admin label for context (e.g. "Ramadan visitor", "30-day pass")

Leave both dates blank for permanent access (default behavior). The system automatically enforces the window — no manual deactivation needed.

Member Status Badges

  • Active — permanent or within their access window
  • Temporary — has an access window set
  • Upcoming — access hasn't started yet
  • Expired — access window has passed

Time Gating

Restrict access to specific hours and days. When enabled, visitors outside the window see "Access is not available at this time."

Setting Up

  1. Open the location's Rules tab and turn on "Limit to these windows"
  2. Click "+ Add a window" to add an access window
  3. Set open/close times and select active days
  4. Add multiple windows if needed (e.g. morning + evening)

Lock Codes & Rotation

The lock code is what visitors receive after verification. It must match the code on your physical keypad.

Setting a Code

In the location's Door code tab you can type any code directly or generate a new one.

Code Generation Rules

Configure how random codes are generated:

  • Length — 1 to 20 characters
  • Character set — Digits only, letters only, or both
  • Prefix/Suffix — Code always starts or ends with specific characters

Rotation Policy

  • Manual — You rotate when you want
  • Daily — Auto-rotates at midnight
  • Weekly — Auto-rotates every Monday
  • Monthly — Auto-rotates on the 1st
  • Custom — Set your own interval

Important: When the code rotates (manually or automatically), you must also update your physical keypad to match. You'll get a reminder on your dashboard until you confirm.

Smart Locks

Three unlock methods are available:

Keypad Code (default)

Visitor receives the code in the app. They type it into your physical keypad.

Smart Lock (Seam)

The door unlocks automatically via the Seam API. Supports 50+ lock brands (August, Schlage, Yale, etc.).

  1. Get a Seam API key at seam.co
  2. Add your SEAM_API_KEY to the API environment
  3. In the location's Lock tab, select "Smart Lock (Seam)"
  4. Enter your device ID

Fail-safe: If the smart lock fails, the visitor automatically receives the keypad code instead.

Custom Webhook

Send a POST request to your own endpoint when access is granted.

  1. In the location's Lock tab, select "Custom Webhook"
  2. Enter your endpoint URL
  3. Click "Test" to verify connectivity

The payload is HMAC-signed with that door's signing secret (Lock tab → Signing secret). Verify X-QRGate-Signature before opening anything — otherwise anyone who learns the URL could open the door. Fail-safe: If the webhook fails, the visitor receives the keypad code.

White-Label Branding

Customize how the visitor access page looks for your organization. Available on GROWTH plan and above.

Customizable Fields

  • Display Name — shown on the visitor page instead of your org name (e.g. "North Street Gym")
  • Logo — square image URL, displayed above the title
  • Primary Color — hex color code for buttons and accents (e.g. #16a34a for green)
  • Welcome Text — custom message below the title (e.g. "Please verify your phone to receive the door code.")

Setup

  1. Go to Dashboard → Settings → Visitor page
  2. Fill in any fields you want to customize (all are optional)
  3. Click "Save"
  4. Visit your access page to see the changes

All branding is optional. Without it, visitors see the standard QR Gate look.

API Keys

API keys let your external software interact with your QR Gate data. Requires GROWTH plan or above.

Creating a Key

  1. Go to Dashboard → Integrations and find the API keys section
  2. Click "Create Key", enter a label
  3. Copy the key immediately — it's shown only once

Using the Key

Include it in the X-API-Key header:

curl -H "X-API-Key: lmi_your_key_here" \
  https://api.yourapp.com/api/v1/orgs/:orgId/members

Revoking

Click "Revoke" next to any key. It stops working immediately. This cannot be undone.

Outbound Webhooks

Receive real-time HTTP notifications when events happen in your organization.

Setting Up

  1. Go to Integrations and click "+ Add webhook" in the Webhooks section
  2. Enter your endpoint URL
  3. Select which events to subscribe to
  4. Copy the signing secret — shown only once

Available Events

  • access.granted — A visitor was granted access
  • access.denied — A visitor was denied access (with the reason in result)
  • code.rotated — A lock code was changed (the new code is never sent)

Every payload includes a plain-English text line, so a Slack incoming-webhook URL works as-is.

Payload Format

POST https://your-endpoint.com/webhook
Header: X-QRGate-Signature: sha256=<hmac>
Header: X-QRGate-Delivery: <delivery id — the same on every retry>
Content-Type: application/json

{
  "event": "access.denied",
  "orgId": "clx...",
  "locationId": "clx...",
  "locationName": "Main Entrance",
  "phonePartial": "**7823",
  "result": "DENIED_NOT_MEMBER",
  "timestamp": "2026-03-24T14:32:00Z",
  "text": "Access denied at Main Entrance · **7823 · not on the members list"
}

Retries

Reply with any 2xxwithin 10 seconds. Anything else (including redirects, which aren't followed) is retried after 1 min, 5 min, 30 min, 2 h, 6 h and 12 h — about a day in all. Use X-QRGate-Deliveryto ignore a delivery you've already processed. Integrations shows the latest delivery result for each webhook.

Verifying Signatures

Verify the X-QRGate-Signature header against the raw request body (not re-serialized JSON) using your signing secret:

const crypto = require('crypto');

// rawBody: the exact bytes received, e.g. express.raw({ type: 'application/json' })
const expected = 'sha256=' + crypto
  .createHmac('sha256', YOUR_WEBHOOK_SECRET)
  .update(rawBody)
  .digest('hex');
const received = req.headers['x-qrgate-signature'] ?? '';

const valid = received.length === expected.length &&
  crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!valid) return res.status(401).send('Invalid signature');

API Reference

All API routes are prefixed with /api/v1/. Authenticate with X-API-Key header.

Members

GET    /api/v1/orgs/:orgId/members          # List all members
POST   /api/v1/orgs/:orgId/members          # Add a member { phone, name?, email? }
POST   /api/v1/orgs/:orgId/members/bulk     # CSV import { members: [...] }
PATCH  /api/v1/members/:id/toggle           # Toggle active/inactive
DELETE /api/v1/orgs/:orgId/members/:id       # Remove member

Trust Codes & Blocklist

GET    /api/v1/orgs/:orgId/trust-codes            # List codes with usage counts
POST   /api/v1/orgs/:orgId/trust-codes            # Issue a code { label? }
PATCH  /api/v1/orgs/:orgId/trust-codes/:id/toggle # Enable/disable

GET    /api/v1/orgs/:orgId/blocklist              # List blocked numbers (last 4 only)
POST   /api/v1/orgs/:orgId/blocklist              # Block { phone }
DELETE /api/v1/orgs/:orgId/blocklist/:id          # Unblock

Locations

GET    /api/v1/orgs/:orgId/locations         # List locations
POST   /api/v1/orgs/:orgId/locations         # Create { name, slug, lockCode?, ... }
GET    /api/v1/locations/:id                  # Get location details
PATCH  /api/v1/locations/:id                  # Update location
DELETE /api/v1/locations/:id                  # Delete location
POST   /api/v1/locations/:id/rotate-code     # Rotate lock code { code?, rules? }

Access Events

GET    /api/v1/orgs/:orgId/events
  ?locationId=   # Filter by location
  &result=       # GRANTED, DENIED_OTP, DENIED_HOURS, etc.
  &from=         # ISO date start
  &to=           # ISO date end
  &page=         # Page number
  &limit=        # Items per page (max 100)

Webhooks

GET    /api/v1/orgs/:orgId/webhooks          # List webhooks
POST   /api/v1/orgs/:orgId/webhooks          # Create { url, events[] }
PATCH  /api/v1/webhooks/:id/toggle           # Pause/resume
DELETE /api/v1/orgs/:orgId/webhooks/:id       # Delete

Billing & Plans

PlanPriceLocationsEvents/moSmart LockAPI
FREE$0150——
STARTER$12/mo3500✓—
GROWTH$29/mo102,000✓✓
PRO$79/mo∞∞✓✓
ENTERPRISECustom∞∞✓✓

Upgrade anytime from Dashboard → Billing. Downgrades take effect at the end of your billing period.

Security

  • Phone numbers are never stored raw in access logs — only SHA-256 hashes and last 4 digits for display
  • Lock codes encrypted at rest — AES-256-GCM encryption, decrypted only when shown to visitors
  • API keys are hashed before storage — SHA-256, raw key shown once on creation
  • Webhook secrets are shown once — on creation only; we keep them to sign deliveries
  • All outbound webhooks are HMAC-signed — verify the X-QRGate-Signature header
  • Session tokens are single-use — 10-minute expiry JWTs, invalidated after first verification
  • CAPTCHA protection — Cloudflare Turnstile on the visitor phone form prevents bot abuse
  • Rate limiting — 3 OTP requests per phone per hour, 60 API requests per minute per IP
  • Stripe webhook verification — signature check prevents forged billing events
  • Checkout rate limiting — 3 attempts per org per hour prevents card testing
  • Row-Level Security — PostgreSQL RLS policies prevent cross-org data access
  • Security headers — CSP, HSTS, X-Frame-Options, helmet on API
  • Cloudflare protection — DDoS mitigation, bot detection, WAF at the edge
  • Admin passwords are bcrypt-hashed — sessions are short-lived signed cookies, never stored server-side
  • Fail-safe design — if smart lock or webhook fails, visitors get the keypad code instead

Help & Support

Get help directly from the dashboard. Go to Settings → Help & Support to find:

  • Report a Bug — select a category (visitor flow, dashboard, lock codes, members, QR codes), describe the issue, and optionally add reproduction steps. We receive an email immediately.
  • Request a Feature — tell us what problem it would solve and how you'd like it to work. Feature requests help us prioritize development.
  • Contact Support — for questions, account issues, or anything else. Include your email and we'll respond as soon as possible.

All submissions are tracked in our internal system. We review every ticket.

Visitor Feedback

When a visitor reports an issue (e.g. "Code didn't work"), you'll:

  • Receive an email notification immediately
  • See it on your Dashboard overview as a red banner
  • Find the reports in the location's Door code tab
  • Resolve individual reports with one click

FAQ

Do visitors need to download an app?

No. The entire flow works in the browser. Scan QR → enter phone → get code.

What if a visitor's code doesn't work?

They can tap 'Code didn't work?' on the access granted screen. You'll get an email and see the report in your dashboard.

What happens if the smart lock fails?

The system automatically falls back to sending the keypad code via the app. Fail-safe by design.

Can I use this without a smart lock?

Yes. The default mode is Keypad Code — visitors get the code, type it into your physical keypad.

How do I restrict access to members only?

Set the location's Access Mode to 'Members Only'. Only phones in your members list will be allowed through.

Can I set different access hours for different days?

Yes. Time gating supports multiple windows with per-day selection.

Is my data shared between organizations?

No. Every query is scoped to your organization. Database-level Row-Level Security prevents cross-org access.

Can I export my data?

Yes. Members and access events can be exported as CSV from the dashboard.

Can I give temporary access to visitors?

Yes. Set 'Access From' and 'Access Until' dates on a member. Their access is automatically enforced — no manual deactivation needed.

How do I customize the visitor page with my branding?

Go to Settings → Visitor page (GROWTH plan and above). Add your logo, primary color, display name, and welcome text.

How do I report a problem?

Go to Dashboard → Settings → Help & Support. You can report bugs, request features, or contact support directly.

Need help? Go to Dashboard → Settings → Help & Support.