Skip to main content

Authentication

KISS uses two authentication methods depending on your integration type:

IntegrationAuth methodWho authenticates
PMS PushAPI token (Bearer)Your server
White-Label AppPhone + OTPYour app's end user (tenant)

API Tokens (PMS Integrators)

API tokens authenticate server-to-server requests from your PMS to KISS. Each token is scoped to a single company.

Generate a token

  1. Log in to the KISS Dashboard
  2. Navigate to your Company page from the sidebar
  3. Click the API tab
  4. Enter a name for your token (e.g., production-sync, staging)
  5. Click Create a new token
  6. Copy the token immediately — it won't be shown again
caution

API tokens grant full access to your company's data. Store them securely and never expose them in client-side code, public repos, or logs.

Use the token

Include the token in the Authorization header of every request:

# Generate one key per logical operation. Reuse the same value when retrying.
IDEMPOTENCY_KEY=$(uuidgen)

curl -X PATCH https://api.keepitsimplestorage.com/api/v2/units \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-d '{"units": [...]}'
Idempotency-Key

Every PMS write endpoint accepts an optional Idempotency-Key header. The value is any client-chosen opaque string (max 255 characters) — UUIDs are a common choice but any unique identifier within your namespace works. When present, the server stores the request hash + response for 24 hours — retrying the same key with the same payload returns the cached response without a second write. Retrying the same key with a different payload returns 409 Conflict. Use a new unique value per logical operation; reuse the same value when retrying that operation.

Manage tokens

You can view all active tokens in the API tab of your Company page. Each token shows its name and creation date. To revoke a token, click the delete icon next to it — this takes effect immediately.

You can create multiple tokens (e.g., separate tokens for production and staging environments).

Error responses

StatusMeaning
401 UnauthorizedMissing or invalid token
{
"message": "Unauthorized."
}

Tenant OTP Flow (White-Label Apps)

White-label app integrators authenticate tenants using a phone number + OTP (one-time password) flow. The tenant receives an SMS, verifies the code, and your app receives a Bearer token.

White-label V2 surface in design

The V2 endpoints for the tenant OTP flow and /tenant/access are still being scoped under the V2: Tenant & Mobile Access initiative. Today's production white-label clients use the predecessor (unversioned) endpoints — see the White-Label Quickstart for the current paths. The shape below describes the v2 contract this surface is moving to; treat the URL prefixes as illustrative until V2 white-label ships.

How it works

  1. App calls POST /auth/phone with tenant's phone number
  2. Tenant receives a 6-digit OTP via SMS
  3. App calls POST /auth/verify-otp with the OTP
  4. API returns a Bearer token
  5. App uses the token for all subsequent requests

Step 1: Request OTP

curl -X POST https://api.keepitsimplestorage.com/api/v2/auth/phone \
-H "Content-Type: application/json" \
-d '{
"country_code": "1",
"phone_number": "5551234567"
}'
FieldTypeRequiredDescription
country_codestringYesCountry calling code without + (1-3 digits)
phone_numberstringYesPhone number without country code (7-15 digits)

Success — 200 OK:

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

Error — 422: The phone number is not associated with any tenant in KISS.

{
"message": "A tenant with the submitted phone number does not exist."
}
tip

OTPs expire after 5 minutes. A new OTP cannot be requested until the resend cooldown (30 seconds) has passed.

Step 2: Verify OTP

curl -X POST https://api.keepitsimplestorage.com/api/v2/auth/verify-otp \
-H "Content-Type: application/json" \
-d '{
"country_code": "1",
"phone_number": "5551234567",
"otp": "482910"
}'
FieldTypeRequiredDescription
country_codestringYesCountry calling code without +
phone_numberstringYesPhone number
otpstringYes6-digit code received via SMS
tenant_idstringNoRequired on second call when multiple accounts exist

Success — 200 OK (single account):

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

Handling multiple accounts

If a phone number is linked to multiple tenant accounts (e.g., a tenant renting at two locations), the API returns token: null and a list of accounts:

{
"message": "Multiple accounts found.",
"data": {
"token": null,
"accounts": [
{
"tenant_id": "01HQA123456789ABCDEFGHJKMNPQRS",
"name": "Jane Smith",
"location": "Downtown Storage",
"company": "ABC Storage Co."
},
{
"tenant_id": "01HQB234567890BCDEFGHJKMNPQRST",
"name": "Jane Smith",
"location": "Uptown Storage",
"company": "ABC Storage Co."
}
]
}
}

Prompt the tenant to select an account, then call the same endpoint again with the selected tenant_id:

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

This returns the standard success response with a token.

note

The OTP is not consumed on the first call when multiple accounts are returned — it stays valid so your app can call again with the selected tenant_id.

Step 3: Use the token

Include the Bearer token in all subsequent API requests:

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

Token expiration

Tenant Bearer tokens have a limited lifetime. When a token expires, the API returns 401 Unauthorized. There is no refresh token mechanism. The tenant must re-authenticate via the OTP flow to get a new token.

tip

Cache the token for the duration of the session and handle 401 responses by redirecting the tenant back to the OTP login screen.

Error responses

StatusMeaning
401 UnauthorizedMissing, invalid, or expired token
403 ForbiddenToken is valid but lacks permission for this resource (e.g., accessing a unit that doesn't belong to this tenant)
422 UnprocessableInvalid or expired OTP
429 Too Many RequestsRate limited. Retry after 60 seconds.
{
"message": "The OTP code is invalid or has expired."
}

Rate Limits

White-Label Auth

EndpointLimitWindowNotes
POST /auth/phone5 requests60 secondsPer phone number
POST /auth/verify-otp5 attempts60 secondsPer phone number

PMS Push

Endpoint groupLimitWindowNotes
PATCH /units (bulk)TBD — see note below60 secondsPer company
PUT/DELETE /units/{crm_unit_id}/tenancy, PATCH /units/{crm_unit_id}TBD — see note below60 secondsPer company
Rate limits for PMS push endpoints are not yet finalized

Final numbers are a product decision in progress. Partners should design for retries and treat 429 Too Many Requests with a Retry-After header as a recoverable state. Exponential backoff with jitter is the expected client behavior.

When rate limited, the API returns 429 Too Many Requests with a Retry-After header:

{
"message": "Too many attempts. Please try again in 60 seconds."
}

Best Practices

  • Store tokens securely. API tokens belong in environment variables or a secrets manager, never in source code.
  • Use separate tokens per environment. Create distinct tokens for production, staging, and development.
  • Cache the tenant Bearer token. After OTP verification, cache the token in the app for the session. Don't re-authenticate on every API call.
  • Handle 401 gracefully. If a request returns 401, the token has expired or been revoked. Prompt the tenant to re-authenticate via OTP.
  • Never send tokens to your backend. Tenant Bearer tokens should stay on the device. Your server should use API tokens for server-side operations.