Skip to main content
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 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 when pushDevice 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.
If the unbind fails, nothing is committed and the request returns an error. The session survives, the refresh token still works, and the caller is expected to retry. Do not treat a failed sign-out as signed out — tearing down local state anyway is exactly what leaves a user receiving notifications from an account they can no longer reach.
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 push bundle are unaffected on the success path. The route itself carries no bundle gate — it is never refused with database/tables-not-available. When pushDevice is supplied the server looks the bundle up, and on a project that does not have it the pushDevice is ignored and the endpoint returns 204 exactly 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 200 carrying pushUnbindSkipped: true and the code auth/push-unbind-status-unknown (see Response) instead of a bare 204, 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 plain 204 instead, 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

Returns 204 No Content on success. No response body.

Signed out, unbind skipped — 200

One success case answers with a body instead. When pushDevice 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:
The session really is destroyed, so this is not an error and must not be retried as one. It is not a 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 two 500s 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.
The 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.
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 pushDevice is malformed — for example platform: "ios" with no token, or platform: "web" with no subscription.
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 429 below, which is plain text with no code.
Returned while the project is mid-migration.
Rate limit: 50 requests per 5 minutes per IP. Exceeding it returns 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.

See Also