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

# Token Yenileme

> Access token süresi dolduğunda refresh token kullanarak yeni bir access token alın.

Access token 15 gün geçerlidir. Süresi dolduğunda, token değişiminde aldığınız `refresh_token`'ı kullanarak yeni bir çift (access + refresh) alabilirsiniz. Refresh token'lar 30 gün geçerlidir.

## Ne zaman yenilemeli?

* Bir API çağrısı `401 Unauthorized` ile dönerse token'ı yenileyip isteği bir kez tekrarlayın.
* Proaktif yenileme için `expires_in` değerini saklayın ve süre dolmadan kısa süre önce (ör. 1 saat önce) yenileyin.
* `refresh_token` 30 gün geçerlidir; bu süre dolduktan sonra kullanıcıyı **yeniden yetkilendirmeniz** gerekir.

## İstek

<CodeGroup>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://dev.flextell.ai/oauth/token \
    --header "Content-Type: application/x-www-form-urlencoded" \
    --data "grant_type=refresh_token" \
    --data "refresh_token=ESKI_REFRESH_TOKEN" \
    --data "client_id=SIZIN_CLIENT_ID" \
    --data "client_secret=SIZIN_CLIENT_SECRET" \
    --data "scope=customers:read appointments:read"
  ```

  ```js Node.js theme={null}
  const params = new URLSearchParams({
    grant_type: "refresh_token",
    refresh_token: oldRefreshToken,
    client_id: process.env.FLEXTELL_CLIENT_ID,
    client_secret: process.env.FLEXTELL_CLIENT_SECRET,
  });

  const res = await fetch("https://dev.flextell.ai/oauth/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: params,
  });

  const tokens = await res.json();
  ```
</CodeGroup>

<ParamField path="grant_type" type="string" required>
  Her zaman `refresh_token`.
</ParamField>

<ParamField path="refresh_token" type="string" required>
  Daha önce aldığınız geçerli refresh token.
</ParamField>

<ParamField path="client_id" type="string" required>
  OAuth uygulamanızın `client_id` değeri.
</ParamField>

<ParamField path="client_secret" type="string" required>
  OAuth uygulamanızın `client_secret` değeri. PKCE ile oluşturulmuş public client'lar bu alanı göndermez.
</ParamField>

<ParamField path="scope" type="string">
  Opsiyonel. Orijinal yetkilendirmedeki scope'ların bir **alt kümesi** olabilir. Gönderilmezse orijinal scope'lar korunur. Yeni scope talep etmek için kullanıcıyı tekrar yetkilendirme akışına sokmanız gerekir.
</ParamField>

## Yanıt

```json theme={null}
{
  "token_type": "Bearer",
  "expires_in": 1296000,
  "access_token": "eyJ0eXAi...",
  "refresh_token": "def50200yeni..."
}
```

<Warning>
  **Yeni `refresh_token`'ı saklayın.** Flextell her yenileme sonrasında eski refresh token'ı iptal edip yenisini verir (refresh token rotation). Eski token artık çalışmaz.
</Warning>

## Otomatik yenileme kalıbı

HTTP istemcinizi, 401 yanıtında token'ı yenileyip isteği bir kez tekrarlayacak şekilde sarmalamak iyi bir örüntüdür:

```js theme={null}
async function flextellFetch(url, options = {}, retry = true) {
  const res = await fetch(url, {
    ...options,
    headers: {
      ...options.headers,
      Authorization: `Bearer ${tokens.access_token}`,
    },
  });

  if (res.status === 401 && retry) {
    await refreshTokens();
    return flextellFetch(url, options, false); // bir kez tekrar dene
  }

  return res;
}
```

## Hata yanıtları

| HTTP | `error`          | Sebep                                                                                               |
| ---- | ---------------- | --------------------------------------------------------------------------------------------------- |
| 400  | `invalid_grant`  | Refresh token geçersiz, süresi dolmuş veya iptal edilmiş. Kullanıcıyı yeniden yetkilendirmelisiniz. |
| 401  | `invalid_client` | `client_id`/`client_secret` hatalı.                                                                 |
| 400  | `invalid_scope`  | Orijinal yetkilendirmede olmayan bir scope talep ettiniz.                                           |

## Güvenlik notları

* Refresh token'ları **server-side** saklayın; tarayıcı localStorage'ına yazmayın.
* Mümkünse kullanıcı başına şifrelenmiş saklama kullanın.
* Bir refresh token yenileme sırasında yenisi dönerse **eskiyi kullanmaya devam etmeyin**; eski token muhtemelen iptal edilmiştir.
* Kullanıcı panelden uygulamanıza verdiği yetkiyi kaldırırsa tüm token'lar anında geçersiz olur.
