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

Building a Marketplace App

Connect your marketplace or OTA to Lockii so operators can publish inventory and you can check availability, calculate hire prices, and create bookings.

Use a marketplace app when your platform lists and sells rental inventory on behalf of Lockii operators. Operators connect your marketplace, choose which products to publish, and you push paid bookings into Lockii after you collect payment from the renter.

If you are building an accounting sync, automation tool, or other integration that is not a sales channel, see Building a Lockii App instead.


Get listed

Marketplace apps are reviewed before launch. Email [email protected] to finish your app review, you can start developing today without needing to contact us.

Include:

  • Your company and marketplace name

  • A short description of the integration

  • Your logo (square, PNG or SVG)

  • One or more HTTPS OAuth redirect URIs

  • Your connect URL (the page on your platform where an operator starts the connection)

  • Your webhook HTTPS endpoint

Once approved, Lockii registers your app and issues:

  • A client_id and client_secret

  • A webhook signing secret

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.


How it works

Connect operator → Discover listings → Check availability → Get price for dates → Take payment → Create booking

Concept Meaning
Connection One Lockii operator who granted your marketplace access. Each connection has its own tokens.
Listing A product the operator published to your marketplace, plus location and a display "from" price.
Booking A rental order in Lockii attributed to your marketplace.

Important behaviours:

  • Availability is not a stock number. It depends on start/end time, location, existing reservations, and buffer times. Always re-check near booking — never cache it as a static quantity.

  • No holds. Availability and pricing endpoints are informational. The binding capacity check happens when you create the booking.

  • Use Lockii pricing for hire totals. Listing pricing_summary.from_amount is for "from $X/day" display only. Once a renter picks dates, call POST /channel/pricing for the catalogue total — do not reimplement pricing tiers on your side.

  • Auto-confirm with pending fallback. If capacity is free, the booking is confirmed. If not (for example another booking landed in between), it is created as pending_approval and the operator decides. Handle both outcomes.

  • Channel-only tokens stay on the Channel API. Connections granted only channel:read and channel:write can call /api/v1/channel/* routes. They cannot call the operator Standard API (bookings, stock, customers, webhooks, or MCP). If you need that data, build a Lockii App with the right operator scopes instead.

  • Instant confirmation emails are skipped. Lockii does not send the operator's instant post-confirm email for bookings created through your marketplace app. You notify the renter on your side. Timed reminders and other automated messages still send from Lockii as configured by the operator.


Base URLs

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

Scopes

Request only what you need:

Scope Grants
channel:read Read published listings, check availability (including calendars), calculate hire prices, read bookings you created
channel:write Create and cancel bookings

Marketplace apps should request only channel scopes. Operator scopes (booking:read, booking:write, inventory:write, reporting:read) are for non-marketplace Lockii apps.


OAuth connection flow

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

1. Authorization request

While the operator is logged in on your platform, redirect them 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=channel:read channel:write
  &state={opaque anti-CSRF value}
  &external_account_id={your seller account ID}
  &external_account_name={display name, optional}

external_account_id is required for marketplace apps. It is your identifier for the seller account starting the connection. Lockii stores it on the connection and echoes it in the API and webhooks.

Operators can also start from Settings → Integrations in Lockii if you provided a connect URL.

2. Consent

The operator signs in to Lockii, picks a company, and approves the scopes.

  • 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 includes access_token, refresh_token, expires_in, scope, and connection_id. Store the token pair against your seller account, keyed by connection_id.

Authorization codes are single-use. If your token exchange fails, send the operator through the authorize flow again instead of retrying the same code.

4. Refresh tokens

  • Access tokens last 60 minutes

  • Refresh tokens last 90 days and rotate on every use — always persist the new refresh token atomically

  • Reusing an already-rotated refresh token revokes the connection's tokens (replay protection)

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".

5. Pause, disconnect, reconnect

  • Operators can pause or disconnect from Lockii at any time

  • Paused connections return 403 with connection_paused

  • Disconnect revokes tokens and sends connection.disconnected — stop showing their listings immediately

  • Running authorize again for the same operator/seller pair reuses the existing connection_id and publications, with fresh tokens

  • You can revoke with POST /api/oauth/revoke (RFC 7009)


Calling the Channel API

Every request uses the connection's access token:

Authorization: Bearer lk_at_xxxxxxxxxxxxxxxx

The token identifies the connection — you never pass a company ID.

Use only the /channel/* paths listed below. If you call other /api/v1 routes with a channel-only token, Lockii returns 403 insufficient_scope and tells you to use /api/v1/channel or reconnect with operator scopes.

Conventions

  • Timestamps: ISO 8601 with offset, e.g. 2026-08-14T09:00:00+10:00. Display times in the listing location's IANA timezone.

  • Money: integer minor units (cents) plus ISO currency, e.g. { "amount": 36000, "currency": "AUD" }.

  • Pagination: cursor-based via next_cursor / ?cursor=.

  • Idempotency: booking creation is idempotent on your external_order.id. Always retry failed creates with the same ID.

Endpoints

Method Path Scope Purpose
GET /channel/connection channel:read Introspect the current connection
GET /channel/listings channel:read List published listings
GET /channel/listings/{id} channel:read Single listing
POST /channel/availability channel:read Check availability for a time window
POST /channel/availability/calendar channel:read Day-by-day availability calendar
POST /channel/pricing channel:read Calculate hire price for a listing + window
POST /channel/bookings channel:write Create a booking (after payment)
GET /channel/bookings/{id} channel:read Get booking state
POST /channel/bookings/{id}/cancel channel:write Cancel a booking

Availability (exact window)

Use when a renter has picked a specific hire start/end:

POST /channel/availability

{
  "listing_ids": ["listing_123"],
  "start_at": "2026-08-14T09:00:00+10:00",
  "end_at": "2026-08-17T09:00:00+10:00",
  "quantity": 1
}

Availability calendar (day grid)

Use this to paint a date picker — do not bulk-poll /channel/availability once per day.

POST /channel/availability/calendar

{
  "listing_ids": ["listing_123"],
  "start_date": "2026-08-01",
  "end_date": "2026-08-31",
  "quantity": 1
}

Example response:

{
  "data": [
    {
      "listing_id": "listing_123",
      "timezone": "Australia/Sydney",
      "days": [
        { "date": "2026-08-01", "available": true, "available_quantity": 3 },
        { "date": "2026-08-02", "available": false, "available_quantity": 0 }
      ]
    }
  ],
  "checked_at": "2026-07-31T05:34:30+10:00"
}
  • start_date / end_date are inclusive YYYY-MM-DD dates in the listing timezone

  • Each day is checked as local midnight → next local midnight (same padding-aware pool check as the window endpoint)

  • Limits: max 20 listings and 92 days per request

  • Still informational — re-check the renter's exact window with /channel/availability before charging; booking creation is the binding capacity check

Calculate hire price

Use listing pricing_summary.from_amount for "from $X/day" on listing pages. Once the renter picks dates, call this endpoint for the Lockii catalogue total (same tiers, day-after rates, and tax as Lockii checkout) — do not reimplement pricing on your side.

POST /channel/pricing

{
  "listing_id": "listing_123",
  "start_at": "2026-08-14T09:00:00+10:00",
  "end_at": "2026-08-17T09:00:00+10:00",
  "quantity": 1
}

Example response:

{
  "listing_id": "listing_123",
  "currency": "AUD",
  "amount": 41580,
  "subtotal": 37800,
  "tax": 3780,
  "quantity": 1,
  "start_at": "2026-08-14T09:00:00.000Z",
  "end_at": "2026-08-17T09:00:00.000Z"
}
  • amount is the total in cents, including tax. Use it as payment.amount when creating the booking (or charge a different amount if your marketplace policy requires it — you are still merchant of record).

  • subtotal / tax are a breakdown for display or invoicing.

  • Optional quantity defaults to 1.

  • Informational only — does not reserve inventory. Re-check availability before charging; booking creation is the binding capacity check.

Create a booking

Call after you have collected payment:

{
  "external_order": {
    "id": "mp_order_89201",
    "number": "MP-89201",
    "url": "https://marketplace.example/orders/89201"
  },
  "lines": [{ "listing_id": "listing_123", "quantity": 1 }],
  "start_at": "2026-08-14T09:00:00+10:00",
  "end_at": "2026-08-17T09:00:00+10:00",
  "customer": {
    "first_name": "Jane",
    "last_name": "Smith",
    "email": "[email protected]",
    "phone": "+61400000000"
  },
  "payment": {
    "status": "paid",
    "amount": 41580,
    "currency": "AUD",
    "external_payment_id": "pay_7832"
  },
  "notes": "Optional notes for the operator."
}

Example response:

{
  "id": "booking_01K4A2...",
  "number": "L-10492",
  "status": "confirmed",
  "external_order_id": "mp_order_89201",
  "confirmation_url": "https://book.lockii.app/{company}/confirmation/private_...",
  "start_at": "2026-08-14T09:00:00+10:00",
  "end_at": "2026-08-17T09:00:00+10:00"
}

Both confirmed and pending_approval responses are HTTP 201. Treat status as the source of truth. For pending_approval, tell the renter their booking is awaiting confirmation and be ready to refund if it is declined.

Optional — send renters to the Lockii confirmation page. The response includes confirmation_url. You can link to it from your post-purchase screen or confirmation email so the customer can open pickup instructions, access codes, and booking details on the operator's Lockii page. This is optional — keep your own confirmation flow if you prefer, and offer the Lockii link as an extra option when it helps the renter complete pickup.

The same confirmation_url is returned on get and cancel responses. Booking date changes are not supported in v1 — cancel and re-book, or send the renter to the operator.


What operators do in Lockii

  1. Connect your marketplace from Settings → Integrations (or from your connect URL).

  2. On each product, use the Channels sidebar to publish that product to your marketplace.

  3. Unpublished products are invisible to you.

  4. Operators can pause or disconnect the connection at any time.

A product available at multiple locations appears as one listing per location.


Webhooks

Lockii POSTs JSON events to your registered webhook URL. Events cover all your connections and include connection_id.

Event When
connection.activated Operator completed OAuth (or reconnected)
connection.disconnected Operator disconnected — remove their listings
listing.published Product published to your marketplace
listing.updated Published product details changed
listing.unpublished Product unpublished — remove it
booking.confirmed Booking confirmed (including after operator approval)
booking.cancelled Booking cancelled or declined

Payloads are thin (ids + timestamps). Re-fetch the resource 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: listing.updated
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.


Recommended flow

Connect

  1. Operator clicks Connect Lockii (or arrives from the Lockii directory).

  2. Complete OAuth; store tokens by connection_id.

  3. Page through GET /channel/listings and import the catalog.

Ongoing

  1. Apply listing.* webhooks by re-fetching the listing.

  2. Re-sync the full listing set periodically (e.g. daily).

  3. Refresh access tokens proactively; persist rotated refresh tokens atomically.

Checkout

  1. POST /channel/availability/calendar to paint the date picker; POST /channel/availability when the renter selects an exact hire window.

  2. POST /channel/pricing with the listing and hire window for the catalogue total (use listing pricing_summary.from_amount only for "from $X/day" display).

  3. Collect payment on your side.

  4. POST /channel/bookings with your external order ID and the amount collected.

  5. Optionally give the renter confirmation_url (button or email link) so they can open the Lockii confirmation page for pickup and access details.

  6. If confirmed — done. If pending_approval — wait for booking.confirmed or booking.cancelled; refund on decline.


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

  • external_account_id sent on every authorize request

  • Token refresh handles rotation atomically

  • Webhook endpoint live with signature verification and event deduplication

  • Full listing sync + webhooks + periodic reconciliation

  • Calendar via /channel/availability/calendar; exact window re-checked via /channel/availability (not cached as static stock)

  • Hire totals via /channel/pricing after dates are picked (not by reimplementing tiers from pricing_summary)

  • Booking retries reuse the same external_order.id

  • Optionally surface confirmation_url to renters after booking

  • pending_approval handled in renter UX

  • connection.disconnected and listing.unpublished remove content promptly

  • Times displayed in the listing location's timezone

  • API calls use only /channel/* routes (not the operator Standard API)


Related

  • Building a Lockii App — non-marketplace apps (accounting, automation, etc.)

  • Lockii REST API — full standard API reference

  • Login with Lockii — user-delegated OAuth (different from App Platform apps)

PrevUser MCP
NextLogin with Lockii
Was this helpful?