> ## 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.

# Toplu randevu taşıma

> Kapatılan saatlerdeki randevuları önizleyip toplu taşımayı ve taşıma sonuçlarını anlatır.

Bir doktorun belirli bir saat aralığını kapattığınızda (izin, toplantı, arıza), o aralıktaki randevuları tek istekle yeni saatlere taşıyabilirsiniz. Flextell her randevu için doktorun sonraki boş saatini bulur ve hastaya onay mesajı gönderir. Randevu, hasta onaylayana kadar eski saatinde kalır.

Bu sayfa önizleme ve taşıma endpoint'lerini, sonuç alanlarını ve hastaya giden mesajı anlatır. Panelden aynı işlemi yapmak için bkz. [Toplu taşıma](/panel/randevular/toplu-tasima). Randevu nesnesi için bkz. [Randevular](/api/kaynaklar/randevular).

## Endpoint özeti

Tüm istekler `Authorization: Bearer <access_token>` ve `X-Tenant: <tenant_id>` başlıklarını gerektirir. Bkz. [Klinikler ve X-Tenant](/api/klinikler-ve-x-tenant).

| Metot | Endpoint | Açıklama | Scope | İzin |
| - | - | - | - | - |
| GET | `/v1/appointments/reschedule/preview` | Kapatılan aralıktaki randevuları ve önerilen yeni saatleri listeler. Hiçbir şey değiştirmez. | `appointments:read` | `BulkReschedule:Appointment` |
| POST | `/v1/appointments/reschedule` | Randevuları yeni saatler için önerir ve hastalara onay mesajı gönderir. `202` döner. | `appointments:write` | `BulkReschedule:Appointment` |
| GET | `/v1/appointments/reschedule/history` | Toplu taşıma partilerini ilerleme sayılarıyla listeler. | `appointments:read` | `View:Appointment` |
| GET | `/v1/appointments/reschedule/history/{batchId}` | Bir partinin taşıdığı randevuları ve hasta yanıtlarını verir. | `appointments:read` | `View:Appointment` |

<Info>
  `BulkReschedule:Appointment` arayüzde **Toplu Randevu Taşı** olarak görünür. Yeni kliniklerde varsayılan satış temsilcisi rolünde bu izin yoktur; satış temsilcileri API ile toplu taşıma yapamaz. İzin modeli için bkz. [İzinler ve görünürlük](/api/izinler-ve-gorunurluk).
</Info>

## Önizleme

Önizleme ve taşıma isteği aynı üç parametreyi alır.

<ParamField query="user_id" type="integer" required>
  Doktorun kullanıcı ID'si. Doktor isteği yapan kliniğe ait olmalıdır.
</ParamField>

<ParamField query="blocked_from" type="string" required>
  Kapatılan aralığın başlangıcı (dahil). Bugün veya sonrası olmalıdır.
</ParamField>

<ParamField query="blocked_until" type="string" required>
  Kapatılan aralığın bitişi (hariç). `blocked_from` değerinden sonra olmalıdır.
</ParamField>

Offset içermeyen bir değer klinik saat diliminde okunur. `Z` veya `+03:00` gibi offset içeren değer olduğu gibi kullanılır.

Aralığa şu randevular girer: doktorun `pending` veya `approved` durumundaki, silinmemiş ve `blocked_from <= scheduled_at < blocked_until` koşulunu sağlayan randevuları. Bitiş hariç olduğu için 09:00–12:00 aralığını kapattığınızda 12:00'deki randevu taşınmaz. Yalnızca sizin görebildiğiniz randevular sayılır.

<CodeGroup>
  ```bash curl theme={null}
  curl -G "https://app.flextell.ai/api/v1/appointments/reschedule/preview" \
    -H "Authorization: Bearer <access_token>" \
    -H "X-Tenant: <tenant_id>" \
    --data-urlencode "user_id=123" \
    --data-urlencode "blocked_from=2026-10-20T09:00:00+03:00" \
    --data-urlencode "blocked_until=2026-10-20T12:00:00+03:00"
  ```
</CodeGroup>

Yanıtta `data` bir dizidir. Her satır bir randevudur.

```json theme={null}
{
  "success": true,
  "data": [
    {
      "appointment_id": 456,
      "customer_name": "Ayşe Yılmaz",
      "channel_name": "WhatsApp",
      "channel_address": "+90 5xx xxx xx xx",
      "original_at": "20.10.2026 10:00",
      "proposed_at": "20.10.2026 12:15",
      "is_already_pending": false,
      "skip_reason_code": null
    }
  ]
}
```

