Lockii
Lockii
Docs
  • Docs
  • Changelog
  • Feature requests
  • Support portal
    • Building a Marketplace App
    • User MCP
    • Login with Lockii
    • Building a Lockii App
    • Customer MCP
    • Lockii REST API
    • Booking API

Building a Lockii App

Register a third-party app so operators can connect it from Integrations and grant scoped offline access to the Lockii API.

Use a Lockii app when you want operators to connect your product from Settings → Integrations and grant your servers offline access to their company data. Good fits include accounting syncs, reporting tools, messaging add-ons, and operations automation.

If you are building a marketplace or OTA that lists inventory and creates paid bookings as merchant of record, see Building a Marketplace App instead.


Apps vs API keys vs Login with Lockii

Method Best for Access
Lockii app (this guide) Third-party products installed by operators Company-scoped offline tokens with the scopes the operator approved
API key Server-to-server scripts and Zapier Company-level key created by the operator
Login with Lockii Tools acting as a signed-in user (including MCP) That user's role and permissions

App tokens are just another way to authenticate to the standard Lockii REST API. There is no separate API surface for apps — your granted scopes are ordinary Lockii permissions.


Get listed

Apps are curated. Email [email protected] to request permission to have your app listed in the Lockii directory.

Include:

  • Your company and app name
  • A short description (shown to operators on the consent screen and in Integrations)
  • Your logo (square, PNG or SVG)
  • Category (payments, messaging, accounting, analytics, automation, or other)
  • The scopes you need (see below)
  • One or more HTTPS OAuth redirect URIs
  • Optional connect URL (your page where an operator starts the install)
  • Optional webhook HTTPS endpoint

Once approved, Lockii registers your app and issues:

  • A client_id and client_secret
  • A webhook signing secret (if you registered a webhook URL)

Treat the client_secret like a password. Store it in a secrets manager — never in client-side code or version control. Rotation is available on request.


Scopes

Scopes are Lockii permission strings. Only scopes approved for your app at registration can be requested. Operators see plain-language descriptions on the consent screen.

Scope Grants
booking:read View bookings, schedule, customers, and products
booking:write Create and update bookings
inventory:write Manage products, stock, and inventory details
reporting:read Read reporting data and dashboards

Request the minimum set your product needs. Marketplace channel scopes (channel:read / channel:write) are for sales-channel apps only — see the marketplace guide.

Do not send external_account_id on authorize unless you are requesting channel scopes.


Base URLs

Purpose URL
API https://dash.lockii.app/api/v1
OAuth https://dash.lockii.app/api/oauth

OAuth install flow

Lockii uses the OAuth 2.0 authorization code grant with refresh tokens. Each successful grant creates a connection (installation) for one operator company. PKCE (S256) is supported and recommended.

1. Authorization request

Redirect the operator to:

https://dash.lockii.app/api/oauth/authorize
  ?client_id={your client_id}
  &redirect_uri={one of your registered redirect URIs}
  &response_type=code
  &scope=booking:read reporting:read
  &state={opaque anti-CSRF value}

Replace scope with the space-separated scopes approved for your app.

If you provided a connect URL, operators can also start from Settings → Integrations in Lockii (the Connect button links to your URL). Otherwise Lockii can start the authorize URL directly.

2. Consent

The operator signs in to Lockii, picks which company to connect, and approves the scopes. Only users who can manage integrations for that company can approve.

  • Approved → redirect to your redirect_uri with ?code=...&state=... (code expires in 10 minutes, single-use)
  • Declined → ?error=access_denied&state=...

Always verify state before continuing.

3. Token exchange

POST /api/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code={code}
&client_id={client_id}
&client_secret={client_secret}
&redirect_uri={same redirect_uri as authorize}

Response:

{
  "access_token": "lk_at_...",
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "lk_rt_...",
  "scope": "booking:read reporting:read",
  "connection_id": "conn_01K3ZQ..."
}

Store the token pair keyed by connection_id (and your own customer/account id).

4. Refresh tokens

  • Access tokens last 60 minutes
  • Refresh tokens last 90 days and rotate on every use — always persist the newest refresh token atomically
  • Reusing an already-rotated refresh token revokes that connection's tokens
POST /api/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token={current refresh token}
&client_id={client_id}
&client_secret={client_secret}

Refresh a few minutes before expiry, or on a 401 with error.code: "token_expired". If the refresh token expired or the operator disconnected, they must authorize again.

5. Pause, disconnect, reconnect

  • Operators can pause or disconnect from Settings → Integrations
  • Paused connections return 403 with connection_paused
  • Disconnect revokes tokens and sends connection.disconnected
  • Re-running authorize for the same app + company reuses the existing connection_id and issues fresh tokens
  • You can revoke with POST /api/oauth/revoke (RFC 7009: token + client credentials)

Calling the REST API

Send the connection's access token on every request:

GET /api/v1/booking?status=active&limit=10 HTTP/1.1
Host: dash.lockii.app
Authorization: Bearer lk_at_xxxxxxxxxxxxxxxx

The token is scoped to one company. You do not pass a company ID. Handlers enforce the granted scopes the same way they enforce user permissions.

Status Code Meaning
401 token_expired Refresh and retry
401 token_invalid Token revoked or unknown — re-authorize
403 insufficient_scope Connection lacks the required scope
403 connection_paused Operator paused the connection

For endpoint details, see the Lockii REST API.


Webhooks

If you registered a webhook URL, Lockii POSTs JSON events for your connections.

Event When
connection.activated Operator completed OAuth (or reconnected)
connection.disconnected Operator disconnected — stop using their tokens

Marketplace listing and booking events apply only to apps with channel scopes.

Payloads are thin (ids + timestamps). Re-fetch canonical state via the API. Respond with 2xx within 10 seconds. Delivery order is not guaranteed; deduplicate on event id.

Signature verification

Each delivery includes:

Lockii-Event-Id: evt_...
Lockii-Event-Type: connection.activated
Lockii-Signature: t={unix},v1={hmac_hex}

The signature is HMAC-SHA256 of {t}.{raw_request_body} using your webhook signing secret. Reject deliveries older than 5 minutes. Compute the HMAC over the raw body bytes before JSON parsing.

Example:

import { createHmac, timingSafeEqual } from "node:crypto";

function verifyLockiiSignature(rawBody: string, header: string, secret: string): boolean {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const age = Math.abs(Date.now() / 1000 - Number(parts.t));
  if (age > 300) return false;
  const expected = createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");
  return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

What operators see

  1. Your app appears under Settings → Integrations in its category section once approved.
  2. They click Connect, approve scopes for a company, and your app receives tokens.
  3. They can pause, resume, or disconnect the connection at any time.
  4. Granted scopes are visible on the connection detail.

Go-live checklist

  • Listed after approval from [email protected]
  • client_secret and tokens stored securely; tokens keyed by connection_id
  • OAuth state verified; PKCE implemented
  • Only approved scopes requested; no external_account_id unless you are a marketplace app
  • Token refresh handles rotation atomically; 401 token_expired triggers refresh-and-retry
  • Webhook endpoint live (if used) with signature verification and event deduplication
  • connection.disconnected stops API use for that company promptly
  • API calls use only the scopes you requested and handle insufficient_scope / connection_paused

Related

  • Building a Marketplace App — sales channels and OTAs
  • Lockii REST API — endpoint reference
  • Login with Lockii — user-delegated OAuth for REST and MCP
PrevLogin with Lockii
NextCustomer MCP
Was this helpful?