Rentals — Authentication
Every call to the Rentals API carries an OAuth 2.0 bearer token. This page covers getting one, the scopes that gate this vertical, and revoking a token.
Credentials
You need a client ID and client secret issued for your rentals vendor account. These are machine credentials — there is no browser redirect and no user login.
The client secret authenticates your whole account. Keep it server-side. Never ship it in a mobile app or browser bundle.
Getting a token
Kesk supports exactly one grant: client_credentials. Authenticate with HTTP
Basic — client ID as username, secret as password — and send the grant type as
JSON.
A successful response:
Read scope and expires_in from the response rather than assuming either.
The values above are illustrative — the granted scope can be narrower than
what you asked for, and the token lifetime is set server-side. Never hardcode
a lifetime.
Using the token
Cache the token for its expires_in and refresh on a margin. The token endpoint
does not emit rate-limit headers, but requesting a fresh token per call is
wasteful and slow.
Scopes for Rentals
These are the scopes that gate this vertical. Request the narrowest set that does the job.
| Scope | Grants |
|---|---|
vendors:profile:read | Read the vendor profile |
vendors:settings:read | Read vendor settings |
vendors:locations:read | List and fetch locations |
vendors:locations:write | Create, update and delete locations |
vendors:webhooks:read | List and fetch webhooks |
vendors:webhooks:write | Manage webhooks and rotate secrets |
vendors:bots:read | List bots and read analytics |
vendors:bots:write | Manage bots and rotate secrets |
vendors:rentals:read | List and fetch rental orders and settings |
vendors:rentals:write | Process pickups and returns, extend and cancel |
vendors:promotions:read | List the coupons this vendor owns |
vendors:promotions:write | Create, update and delete this vendor's coupons |
vendors:analytics:read | Read sales analytics and performance metrics |
vendors:reviews:read | Read customer reviews |
vendors:promotions:read gates
vendorCoupons;
vendors:promotions:write gates
createVendorCoupon,
updateVendorCoupon and
deleteVendorCoupon.
vendors:analytics:read gates
vendorSalesAnalytics and
vendorPerformanceMetrics;
vendors:reviews:read gates
vendorReviews.
Scopes are matched exactly, or by a vendors:* wildcard on the : boundary.
vendors:* is not a synonym for the coarse grant vendors — the wildcard
grants the whole surface including the sensitive scopes the coarse grant
withholds. Ask for it deliberately, not as shorthand.
The coarse grant vendors expands at issuance into the vendor surface's
granular scopes, minus the eight the registry withholds by name — see
OAuth 2.0 for the list. Those must be requested explicitly,
or granted with a surface wildcard such as vendors:*.
One of the eight is on this vertical: vendors:promotions:write. A coupon
discounts your own revenue, and a coarse consent must not silently carry the
ability to issue discounts against your business.
So a coarse vendors grant gives you every scope in the table above except
vendors:promotions:write. Read the granted scope back from the token
response regardless — it is the record of what you were actually given.
The complete scope registry is published as scopes_supported at
accounts.kesk.app/.well-known/oauth-authorization-server.
Note the host: that is the authorization server, not the API host, so the
path does not resolve on api.kesk.app. The table above is the subset
that gates this vertical; the registry also lists the scopes of every other
surface. The scope in your token response remains the record of what you were
actually granted.
Errors
| Situation | Status | Body |
|---|---|---|
Missing Authorization header | 401 | {"title":"Unauthorized","message":"Authentication required.","status":401,"type":"unauthorized"} |
Wrong client ID or secret | 401 | {"error":"invalid_client","error_description":"Client authentication failed."} |
Unsupported grant_type | 400 | {"error":"unsupported_grant_type","error_description":"Only 'client_credentials' grant type is supported."} |
Scope not on your registration | 400 | {"error":"invalid_scope","error_description":"Scope exceeds the client's registered grant: ..."} |
Form-encoded body on /token | 400 | {"error":"Bad Request","message":"invalid content type ..., expect: application/json"} |
Validation order is grant_type → client → scope, so a bad grant type is
rejected before your credentials are checked.
Note the missing-header and wrong-content-type cases return plain text — parsing either as JSON will throw.
Revoking a token
The token endpoint takes JSON; the revoke endpoint takes
application/x-www-form-urlencoded. Sending JSON here returns 415.
token_type_hint is optional and ignored, and access_token is the only value
the endpoint will parse — any other string is a 400. Revocation applies to
access tokens: passing a refresh token returns 200 and revokes nothing.
Revocation kills the entire token family, not just the string you passed — which is what you want when responding to a leak.
Per RFC 7009 the endpoint returns 200 even for an unknown token, so a 200
means "not valid any more" rather than "existed and was revoked". Treat it as
idempotent and safe to retry.
To rotate a secret: issue and deploy the new credentials, drain in-flight work, then revoke the old token. Revoking first breaks every worker still holding it.
Reference
Generated reference — every type, field and argument, built from the live schema.