Skip to content
TDACorpIDENTITYSign in

HTTP status codes and Retry-After

The status codes the protocol endpoints and the account API return, what each means, and how to honour Retry-After.

On this page

This page says which status code to expect from which endpoint, and what to do about it. The error codes themselves, with their text, are on Error codes.

Token, revocation and introspection endpoints, and pushed authorization requests#

The body is JSON with error and error_description, and the response is marked no-store.

StatusWhenWhat to do
200The grant succeeded.Read the tokens.
400Any OAuth error except the cases below: invalid_request, invalid_grant, invalid_scope, invalid_target, unauthorized_client, unsupported_grant_type, invalid_dpop_proof, use_dpop_nonce, insufficient_user_authentication and the rest.Read error; see the troubleshooting pages. An insufficient_user_authentication answer (an organization that requires single sign-on, behind the sso_enforcement flag, which defaults OFF) carries acr_values and max_age: start a new authorization with them. A use_dpop_nonce answer carries a DPoP-Nonce header: sign a new proof with it and send the request again.
401invalid_client: the client could not be authenticated, the client is suspended, or no key was proved where one is required. This is the status at the token, revocation and introspection endpoints.Check the client registration, the assertion and the DPoP key.
400invalid_client at the pushed authorization request endpoint, where it is answered with 400 rather than 401.As for 401. Rely on the error code, not the status.
429A limit was reached. The body is the same OAuth error shape, and Retry-After is set.Wait; see below.
500server_error.Retry later. Do not loop.

Authorization endpoint#

How an error arrives depends on what failed.

  • The client or its redirect address cannot be trusted. Nothing is sent to the application. A browser navigation is sent to an error page on the identity site (status 303) that shows a Request ID; a request that is not a browser navigation gets a 400 JSON body.
  • Everything about the client is fine, but the request failed. The person is sent back to the registered redirect address with error, error_description, state and iss, as a redirect or a form post.

Userinfo and the account profile#

These endpoints take an access token, and the answer follows RFC 6750 and RFC 9449.

StatuserrorMeaning
401invalid_tokenThe token is missing, malformed, expired, revoked or not valid for this use. The WWW-Authenticate header says Bearer and DPoP.
401use_dpop_nonceThe proof needs a current server nonce. A DPoP-Nonce header is returned. Use it and retry once.
403insufficient_scopeThe token does not carry the scope the endpoint needs. The header names it.
429temporarily_unavailableA limit was reached at userinfo. Retry-After is set. The profile route that takes a personal access token answers rate_limited instead.

Account API#

The account API under /v1/me answers a signed-in browser and a first-party token. See Account API at a glance.

StatusCodeMeaning
400invalid_requestSomething in the request was not valid.
401unauthenticatedThe browser has no live session, or a token caller's token is not valid (a token caller sees invalid_token).
401step_up_requiredA fresh second factor is needed (a token caller sees insufficient_user_authentication, with acr_values and max_age). See Troubleshooting: step_up_required.
403forbiddenThe caller may not do this (a token caller sees insufficient_scope when a scope is missing).
403managed_accountAn account that an organization manages tried a route that changes its sign-in methods, creates personal access tokens, manages agents or deactivates the account, or a step-up it has no way to answer. The credentials and lifecycle belong to the organization.
403cross_originA request that changes something did not come from the identity site, so it was refused.
403device_too_newThe browser is new to the account, and the account holds no second factor to confirm with. Make the change from a browser you already use, or 24 hours after you confirm this one by signing in with an emailed link or code. An account that holds a passkey or an authenticator app gets step_up_required instead.
403insufficient_assuranceThe sign-in is not strong enough for this.
404not_foundThere is nothing at that address, or it is not yours.
413payload_too_largeThe request was too large.
415unsupported_media_typeThe request was not in a format accepted.
429rate_limitedToo many attempts.
500server_errorSomething went wrong on the server.
503email_unavailableEmail could not be sent right now: the provider refused the message, the service's daily send budget is spent, or the account has no verified address to send to.

Only the codes in Error codes appear in these answers. A code not on this table can still occur; use the code, not the status alone, to decide what to do.

The browser form of an error is { "error": { "code", "reason", "message", "requestId" } }, where reason is present only when the cause is safe to say (address_capped, email_budget, no_verified_address, second_step_budget, method_not_allowed or passkey_limit) and the account pages then show a Why does this happen? link and a Reference: line with the requestId, with methods and providers on a step-up answer and retryAfterS on a limit. A token caller gets the flat form { "error", "error_description" }, with the same reason beside them when there is one. Quote requestId when you ask for help.

Retry-After#

A 429 from the protocol endpoints carries Retry-After in whole seconds, never less than 1. The account pages show the same value as Too many attempts. Try again in followed by the wait in the largest sensible unit, rounded up: 1 second, 45 seconds, 57 minutes or 2 hours.

  • Wait at least that long before the next request. Sending more requests earlier does not make the limit lift sooner.
  • The SDK's relying party and service client retry a 429 a bounded number of times, waiting for Retry-After, and then report retryAfterSeconds for you to act on. They never retry a 5xx. The other retry they make is the DPoP nonce challenge.
  • A 429 can follow a run of failures, for example wrong credentials or a bad client assertion, so fix the cause as well as waiting.
  • This page does not publish the limits themselves. They can change without notice, so code against Retry-After, not a number.

See Troubleshooting: 429 Too Many Requests.

Verified against commit ccc9960