# invalid_grant

> The code couldn't be redeemed

- Status: 400 Bad Request
- Returned by: The account connection (/oauth)
- Retry (`retryWhen`): `never`
- Did anything happen? (`outcome`): `unknown` (Unknown)
- The account connection
- Page in the portal: https://developers.rivr.io/errors/invalid_grant
- Try it: `GET https://developers.rivr.io/errors/simulate/invalid_grant` answers with the code's exact wire form; `?succeedAfter=N&attempt=K` gives a 200 once `attempt` passes `N`

## What it means

The code is unknown, issued to another client, older than five minutes or already used, `redirect_uri` isn't the same as in `GET /oauth/authorize`, `code_verifier` doesn't match, the clinic can't be connected any more (it has got another platform, been suspended or stopped being ready to take payments), the clinic or you have disconnected after the code was issued, or the connection has been switched off for your platform, or the client may no longer connect. The answer doesn't say which. A wrong secret gives `invalid_client` instead.

## What your system does

Don't repeat the call with the same code: a wrong address, a wrong verifier and an expired code burn it, and a used code gets the same answer. Start over from `GET /oauth/authorize` with a new `state` and a new verifier. Show the user an error, not a successful connection.

## Is it worth retrying?

No, not without changing the call.

`retryWhen`: `never`. Don't send it again. The answer is final for this request: read the current state, the order for example, and act on that.

## Did anything happen?

`outcome`: `unknown`. **Unknown.** The answer doesn't say whether what you asked for has happened, through this call or an earlier one. Read the state first.

If the same code has already been redeemed the clinic may be connected, and then it stays connected, so a lost answer can look like this. In other cases the call made no connection, with one rare exception: if the clinic disconnects during the redemption and the connection can't be taken back, it stays. So read the list.

Read the state (`resolveWith`):

- `oauthToken` → [`GET /api/public/merchants`](https://developers.rivr.io/reference/api-public/listMerchants.md): If the clinic is in the list the connection is made. Start over from `GET /oauth/authorize` to get the clinic's id in a new answer: a clinic already connected isn't changed by that.

## Example: The account connection (/oauth)

```http
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store

{
  "error": "invalid_grant",
  "error_description": "The code is not valid."
}
```

The `title`, `detail`, `message` and `error_description` texts are exactly as the API writes them today.
