> ## Documentation Index
> Fetch the complete documentation index at: https://metacognition-fdc534de-master.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Refresh access token

> Use a refresh token to get a new access token.

export const TokenRetryVisual = () => <div className="not-prose my-6">
    <figure className="rounded-xl border border-zinc-950/15 bg-zinc-950/[0.02] p-4 dark:border-white/15 dark:bg-white/[0.04]">
      <div className="flex flex-col gap-2.5">
        <div className="max-w-full rounded-lg border border-[#F32C05]/40 bg-[#F32C05]/10 p-3 dark:border-[#FF5530]/45 dark:bg-[#F32C05]/15">
          <div className="text-[10px] font-semibold uppercase tracking-wide text-zinc-600 dark:text-zinc-400">
            Start
          </div>
          <div className="mt-1.5 text-sm font-semibold text-zinc-900 dark:text-zinc-50">
            A normal request comes back 401
          </div>
          <div className="mt-2 text-xs leading-snug text-zinc-600 dark:text-zinc-400">
            Usually the access token expired; refresh might still work.
          </div>
        </div>
        <div className="text-center text-sm text-zinc-400 dark:text-zinc-500">↓</div>
        <div className="max-w-full rounded-lg border border-[#F32C05]/40 bg-[#F32C05]/10 p-3 dark:border-[#FF5530]/45 dark:bg-[#F32C05]/15">
          <div className="text-[10px] font-semibold uppercase tracking-wide text-zinc-600 dark:text-zinc-400">
            First try
          </div>
          <div className="mt-1.5 text-sm font-semibold text-zinc-900 dark:text-zinc-50">POST /auth/refresh</div>
          <div className="mt-2 text-xs leading-snug text-zinc-600 dark:text-zinc-400">
            If that returns 200, you get a new access token and retry what you were doing.
          </div>
        </div>
        <div className="text-center text-sm text-zinc-400 dark:text-zinc-500">
          ↓ if refresh is also 401
        </div>
        <div className="max-w-full rounded-lg border border-[#F32C05]/40 bg-[#F32C05]/10 p-3 dark:border-[#FF5530]/45 dark:bg-[#F32C05]/15">
          <div className="text-[10px] font-semibold uppercase tracking-wide text-zinc-600 dark:text-zinc-400">
            Fallback
          </div>
          <div className="mt-1.5 text-sm font-semibold text-zinc-900 dark:text-zinc-50">
            POST /auth/token-exchange
          </div>
          <div className="mt-2 text-xs leading-snug text-zinc-600 dark:text-zinc-400">
            Send your API key again. 200 means retry with the new token. 401 means the key is dead and you need a new
            one or a proper login flow.
          </div>
        </div>
      </div>
      <p className="mb-0 mt-3.5 text-xs leading-relaxed text-zinc-600 dark:text-zinc-400">
        In most SDK setups a single failed request can walk through refresh (and then exchange) before your code
        returns an error unless the full chain returns 401.
      </p>
    </figure>
  </div>;

Use this when an `access_token` has expired and the `refresh_token` is still valid. The SDK does this automatically after a 401.

## Body

```json theme={null}
{
  "refresh_token": "eyJhbGciOiJSUzI1NiIs..."
}
```

<ParamField body="refresh_token" type="string" required>
  The refresh token returned by `/auth/token-exchange`.
</ParamField>

## Response — `200`

<ResponseField name="access_token" type="string">
  Fresh 24h JWT.
</ResponseField>

<ResponseField name="refresh_token" type="string">
  May be rotated. Store the value returned by the response.
</ResponseField>

<ResponseField name="token_type" type="string">
  Always `"bearer"`.
</ResponseField>

<ResponseField name="expires_in" type="number">
  Lifetime of the new access token in seconds.
</ResponseField>

## Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.getmetacognition.com/auth/refresh \
    -H 'content-type: application/json' \
    -d '{"refresh_token":"eyJ..."}'
  ```

  ```python Python theme={null}
  import httpx

  resp = httpx.post(
      "https://api.getmetacognition.com/auth/refresh",
      json={"refresh_token": refresh_token},
  )
  tokens = resp.json()
  ```
</CodeGroup>

## When refresh fails

If the refresh token is expired (more than 7 days old) or revoked, `/auth/refresh` returns `401`. At that point, call `/auth/token-exchange` with the original API key. If the API key is also gone, ask the user or service to authenticate again.

### After 401

<TokenRetryVisual />

Without the SDK, implement this sequence in your HTTP client. With the SDK, `AuthenticationError` usually means refresh **and** exchange both failed.