| Alan | Tür | Açıklama |
| - | - | - |
| `appointment_id` | integer | Randevu ID'si. |
| `customer_name` | string | Hastanın adı. Ad yoksa `Bilinmeyen`. |
| `channel_name` | string | Onay mesajının gideceği kanal. Randevunun kanalı, yoksa hastanın son aktif sohbet kanalı. Hiçbiri yoksa `Bilinmiyor`. |
| `channel_address` | string | Kanalın adresi. Telefon tabanlı kanallarda telefon, diğerlerinde hastanın kanal kimliği. |
| `original_at` | string | Mevcut randevu saati. `d.m.Y H:i` biçiminde, klinik saatiyle. |
| `proposed_at` | string \| null | Önerilen yeni saat, aynı biçimde. Uygun saat bulunamazsa `null`. |
| `is_already_pending` | boolean | Randevunun önceki bir taşımadan kalan, yanıt bekleyen önerisi varsa `true`. |
| `skip_reason_code` | string \| null | Randevu taşınmayacaksa nedeni. Verisi silinmiş hastada `CUSTOMER_ERASED`. |

<Note>
  Önizleme, öneriyi hesaplarken aynı partide başka randevuya ayrılan saatleri atlar. Gerçek taşıma sırasında aynı hesap yeniden yapılır; iki çağrı arasında takvim değişirse önerilen saatler farklı çıkabilir.
</Note>

### Yeni saat nasıl bulunur

* Randevular saat sırasıyla işlenir. Arama, aynı doktor için `blocked_until` anından başlar ve en çok 365 gün ileri gider.
* Kapalı günler, tatiller (tatil kapatma açıksa), geçmiş saatler, aynı partide başka randevuya ayrılmış saatler ve doktorun dolu olduğu saatler atlanır.
* Doktor dolu sayılır: aynı saatte başka bir `pending` veya `approved` randevusu varsa, o saate tutulan başka bir öneri varsa ya da doktorun bağlı Google Takvim veya Outlook takviminde o saatin ilk 15 dakikası doluysa.
* Saatler kliniğin randevu aralığı ızgarasına oturur.

## Taşıma isteği

