Sign Out
Auth Endpoints
Sign Out
Sign out the current user, invalidate the refresh token family, and optionally unbind the device’s push
Sign Out
Signs out a user by invalidating their refresh token and the entire token family associated with it. All rotated tokens from the same session are revoked, preventing future token refreshes.
A plain sign-out — a body carrying only
The session really is destroyed, so this is not an error and must not be retried as one. It is not a
The
The unbind was not what failed. Either it succeeded and the token-family
destroy did not, or the transaction never opened at all — in both cases the
whole transaction, including any unbind, was rolled back.
A generic fault, raised on a path where no unbind was ever attempted and so
no push binding is at stake. Nothing was committed; retry.
Returned when
Returned when the project has spent its monthly API-call allowance and is on
a plan that hard-blocks at the limit. Distinct from the rate-limit
Returned while the project is mid-migration.
Rate limit: 50 requests per 5 minutes per IP. Exceeding it returns
refreshToken — always returns 204 No Content, even if the refresh token is missing, invalid, or already expired. This keeps sign-out safe to call unconditionally: a user must always be able to end their session locally, whatever state their credential is in.
Supplying the optional pushDevice object changes that in one narrowly scoped way. See Atomic push deregistration below.
Body Parameters
string
The refresh token JWT to invalidate. If omitted or
null, the server returns
204 without performing any revocation.object
Optional. Identifies the physical device the user is signing out on, so the
server can unbind that account’s push notifications in the same request. Same
shape as the Deregister Device
body. Ignored on projects without the
push bundle.Atomic push deregistration
A device can be signed into several accounts at once, and each account has its own push binding on it. Signing out of an account without removing its binding would leave that account’s notifications arriving on a device nobody is signed into — and once the credential is gone there is nothing left to repair it with. So whenpushDevice is present and the project has the push bundle, the server deletes that user’s push binding for that device inside the same transaction as the token-family destroy, attempting the unbind first. Either both happen or neither does.
Details worth knowing:
- The binding is user-scoped. The unbind targets only the signing-out user’s row for that device. Other accounts bound to the same device are untouched.
- An expired refresh token still unbinds. Session destruction requires a live token, but the unbind accepts an authentic-but-expired one (its signature still proves which user it was minted for) up to 90 days past expiry. Beyond that bound the token is refused as an unbind credential and the request falls back to a plain
204. - A destroyed token family still unbinds. If the session was already ended by a prior sign-out-all, the binding is still removed.
- Projects without the
pushbundle are unaffected on the success path. The route itself carries no bundle gate — it is never refused withdatabase/tables-not-available. WhenpushDeviceis supplied the server looks the bundle up, and on a project that does not have it thepushDeviceis ignored and the endpoint returns204exactly as it does without one. - If the bundle lookup itself cannot be resolved, the sign-out still completes and the skipped unbind is reported. The response is a
200carryingpushUnbindSkipped: trueand the codeauth/push-unbind-status-unknown(see Response) instead of a bare204, because nobody checked and the binding may still be live. The session is destroyed either way — a user can always sign out. That body is emitted only once the sign-out has actually committed; in the rare case where the lookup could not be resolved and the session write then failed, the response is a plain204instead, because the claim it makes — signed out, do not retry — would be false. - A device identifier matching no binding still returns
204. The unbind is scoped to the signing-out user, so a key that matches no row of theirs removes nothing and the sign-out completes. The server records that outcome in its own logs; there is no separate response for it. - A malformed or wrong-signature token still returns
204. There is no user to scope an unbind to and no session to destroy, so the endpoint keeps its permissive behavior rather than blocking sign-out.
Response
Returns204 No Content on success. No response body.
Signed out, unbind skipped — 200
One success case answers with a body instead. WhenpushDevice was supplied and the server could not determine whether the project has the push bundle, it signs the user out and does not attempt an unbind:
204 either: a 204 says there is nothing left to remove, and here nobody looked — the binding may still be live while the credential that could have removed it is being discarded. Surface it, and remove the binding by registering that account on the device again.
Because that promise is what clients act on, the body rides only on a committed sign-out. If the session write fails on this path the transaction rolls back, the token family survives, and the endpoint answers a plain 204 rather than claiming a sign-out that did not happen.
Error Responses
The first two500s below are reachable only when pushDevice is supplied; the generic one is reachable with or without it. Every 500 means nothing was committed — the session, and the push binding when there was one at stake, are both still in place, and the request can be retried. Everything after them is decided before the handler runs and can be returned with or without a pushDevice.
Push Deregistration Failed — 500
Push Deregistration Failed — 500
PushDevices delete itself failed inside the transaction, so nothing
was committed and the session was left intact. The binding is still live, so
keep the account and its credential and retry.Sign Out Failed — 500
Sign Out Failed — 500
Server Error — 500
Server Error — 500
Invalid Body — 400
Invalid Body — 400
pushDevice is malformed — for example platform: "ios" with no
token, or platform: "web" with no subscription.Quota Reached — 429
Quota Reached — 429
429
below, which is plain text with no code.Project Pending Deletion — 423
Project Pending Deletion — 423
Project Migrating — 503
Project Migrating — 503
429 with the plain-text message Too many sign out attempts, please wait 5 minutes and no code.
The budget is sized for a fan-out: signing out of every stored account issues one request per account in parallel, so a five-account sign-out spends five of the fifty. The limiter keys on IP, so several users behind one address share the budget.
A rejection that never reached this endpoint is not an unbind failure. The
423, 503 and
429 above — and a 400 — are decided before the handler runs, so no unbind was attempted and no
push binding is at risk. Exactly two responses mean the server could not complete an unbind it was
asked for and committed nothing: auth/device-deregistration-failed and auth/sign-out-failed.
Those two are the ones a client must keep its credential for; on anything else it should sign out
locally as usual. A client that blocks account removal on any failure makes an account
unremovable whenever the project is throttled or migrating. auth/push-unbind-status-unknown is
deliberately not one of them: it rides on a 200, the sign-out has already happened, and blocking
on it would leave a user unable to leave their account because a cache lookup was briefly
unavailable.
