Skip to main content

Quickstart: White-Label App Integration

This guide walks you from zero to your first successful API call. By the end, you'll have authenticated a tenant, retrieved their unit access, and reported a lock event — the core loop of a white-label app integration.

Time to complete: ~15 minutes

Endpoints shown here are the production surface today

The KISS API is consolidating onto a /api/v2/ surface, but the white-label endpoints are still scoped under V2: Tenant & Mobile Access (in design). Until that ships, white-label clients use the predecessor endpoints below. URL paths will be republished alongside the V2 white-label work; the request and response shapes will remain functionally similar.

Prerequisites

  • A tenant phone number registered in KISS (ask your KISS contact for a test tenant)
  • A tool to make HTTP requests (curl, Postman, or your app)

Base URL

https://api.keepitsimplestorage.com/api/v1

Step 1: Authenticate a tenant

Tenants authenticate with their phone number via OTP. Send the phone number to request a code:

curl -X POST https://api.keepitsimplestorage.com/api/v1/auth/phone \
-H "Content-Type: application/json" \
-d '{
"country_code": "1",
"phone_number": "5551234567"
}'

Response:

{
"message": "OTP sent successfully."
}

The tenant receives a 6-digit code via SMS. Verify it:

curl -X POST https://api.keepitsimplestorage.com/api/v1/auth/verify-otp \
-H "Content-Type: application/json" \
-d '{
"country_code": "1",
"phone_number": "5551234567",
"otp": "482910"
}'

Response:

{
"message": "Login successful.",
"data": {
"token": "1|abc123def456...",
"user": {
"id": "usr_abc123",
"name": "Jane Smith",
"email": null,
"phone": "+15551234567"
}
}
}

Save the token — you'll use it for all subsequent requests.

tip

If the phone number is linked to multiple accounts, the API returns a list of accounts instead of a token. See the Authentication guide for how to handle this.


Step 2: Retrieve tenant access

This is the core endpoint. It returns everything your app needs in a single call: the tenant's units, their access status, lock keys, and entry points.

curl https://api.keepitsimplestorage.com/api/v1/tenant/access \
-H "Authorization: Bearer 1|abc123def456..."

Response:

{
"message": "Request successful.",
"data": {
"tenant_id": "01HQ0123456789ABCDEFGHJKMNPQRS",
"synced_at": "2026-02-13T14:30:00Z",
"units": [
{
"unit_id": "01HQ1234567890ABCDEFGHJKMNPQRS",
"unit_name": "B204",
"location_id": "01HQ9876543210ZYXWVUTSRQPONML",
"grant_type": "tenant_primary",
"state": "tenant_permitted",
"reason": "active",
"access": true,
"has_unit_lock": true,
"has_entry_point": true
},
{
"unit_id": "01HQ2345678901BCDEFGHJKMNPQRST",
"unit_name": "C101",
"location_id": "01HQ9876543210ZYXWVUTSRQPONML",
"grant_type": "tenant_primary",
"state": "tenant_denied",
"reason": "delinquent",
"access": false,
"has_unit_lock": true,
"has_entry_point": true,
"balance_due": 150.00,
"paid_through_date": "2026-01-10"
},
{
"unit_id": "01HQ3456789012CDEFGHJKMNPQRSTU",
"unit_name": "A305",
"location_id": "01HQ8765432109YXWVUTSRQPONMLK",
"grant_type": "tenant_shared",
"state": "tenant_permitted",
"reason": "active",
"access": true,
"has_unit_lock": false,
"has_entry_point": true
}
],
"entry_points": [
{
"entry_point_id": "01HQ4567890123DEFGHJKMNPQRSTUV",
"name": "Main Gate",
"zone_id": "01HQ5678901234EFGHJKMNPQRSTUVW",
"location_id": "01HQ9876543210ZYXWVUTSRQPONML",
"would_have_access": true,
"access": false,
"denied_reason": "tenant_denied_in_zone"
},
{
"entry_point_id": "01HQ6789012345FGHJKMNPQRSTUVWX",
"name": "Building B Door",
"zone_id": "01HQ7890123456GHJKMNPQRSTUVWXY",
"location_id": "01HQ9876543210ZYXWVUTSRQPONML",
"would_have_access": true,
"access": true
}
]
}
}

Key fields to use in your app:

Units:

FieldWhat it means
accessThe final access decision. true = tenant can unlock.
stateThe unit's access state: tenant_permitted or tenant_denied
reasonWhy — e.g., active, delinquent, pms_lockout, future_move_in, blanket_delinquency
grant_typetenant_primary (direct lease) or tenant_shared (shared access from another tenant)
has_unit_lockWhether this unit has a ONELock device
has_entry_pointWhether this unit's zone has gates/doors
balance_dueAmount owed. Only present when denied for financial reasons.
paid_through_dateLast paid date. Only present when denied for financial reasons.

Entry points:

FieldWhat it means
accessWhether the tenant can open this gate/door.
would_have_accessWhether the tenant would have access if all their units in this zone were permitted.
denied_reasonWhy access was denied (e.g., tenant_denied_in_zone). Only present when access is false.
note

Call this endpoint on app launch and on pull-to-refresh. Cache the response for offline access.


Step 3: Report lock activity

After every NFC tap attempt (successful or failed), report it back to KISS:

curl -X POST https://api.keepitsimplestorage.com/api/v1/locks/LOCK_ID/logs \
-H "Authorization: Bearer 1|abc123def456..." \
-H "Content-Type: application/json" \
-d '{
"key": "lock.open_successful"
}'

Response:

{
"message": "Log created successfully.",
"data": {
"id": "log_xyz789",
"key": "lock.open_successful",
"happened_at": "2026-02-13T14:35:22Z",
"created_at": "2026-02-13T14:35:22Z"
}
}

Available log event keys:

KeyWhen to use
lock.open_successfulLock opened successfully
lock.open_failureLock failed to open
lock.open_blockedLock open was blocked (access denied)
lock.close_successfulLock closed successfully
lock.close_successful_confirmedLock close confirmed
lock.close_failureLock failed to close (include reason field)
lock.close_blockedLock close was blocked

Entry points (gates, doors) work the same way — use POST /entry-points/{entry_point_id}/logs with entry_point.* event keys.


What's next

  • Authentication guide — token expiration, rate limits, best practices
  • Concepts — understand units, tenants, access states, and the KISS data model
  • Error handling — standard error format and troubleshooting
  • Flutter SDK setup — NFC lock interaction layer (coming soon)