`POST /v1/appointments/reschedule` isteği aynı üç alanı JSON gövdesinde alır.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "https://app.flextell.ai/api/v1/appointments/reschedule" \
    -H "Authorization: Bearer <access_token>" \
    -H "X-Tenant: <tenant_id>" \
    -H "Content-Type: application/json" \
    -d '{
      "user_id": 123,
      "blocked_from": "2026-10-20T09:00:00+03:00",
      "blocked_until": "2026-10-20T12:00:00+03:00"
    }'
  ```
</CodeGroup>

İstek eşzamanlı çalışır ve `202` döner. İsteğin sonunda her randevu için şunlar yazılmış olur:

* Önerilen yeni saat ve onay bekleme süresi (aşağıya bakın).
* Aralıktaki tüm randevuları tek partide toplayan bir `batch_id`.
* Hastaya gidecek onay mesajı. Mesajı istekten sonra kuyruktaki bir iş gönderir.

**Randevunun kendi saati, hasta onaylayana kadar değişmez.** Önerilen saat bu sürede "tutulmuş" sayılır: boş saat sorgusunda dolu görünür ve takvimde kilit olarak gösterilir. Bkz. [Randevu uygunluğu ve takvim](/api/kaynaklar/randevu-uygunlugu-ve-takvim).

Bekleme süresi, klinik randevu ayarlarındaki onay süresiyle belirlenir: 1–30 gün, varsayılan 3 gün. Ayara [Ayarlar](/api/kaynaklar/ayarlar) sayfasından ulaşırsınız.

Daha önceki bir partiden onay bekleyen bir randevu aralığa girerse yeniden önerilir. Eski onay isteği geçersiz sayılır ve yanıttaki `superseded` sayısına eklenir.

## Sonuç alanları

Başarılı yanıt şu şekildedir:

```json theme={null}
{
  "success": true,
  "data": {
    "processed": 1,
    "failed": 1,
    "skipped": 1,
    "superseded": 0,
    "batch_id": "0b6f6c3e-0000-0000-0000-000000000000",
    "items": [
      {
        "appointment_id": 456,
        "status": "processed",
        "was_pending": false,
        "original_at": "2026-10-20T10:00:00+03:00",
        "proposed_at": "2026-10-20T12:15:00+03:00"
      },
      {
        "appointment_id": 457,
        "status": "skipped",
        "reason_code": "CUSTOMER_ERASED",
        "reason": "<hata kodunun Türkçe mesajı>"
      },
      {
        "appointment_id": 458,
        "status": "failed",
        "reason": "No available slot found"
      }
    ]
  }
}
```

| Alan | Tür | Açıklama |
| - | - | - |
| `processed` | integer | Yeni saat önerilen ve onay mesajı kuyruğa alınan randevu sayısı. |
| `failed` | integer | İşlenemeyen randevu sayısı. |
| `skipped` | integer | Atlanan randevu sayısı. Verisi silinmiş hastanın randevuları yerinde kalır, hastaya mesaj gitmez. |
| `superseded` | integer | `processed` içinde, önceki bir partiden bekleyen önerisi yeniden önerilenlerin sayısı. |
| `batch_id` | string \| null | Parti kimliği (uuid). Aralıkta işlenecek randevu yoksa `null`. |
| `items` | array | Her randevunun sonucu. |

Aralıkta hiç randevu yoksa yanıt `{"processed": 0, "failed": 0, "skipped": 0, "items": [], "batch_id": null}` olur.

`items` satırı `status` değerine göre değişir.

| `status` | Ek alanlar | Anlamı |
| - | - | - |
| `processed` | `was_pending`, `original_at`, `proposed_at` | Öneri yazıldı. Saatler ISO 8601 biçimindedir. `was_pending`, randevunun önceden onay beklediğini gösterir. |
| `skipped` | `reason_code`, `reason` | Atlandı. `reason_code` şu an `CUSTOMER_ERASED`. |
| `failed` | `reason` | İşlenemedi. 365 gün içinde uygun saat yoksa `reason` değeri `No available slot found` olur. |

<Warning>
  `failed` ve `skipped` durumundaki randevuları sizin ele almanız gerekir. Bu randevulara otomatik yeniden deneme yapılmaz; randevuyu elle taşıyın veya hastayla iletişime geçin.
</Warning>

### Parti geçmişi

`GET /v1/appointments/reschedule/history` her parti için bir satır döner.

| Alan | Açıklama |
| - | - |
| `batch_id` | Parti kimliği. |
| `doctor`, `triggered_by` | Doktor ve işlemi başlatan kullanıcı. |
| `started_at` | Başlangıç zamanı. |
| `total` | Partideki randevu sayısı. |
| `confirmed_count` | Hastanın onayladığı randevu sayısı. |
| `in_flight_count` | Hâlâ yanıt bekleyen ve süresi dolmamış öneriler. |
| `expired_count` | Süresi dolmuş öneriler. |
| `status` | `in_progress` (`Devam ediyor`) veya `completed` (`Tamamlandı`). `in_flight_count` sıfırdan büyükse `in_progress`. |

Filtreler: `doctor_id`, `status`, `started_at_from`, `started_at_to` (klinik saatinde gün sınırları), `per_page` (en çok 100, varsayılan 15), `page`. Sıralama için `sort_by` değerleri: `started_at` (varsayılan, azalan), `total`, `confirmed_count`, `in_flight_count`, `expired_count`, `doctor_name`; yönü `sort_order` belirler. Genel kurallar için bkz. [Filtreleme ve sıralama](/api/istekler/filtreleme-ve-siralama).

`GET /v1/appointments/reschedule/history/{batchId}` parti alanlarına ek olarak `appointments` dizisi döner. Her satırda `appointment_id`, `is_appointment_deleted`, `customer`, `original_scheduled_at`, `proposed_scheduled_at` ve `status` (`value` ve `label`) bulunur. Satırlar en eski asıl saatten başlar. Başka bir kullanıcının lead'ine ait hasta bilgileri maskelenir.

| `status.value` | `status.label` | Anlamı |
| - | - | - |
| `pending` | Bekliyor | Hasta henüz yanıt vermedi. |
| `confirmed` | Onaylandı | Hasta onayladı, randevu yeni saate taşındı. |
| `rejected` | Reddetti — yeni öneri bekliyor | Hasta reddetti, yeni öneri gönderildi. |
| `expired` | Süresi doldu | Bekleme süresi içinde yanıt gelmedi. |
| `superseded` | Yeni kaydırma işlemi ile değiştirildi | Yeni bir parti veya elle yapılan değişiklik öneriyi geçersiz kıldı ya da randevu silindi. |

Yalnızca `View:Appointment` iznine sahip ve tüm randevuları görme izni olmayan bir kullanıcı, yalnızca kendi görebildiği randevuları içeren partileri görür. Sayılar da yalnızca bu satırları sayar. Görünür satırı olmayan bir partinin detayı `404 RESOURCE_NOT_FOUND` döner (`Kaydırma işlemi bulunamadı`).

## Hastaya giden onay

Her `processed` randevu için hastaya, randevunun kanalından (yoksa hastanın son aktif sohbet kanalından) **Randevu Taşıma Onay Talebi** şablonuyla mesaj gider. Mesaj onay gerektirmez, istekten hemen sonra kuyruğa alınır. Varsayılan metin:

```text theme={null}
Merhaba {customerName}, {tenantName} kliniğinizdeki {oldDate} saat {oldTime} randevunuz {newDate} saat {newTime} tarihine taşınmak isteniyor. Onaylıyor musunuz? (Evet / Hayır)
```

Hastanın yanıtı AI asistan tarafından yorumlanır:

* **Evet**: Randevu önerilen saate taşınır, randevu hatırlatmaları yeni saat için yeniden oluşturulur. Doktora "randevu zamanı değişti" bildirimi gitmez; asistanlara gider. Önerilen saat bu arada dolmuşsa hastaya bir sonraki boş saat önerilir.
* **Hayır**: Hastanın tercih ettiği tarih ve saatten (yoksa reddedilen önerinin bir dakika sonrasından) başlayarak yeni bir saat aranır ve yeni mesaj gider. Hasta bir günü hariç tutabilir.
* **Vazgeçme**: Randevu `cancelled` olur, iptal nedeni `Hasta randevu taşıma sürecinde randevudan vazgeçti.` yazılır ve iptal takibi açıksa ayrı bir iptal takibi kuyruğa girer. Personele **Hasta Randevudan Vazgeçti** bildirimi gider.
* **Uygun saat yok**: 365 gün içinde saat bulunamazsa tutulan saat serbest bırakılır ve personele **Taşıma için Uygun Slot Yok** bildirimi gider; hastayla elle iletişime geçmeniz gerekir.

Partideki son bekleyen öneri sonuçlandığında doktora, işlemi başlatan kullanıcıya ve klinikteki tüm asistanlara bir kez **Toplu Kaydırma Tamamlandı** bildirimi gider.

<Warning>
  AI yanıt modu kapalıysa veya sohbette AI asistan devre dışıysa hastanın yanıtı işlenmez. Bu durumda öneriyi elle sonuçlandırmanız gerekir.
</Warning>

Mesaj şablonlarını düzenlemek için bkz. [Mesaj şablonları](/api/kaynaklar/mesaj-sablonlari). Onay mesajları da dahil tüm otomatik mesajlar için [Randevu hatırlatmaları](/api/kaynaklar/randevu-hatirlatmalari) sayfasına bakın.

## Hatalar

Yanıt biçimi ve genel kodlar için bkz. [Hatalar](/api/istekler/hatalar).

| Kod | HTTP | Ne zaman |
| - | - | - |
| (kodsuz) | 403 | `BulkReschedule:Appointment` iznine (önizleme ve taşıma) veya `View:Appointment` iznine (geçmiş) sahip değilsiniz. |
| `LEAD_NOT_ASSIGNED` | 403 | Lead'lerinize göre sınırlı bir kullanıcının aralığında başka bir satış temsilcisinin lead'ine ait randevu var. Hiçbir randevu taşınmaz. |
| `RESOURCE_NOT_FOUND` | 404 | Geçmiş detayında parti yok, başka klinike ait ya da görebildiğiniz satırı yok. |
| `TENANT_HEADER_REQUIRED` | 400 | `X-Tenant` başlığı eksik. |
| (doğrulama) | 422 | Alanlar geçersiz. Mesajlar aşağıda. |

`422` doğrulama mesajları:

| Mesaj | Neden |
| - | - |
| `Geçmiş tarih seçilemez.` | `blocked_from` veya `blocked_until` bugünden önce. |
| `Bloke bitişi başlangıçtan sonra olmalıdır.` | `blocked_until`, `blocked_from` değerinden büyük değil. |
| `Seçilen doktor bu kliniğe ait değil.` | `user_id` kliniğin doktoru değil. |

Verisi silinmiş hastanın randevusu hata vermez; yanıtta `skipped` olarak listelenir.

Erişim kilitleri için bkz. [Erişim kilitleri](/api/istekler/erisim-kilitleri).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.