> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dubupay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# POST /auth/refresh — Rotate your access token

> Exchange a valid refresh token for a new access token and a new refresh token. The old refresh token is immediately invalidated after a successful rotation.

Access tokens expire after 15 minutes. Instead of asking your users to log in again, you can silently obtain a fresh access token by sending your stored refresh token to this endpoint. The API validates the token, issues a new access token and a new refresh token, and immediately revokes the one you just used. This rotation strategy means a stolen refresh token can only be used once before it becomes invalid.

No authentication header is required — the refresh token itself is the credential.

## Endpoint

```
POST https://api.dubupay.com/api/v1/auth/refresh
```

## Request body

<ParamField body="refresh_token" type="string" required>
  The refresh token previously issued by `POST /auth/login`, `POST /auth/refresh`, or `POST /auth/verify-email`. Each refresh token can only be used once.
</ParamField>

## Response

HTTP `200 OK` on success.

<ResponseField name="success" type="boolean">
  `true` on a successful token rotation.
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="data">
    <ResponseField name="access_token" type="string">
      A new JWT access token. Valid for **15 minutes**.
    </ResponseField>

    <ResponseField name="refresh_token" type="string">
      A new JWT refresh token. Valid for **7 days**. Replace the token you stored with this new value immediately — the previous refresh token is now invalid and cannot be used again.
    </ResponseField>
  </Expandable>
</ResponseField>

## Example

```bash theme={null}
curl -X POST https://api.dubupay.com/api/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }'
```

**Response**

```json theme={null}
{
  "success": true,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}
```

## Token rotation

Every successful call to this endpoint invalidates the refresh token you submitted and replaces it with a brand-new one. You must update your stored refresh token after every rotation.

If the same refresh token is used twice — for example because an attacker replayed a captured token — the second attempt returns `401 TOKEN_REVOKED`. At that point you should treat the session as compromised, clear all stored tokens, and require the user to log in again.

Refresh tokens are also invalidated when you:

* Call `POST /auth/logout`
* Change your password via `PATCH /auth/me/password`

In both cases any stored refresh token becomes immediately unusable and a new login is required.

## Error responses

| Status | Code                    | Description                                                                         |
| ------ | ----------------------- | ----------------------------------------------------------------------------------- |
| `400`  | Validation error        | `refresh_token` is missing from the request body.                                   |
| `401`  | `INVALID_REFRESH_TOKEN` | The token is malformed, has expired, or the signature does not match.               |
| `401`  | `TOKEN_REVOKED`         | The token was already used or has been invalidated (logout / password change).      |
| `401`  | `UNAUTHORIZED`          | The merchant account associated with this token no longer exists or is deactivated. |
