Skip to content

Authentication

Every endpoint except the webhook receivers expects a bearer token:

Authorization: Bearer <access token>
Token Lifetime Where it lives
Access token 15 minutes Returned in the login response body; you store it
Refresh token 7 days vespy_refresh_token, an HttpOnly SameSite=Lax cookie set by the server

POST /api/auth/login returns the access token and sets the refresh cookie in the same response.

Terminal window
curl -X POST https://api.getvespy.com/api/auth/token \
-H 'Content-Type: application/json' \
--cookie 'vespy_refresh_token=…' \
-d '{"grantType":"refresh_token"}'

The body accepts either grantType or grant_type. The response has the same shape as login, including a rotated refresh cookie.

POST /api/auth/logout clears the refresh cookie. Access tokens are not revoked server-side, so an already-issued one stays valid until it expires.

The access token carries an organizationId, and every query is scoped to it — you cannot read another organization’s data by guessing a UUID. A resource that belongs to a different organization returns 404, not 403, so ID enumeration reveals nothing.

Role Sees
admin The whole portfolio, plus billing, integrations, and user management
manager The whole portfolio, and most mutations
staff The portfolio, read-mostly
tenant Only their own leases, charges, payments, and maintenance requests
owner Only their own properties, statements, and ledgers
vendor Only work orders assigned to them

Endpoints tagged Portal and Vendor Portal are the ones scoped to a single tenant, owner, or vendor; they derive the subject from the token rather than from a path parameter.

An endpoint your role cannot use returns 403 with a message naming the requirement, for example Only admins or managers can create vendors.

GET /api/events/stream is the one authenticated endpoint that does not take an Authorization header — EventSource cannot set headers, so it takes the access token as a token query parameter instead.