Skip to content

People in a home: levels, invitations, a guest's stay, the booking API

ALYT lets a household owner add people to a specific home, set their access level, and choose whether the person is a permanent resident or a temporary guest with a checkout date. Invitations are email-based, accepted in the app or in a browser, and they are also the entry point for booking-platform flows keyed by reservation. Access can be conditioned on the home’s network, and that condition is evaluated at sign-in and refresh so every service sees the same result.

The API uses the enum values OWNER, MEMBER, and GUEST. The product words are Owner, Resident, and Guest.

EnumWordWhoMay
OWNEROwnerthe household’s adults who pay and configurehomes, rooms, connectors, routines, people
MEMBERResidentlives in the housecontrol the home, read history and notifications; no configuration, no people
GUESTGuesta temporary stay, such as a sitter, contractor, or short-let guestcontrol the one home for the stay; always pinned to a property, always with a checkout

A home keeps at least one Owner. Removing the last Owner fails with 409. A person does not remove themselves from the People list; account closure is a separate action.

There is no self sign-up in the console or the phone. The invitation link is the door into a home, and accepting it is the proof that the address belongs to that person. Accepting the link sets User.emailVerifiedAt for a new account, and existing accounts that match the invited address are added directly.

POST /tenants/:t/clients/:c/invitations is used by an Owner or tenant staff with { email, name?, role, propertyId?, startsAt?, expiresAt?, requireNetwork?, phoneThingId?, reservationKey? }.

What happens depends on the email address:

  • If the address already belongs to a verified account, the membership is created at once with status: added. The person is signed out of other sessions so the new home appears in the next claims, and both the person and the household receive a notification.
  • If the address does not yet belong to a verified account, the platform creates an invitation row and sends an email with a one-time link. The link works once and for seven days. A resend creates a new token and invalidates the old one. A withdrawn invitation is also invalidated.

GET /auth/invitations/:token shows the invite details without a session: the home, who invited, the role word, the stay, a masked address, and whether that address already has an account. POST /auth/invitations/:token/accept accepts the invitation without a session. A new person sends name and password; an existing account sends the password it already holds, and totp when MFA is on. In both cases the person ends up signed in with the home in their claims.

Every dead invitation link returns the same 410 sentence, whether it is unknown, spent, withdrawn, or expired.

A GUEST has propertyId and expiresAt, which is the checkout time. startsAt is optional; if it is absent, the stay starts now. The guest’s claims include the grant only inside the stay window, so a guest outside that window cannot obtain claims that mention the home.

The stay sweep runs every minute and handles the lifecycle in order:

  1. When the check-in time has passed, the stay is marked checked in once.
  2. When a guest is past checkout, sessions and the home’s widget tokens are revoked first.
  3. If revocation succeeds, the stay row is deleted.
  4. The household is told who left, the audit log records the checkout, and subscribers receive the checkout event.

A guest who comes back needs a new invitation. A new stay is a new row. In the phone, the stay appears in GET /auth/me.homes[].until, and after it ends the person sees “Your stay has ended.”

The only access condition that exists in the API is requireNetwork. There is no geofence condition, and no location reaches the platform. The app asks for coarse location only, but that location is used only to pick the nearest home on the phone and widget, not to decide access.

requireNetwork means the person is in claims only while the home proves their phone is on the home network. The condition is evaluated at sign-in and on every refresh, so the result is uniform across services and not checked per request. A lapse can remain visible until the current access token expires, while a change by the Owner takes effect at once.

Two signals are used, in order:

  1. UniFi presence of the member’s phone, read through the tenant’s internal presence route. If that read is missing, stale, or does not identify a known phone, it counts as no signal.
  2. The home’s internet address, compared against the caller’s first x-forwarded-for hop. This fallback is not available yet for every home.

If no signal is available when the Owner sets the condition, the API returns 400 with a message telling them to connect UniFi or wait for the hub link. An Owner cannot be given the condition. What is stored is the outcome of the last check, shown to users as “last seen home,” not an address or a position.

A home that does not meet the condition still appears in GET /auth/me.homes with status: "needs-network" and a sentence explaining that it answers when the person is on its Wi-Fi. That keeps the refusal understandable when someone is on cellular inside the house.

A conditional member cannot mint a widget token.

RouteWhoWhat
GET /tenants/:t/clients/:c/membersOwner, staffeveryone, with avatar, level, property, stay dates, status, network condition, phone, last seen, reservation key, invited by
GET /tenants/:t/clients/:c/members/phonesOwner, staffthe phones the home’s network watches, for choosing one for the condition
POST /tenants/:t/clients/:c/membersOwner, staffgrant by user id
PATCH /tenants/:t/clients/:c/members/:mOwner, stafflevel, home, stay, condition
DELETE /tenants/:t/clients/:c/members/:mOwner, staffrevoke sessions and the home’s widget tokens first, then remove the row
POST /tenants/:t/clients/:c/invitationsOwner, staffcreate an invitation or add a verified account at once
GET /auth/invitations/:tokenthe linkinspect the invitation
POST /auth/invitations/:token/acceptthe linkaccept the invitation
POST /tenants/:t/clients/:c/guests/:reservationKeyOwner, staff, or a key with guests:writecreate or update a guest stay
GET /tenants/:t/clients/:c/guests/:reservationKeyOwner, staff, or a key with guests:writeread a stay
DELETE /tenants/:t/clients/:c/guests/:reservationKeyOwner, staff, or a key with guests:writecheck a guest out now
GET /properties/:id/presence?tenantId&endpointIdinternalUniFi presence for one phone thing

GET /auth/me returns homes[] for every grant a person holds, whether the grant is open or not.

Short-term-rental tools use the reservation-key shape. PUT /tenants/:t/clients/:c/guests/:reservationKey with { email, name?, propertyId, checkIn, checkOut, requireNetwork?, phoneThingId? } is idempotent by key: the same key with new dates updates the stay, and the same dates return the existing stay. GET returns the stay state, which is invited, member, or checked-out. DELETE checks the guest out immediately.

This API is for a program that has a membership on the tenant and the guests:write scope. The current issuance path is manual for LIVE credentials.

Subscribers receive guest.invited, guest.checked_in, and guest.checked_out. Household notifications also announce when a membership is granted or ended. Invitation and member-added emails are sent through the notification channel.

A guest’s stay can be controlled only for the one home of that stay. Household permissions are narrower than staff. DEV credentials act in the developer sandbox. LIVE credentials only act for their own installed connector.

The console page for people is the normal path for owners, and the invitation link can always be accepted in a browser if the app link does not open the app.

Last updated