# Template ile SMS gönderme (Sandbox)

> Kayıtlı template'leri verilen parametrelerle doldurup gönderen bir görev oluşturur.

- **İstek:** `POST https://ext-sandbox.netgsm.com.tr/sms/v3/send/template`
- **Sandbox:** `https://ext-sandbox.netgsm.com.tr/sms/v3`
- **Kimlik doğrulama:** `Authorization: Bearer <token>`
- **HTML sayfası:** https://api-docs.netgsm.com.tr/sandbox/send-from-template/
- **OpenAPI tanımı:** https://api-docs.netgsm.com.tr/openapi/sandbox-v3.yaml (operationId: `sendFromTemplate`)

## Açıklama

Kayıtlı template'leri verilen parametrelerle doldurup gönderen bir görev oluşturur. Her kayıt bir
template, template'in parametreleri ve bir alıcıdır. Kullanılabilecek template'ler hesabın kendi
template'leri ve herkese açık template'lerdir; kimlikleri ve parametreleri `GET /templates` ile
listelenir. Doldurulan metinler `/send`'e verilmiş gibi işlenir ve yanıt `/send` yanıtıyla aynıdır.

**Toplu doğrulama.** Bir istekte en fazla 20 kayıt bulunur. `/send`'den farklı olarak template ile
ilgili hatalar isteğin tamamını reddeder ve hiçbir şey gönderilmez: eksik ya da geçersiz bir
`templateId` veya `recipient`, hesabın kullanamadığı bir template ya da template'e uymayan parametreler
400 döndürür. Template'ler doldurulduktan sonra kayıtlar `/send`'deki gibi tek tek değerlendirilir
(alıcı biçimi, metin uzunluğu, mükerrer alıcı). Hatalı kayıtlar `rejectedItems` içinde
`templates` dizisindeki konumlarıyla (`index`) bildirilir.

**İstek sınırı.** Hesap başına dakikada 1000 istek, `/send` sınırından ayrı. Pencere ilk istekle
başlar ve sabittir; reddedilen istekler de sayılır. Aşıldığında 429 `Retry-After` başlığıyla döner.

**Yeniden deneme.** `/send` ile aynıdır: aynı istek iki kez gönderilirse iki görev oluşur. 503
çoğunlukla hiçbir şeyin kuyruğa alınmadığını gösterir. Körlemesine yeniden göndermeden önce
`GET /report` ile kontrol edebilirsiniz.

## İstek

### Gövde (`application/json`, zorunlu)

- `sender` (string, zorunlu, en az uzunluk 1, en fazla uzunluk 35): Gönderici başlığı. Hesaba kayıtlı bir başlıkla yazıldığı gibi birebir eşleşmelidir: büyük/küçük harf dahil, kırpma yapılmaz. Alfanümerik bir başlık (örneğin `NETGSM`) ya da hesabın abone numarası olabilir; abone numarası başında `0` ya da `90` olmadan yazılır (`5061234567`). Kullanabileceğiniz başlıkları ve her birinin kullanılabilir olup olmadığını `GET /senders` ile görebilirsiniz. Eşleşmezse istek 422 `REJECTED_HEADER` ile reddedilir; boş ya da yalnızca boşluktan oluşan bir değer ise 400 ile reddedilir. Örnek: `NETGSM`.
- `templates` (array<object>, zorunlu, en az kayıt 1, en fazla kayıt 20): Gönderilecek kayıtlar; her alıcı için bir template. En az 1, en fazla 20 kayıt. Kayıtlardan biri hatalıysa (eksik alan, kullanılamayan template, uymayan parametre) istek 400 ile reddedilir.
- `templates[].templateId` (integer, int64, zorunlu, en az 1): Template'in kimliği. Hesabın kendi template'i ya da herkese açık bir template olmalıdır. Örnek: `3`.
- `templates[].recipient` (string, zorunlu, en fazla uzunluk 17, desen `^(90\d{10}|-\d{9,17})$`): Alıcı numarası, `/send`'deki `recipient` ile aynı biçimde: Türkiye için `90` ve ardından 10 rakam, yurt dışı için `-` ve ardından 9–17 rakam. Boş ya da 17 karakterden uzunsa istek 400 ile reddedilir; biçime uymayan kayıt template doldurulduktan sonra `INVALID_RECIPIENT` ile reddedilir. Örnek: `905061234567`.
- `templates[].params` (object, isteğe bağlı): Template'teki yer tutucuların değerleri: yer tutucu adı → değer. Template'in tanımladığı parametrelerin hepsini boş olmayan değerlerle vermeli, template'te olmayan bir ad vermemelisiniz. Yer tutucusu olmayan bir template'te alan verilmez. Değerler her zaman string olarak yazılır ve parametrenin tanımlı türüne uymalıdır:

  | Tür | Kabul edilen |
  |---|---|
  | `BIGINT` | Tam sayı, örn. `"123456"` |
  | `FLOAT` | En fazla 4 ondalık basamaklı sayı, örn. `"12.50"` |
  | `TEXT` | Template'te tanımlı uzunluğu aşmayan metin |
  | `DATE` | `dd.MM.yyyy` ya da `dd/MM/yyyy` |
  | `DATETIME` | `dd.MM.yyyy HH:mm[:ss]` ya da `dd/MM/yyyy HH:mm[:ss]` |

- `templates[].params.<anahtar>` (string, isteğe bağlı)
- `encoding` (string, isteğe bağlı): Metnin karakter kodlaması. Tüm kayıtlara uygulanır. Büyük/küçük harfe duyarlıdır; listede olmayan bir değer (`turkish`, `GSM` dahil) istek 400 `1 field(s) rejected` ile reddedilir; `violations` alanı (`encoding`) ve geçerli değerleri belirtir. Değerler: `DEFAULT`, `TURKISH`, `UNICODE`. Varsayılan: `DEFAULT`.

  | Değer | Ne zaman | En fazla uzunluk |
  |---|---|---|
  | `DEFAULT` | Standart GSM alfabesi. Türkçe karakterler Latin karşılıklarına çevrilir (ç→c, ş→s gibi). | 917 |
  | `TURKISH` | Türkçe karakterlerin (çğıöşüÇĞİÖŞÜ) korunması gerektiğinde. `çğışĞİŞ` iki karakter sayılır. | 888 |
  | `UNICODE` | Emoji ya da Arapça, Kiril gibi GSM alfabesinde olmayan karakterler için. | 396 |

  Alfanümerik bir gönderici başlığında her sınır 5 karakter azdır. Sınırı aşan kayıt
  `MESSAGE_TOO_LONG` ile reddedilir. Bir metnin kaç SMS'e bölüneceğini `POST /length` ile
  hesaplayabilirsiniz.

- `iysFilter` (string, isteğe bağlı): İYS (İleti Yönetim Sistemi) izin filtresi; ticari iletilerde alıcının izni olmayanlar gönderimden çıkarılır. Büyük/küçük harf fark etmez: Değerler: `NONE`, `BRAND`, `RETAILER`, `BRAND_AND_RETAILER`. Varsayılan: `NONE`.

  | Değer | Anlamı |
  |---|---|
  | `NONE` | Filtre yok. Yalnızca bilgilendirme (ticari olmayan) iletiler için. |
  | `BRAND` | Marka adına alınmış izinlere göre filtrelenir. |
  | `RETAILER` | Bayi adına alınmış izinlere göre filtrelenir. |
  | `BRAND_AND_RETAILER` | Her ikisine göre filtrelenir. |

  Bunların dışındaki her değer `NONE` sayılır. Hesap ticari gönderim (marka ya da bayi) tipinde
  tanımlıysa istekteki değer yok sayılır ve hesabın tipi uygulanır.

  `BRAND` ya da `RETAILER` uygulanacaksa göndericinin İYS marka kodu olmalıdır; hesap İYS
  modunda değilse bu iki değer istek 422 `REJECTED_IYS` ile reddedilir. `BRAND_AND_RETAILER`
  için bu iki kontrol yapılmaz.

- `startAt` (string, date-time, isteğe bağlı): En erken gönderim zamanı, saat dilimi farkıyla (`2026-09-10T09:00:00+03:00`). Verilmezse ya da geçmişteyse gönderim hemen başlar. 18 aydan daha ilerisi 400 `INVALID_SEND_WINDOW` ile reddedilir. Bir dakikadan daha ileri bir başlangıç görevi `SCHEDULED` yapar. Saat dilimi farkı olmayan ya da okunamayan bir tarih istek 400 ile reddedilir.
- `stopAt` (string, date-time, isteğe bağlı): En geç gönderim zamanı, saat dilimi farkıyla. Verilmezse ya da geçmişteyse `startAt` + 21 saat kabul edilir. Aralık, 1000 ve üzeri mesajda 10 dakikadan, daha azında 5 dakikadan kısaysa bu süreye uzatılır; 21 saatten uzunsa 21 saate kısaltılır. Bu düzeltmeler hata döndürmez ve belirlenen aralık yanıtta yer almaz.
- `partnerCode` (string, isteğe bağlı, en fazla uzunluk 32): İş ortağı (bayi) kodu. Yalnızca kayıt amaçlıdır; gönderimi etkilemez.
- `referenceId` (string, isteğe bağlı, en fazla uzunluk 50): Yanıtta aynen geri döndürülür.
- `appName` (string, isteğe bağlı, en fazla uzunluk 50): Gönderimi yapan uygulamanın adı. Yalnızca kayıt amaçlıdır; gönderimi etkilemez.

Örnek gövde:

```json
{
  "sender": "NETGSM",
  "templates": [
    {
      "templateId": 3,
      "recipient": "905061234567",
      "params": {
        "otpCode": "123456"
      }
    },
    {
      "templateId": 4,
      "recipient": "905061234568"
    }
  ],
  "encoding": "TURKISH",
  "referenceId": "order-4711"
}
```

## Örnek istek

```bash
curl -X POST 'https://ext-sandbox.netgsm.com.tr/sms/v3/send/template' \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{
  "sender": "NETGSM",
  "templates": [
    {
      "templateId": 3,
      "recipient": "905061234567",
      "params": {
        "otpCode": "123456"
      }
    },
    {
      "templateId": 4,
      "recipient": "905061234568"
    }
  ],
  "encoding": "TURKISH",
  "referenceId": "order-4711"
}'
```

## Yanıtlar

### 202 — Görev oluşturuldu. İletim sonradan yapılır.

Gövde (`application/json`):

- `jobId` (string, zorunlu): Oluşturulan görevin numarası. Rapor, istatistik ve iptal için kullanılır; destek taleplerinde belirtmelisiniz.
- `status` (string, zorunlu): Değerler: `QUEUED`, `SCHEDULED`.

  | Değer | Anlamı |
  |---|---|
  | `QUEUED` | Gönderim hemen başladı; görev iptal edilemez. |
  | `SCHEDULED` | Görev `startAt` zamanına kadar bekletiliyor (dakikaya göre bir dakikadan daha ileri bir başlangıç); o zamana kadar `POST /cancel` ile iptal edebilirsiniz. |

- `accepted` (integer, zorunlu): Gönderime alınan kayıt sayısı. `accepted + rejected`, istekteki kayıt sayısına eşittir.
- `rejected` (integer, zorunlu): Reddedilen kayıt sayısı. `rejectedItems` kısaltılsa da bu sayı tamdır.
- `rejectedItems` (array<object>, zorunlu, en fazla kayıt 100): Reddedilen kayıtlar, `index` sırasıyla ilk 100 tanesi. Hiçbiri reddedilmediyse boş dizi.
- `rejectedItems[].index` (integer, zorunlu): Kaydın istekteki `messages` dizisindeki sırası, sıfırdan başlar.
- `rejectedItems[].recipient` (string, null olabilir, zorunlu): Kayıttaki alıcı. Kayıt `null` ise ya da alıcısı yoksa `null`.
- `rejectedItems[].reason` (string, zorunlu): Değerler: `INVALID_RECIPIENT`, `INVALID_MESSAGE`, `MESSAGE_TOO_LONG`, `MALFORMED_ENTRY`, `DUPLICATE_RECIPIENT`, `NO_INTERNATIONAL_PERMISSION`.

  | Değer | Ne zaman |
  |---|---|
  | `INVALID_RECIPIENT` | Numara eksik ya da biçimi yanlış; bkz. `recipient`. |
  | `INVALID_MESSAGE` | Metin eksik ya da boş. |
  | `MESSAGE_TOO_LONG` | Metin, `encoding` ve gönderici türüne göre sınırı aşıyor; bkz. `encoding`. |
  | `MALFORMED_ENTRY` | Kayıt `null`. |
  | `DUPLICATE_RECIPIENT` | Aynı alıcı istekte daha sonra yeniden geçiyor. Yalnızca son geçen kayıt gönderilir, öncekiler bu nedenle reddedilir; son kayıt başka bir nedenle reddedilse bile. Numaralar yazıldığı gibi karşılaştırılır. Mükerrer gönderime izin verilen hesaplarda uygulanmaz. |
  | `NO_INTERNATIONAL_PERMISSION` | Numara yurt dışı (`-` ile başlıyor) ve hesabın yurt dışı gönderim izni yok. İzin için destek ekibiyle iletişime geçmelisiniz. Yalnızca başka bir hatası olmayan kayıtlarda bildirilir. |

  Bir kayıt birden fazla nedenle hatalıysa tek neden bildirilir; öncelik: `DUPLICATE_RECIPIENT`,
  `MALFORMED_ENTRY`, `MESSAGE_TOO_LONG`, `INVALID_RECIPIENT`, `INVALID_MESSAGE`,
  `NO_INTERNATIONAL_PERMISSION`.

- `rejectedItemsTruncated` (boolean, zorunlu): 100'den fazla kayıt reddedildiği için `rejectedItems` kısaltıldıysa `true`. `accepted` ve `rejected` yine tamdır.
- `referenceId` (string, null olabilir, zorunlu): İstekte verilen `referenceId`; verilmediyse `null`.

Örnek:

```json
{
  "jobId": "179309912974612345078627",
  "status": "QUEUED",
  "accepted": 2,
  "rejected": 0,
  "rejectedItems": [],
  "rejectedItemsTruncated": false,
  "referenceId": "order-4711"
}
```

### 400 — INVALID_REQUEST ya da INVALID_SEND_WINDOW (`startAt` 18 aydan daha ileride). INVALID_REQUEST için `message`:

| `message` | Anlamı |
|---|---|
| gövde doğrulaması, `violations` ile | Zorunlu bir alan eksik, 20'den fazla kayıt, bir kaydın `recipient` alanı boş ya da 17 karakterden uzun. `violations` alanı belirtir (`templates[0].recipient`). |
| `templates not found: [4]` | Template yok, silinmiş ya da başka bir hesaba ait. Bu durumlar birbirinden ayrılmaz. |
| `Invalid params for templateId 3. Missing params: [otpCode]` | Template'in bir parametresi verilmemiş ya da boş. |
| `Invalid params for templateId 3. Unexpected params: [code]` | Template'te olmayan bir parametre verilmiş. |
| `param 'otpCode' is not a valid BIGINT` | Değer, parametrenin tanımlı türüne ya da uzunluğuna uymuyor; `message` hangi parametre olduğunu belirtir. |

Gövde (`application/json`):

- `code` (string, zorunlu): Değerler: `INVALID_REQUEST`, `INVALID_SEND_WINDOW`.
- `message` (string, zorunlu): Okunabilir ayrıntı. `UNAUTHENTICATED` için `no bearer token` ya da `invalid or expired token`. `REJECTED_HEADER` için ret adıdır; yalnızca rakamlardan oluşan bir göndericide ` (subscriber number headers were not offered)` ekini taşıyabilir. `RATE_LIMITED` için sınırı belirtir; `FORBIDDEN` ve `REJECTED_IYS` için sabit bir cümledir. `SERVICE_UNAVAILABLE` ve `INTERNAL_ERROR` genel bir cümle taşır; bildirirken `requestId` değerini paylaşmanız gerekir.
- `requestId` (string, zorunlu): İsteğe özgü kimlik, 32 onaltılık karakter. Bir sorun bildirirken bu değeri paylaşmanız, isteğin kayıtlarda bulunmasını sağlar.
- `timestamp` (string, date-time, zorunlu)
- `violations` (array<object>, null olabilir, zorunlu): Yalnızca gövde doğrulamasından gelen INVALID_REQUEST için dolu, aksi halde `null`.
- `violations[].field` (string, isteğe bağlı)
- `violations[].message` (string, isteğe bağlı)
- `rejectedItems` (array<object>, isteğe bağlı, en fazla kayıt 100): Yalnızca `NO_DELIVERABLE_MESSAGES` yanıtında bulunur; diğer hatalarda alan hiç yer almaz. Reddedilen kayıtlar, `index` sırasıyla ilk 100 tanesi; 202 yanıtındaki `rejectedItems` ile aynıdır.
- `rejectedItems[].index` (integer, zorunlu): Kaydın istekteki `messages` dizisindeki sırası, sıfırdan başlar.
- `rejectedItems[].recipient` (string, null olabilir, zorunlu): Kayıttaki alıcı. Kayıt `null` ise ya da alıcısı yoksa `null`.
- `rejectedItems[].reason` (string, zorunlu): Değerler: `INVALID_RECIPIENT`, `INVALID_MESSAGE`, `MESSAGE_TOO_LONG`, `MALFORMED_ENTRY`, `DUPLICATE_RECIPIENT`, `NO_INTERNATIONAL_PERMISSION`.

  | Değer | Ne zaman |
  |---|---|
  | `INVALID_RECIPIENT` | Numara eksik ya da biçimi yanlış; bkz. `recipient`. |
  | `INVALID_MESSAGE` | Metin eksik ya da boş. |
  | `MESSAGE_TOO_LONG` | Metin, `encoding` ve gönderici türüne göre sınırı aşıyor; bkz. `encoding`. |
  | `MALFORMED_ENTRY` | Kayıt `null`. |
  | `DUPLICATE_RECIPIENT` | Aynı alıcı istekte daha sonra yeniden geçiyor. Yalnızca son geçen kayıt gönderilir, öncekiler bu nedenle reddedilir; son kayıt başka bir nedenle reddedilse bile. Numaralar yazıldığı gibi karşılaştırılır. Mükerrer gönderime izin verilen hesaplarda uygulanmaz. |
  | `NO_INTERNATIONAL_PERMISSION` | Numara yurt dışı (`-` ile başlıyor) ve hesabın yurt dışı gönderim izni yok. İzin için destek ekibiyle iletişime geçmelisiniz. Yalnızca başka bir hatası olmayan kayıtlarda bildirilir. |

  Bir kayıt birden fazla nedenle hatalıysa tek neden bildirilir; öncelik: `DUPLICATE_RECIPIENT`,
  `MALFORMED_ENTRY`, `MESSAGE_TOO_LONG`, `INVALID_RECIPIENT`, `INVALID_MESSAGE`,
  `NO_INTERNATIONAL_PERMISSION`.

- `rejectedItemsTruncated` (boolean, isteğe bağlı): Yalnızca `rejectedItems` ile birlikte bulunur. 100'den fazla kayıt reddedildiğinde true.

Örnek:

```json
{
  "code": "INVALID_REQUEST",
  "message": "templates not found: [4]",
  "requestId": "3b9e0c1d2f4a4b6c8d0e1f2a3b4c5d6e",
  "timestamp": "2026-10-30T14:05:12.481+03:00",
  "violations": null
}
```

### 401 — UNAUTHENTICATED. Bearer token yok ya da token geçersiz veya süresi dolmuş; `message` `no bearer token` ya da `invalid or expired token`.

Gövde (`application/json`): alanlar 400 yanıtıyla aynı; farklı olanlar:

- `code` (string, zorunlu): Değerler: `UNAUTHENTICATED`.

Örnek (şemadan üretildi):

```json
{
  "code": "UNAUTHENTICATED",
  "message": "string",
  "requestId": "string",
  "timestamp": "2026-01-01T10:00:00+03:00",
  "violations": [
    {
      "field": "string",
      "message": "string"
    }
  ],
  "rejectedItems": [
    {
      "index": 0,
      "recipient": "string",
      "reason": "INVALID_RECIPIENT"
    }
  ],
  "rejectedItemsTruncated": true
}
```

### 403 — FORBIDDEN. Hesap bu gönderimi yapamaz; `message` şunlardan biridir:

| `message` | Anlamı |
|---|---|
| `account is not activated for this operation` | Hesapta SMS gönderimi etkin değil. |
| `sub user may not use the sms api` | Alt kullanıcının SMS API'sini kullanma izni yok. |
| `account operator is not served for this operation` | Hesabın operatörüne bu uç noktadan hizmet verilmiyor. |

Gövde (`application/json`): alanlar 400 yanıtıyla aynı; farklı olanlar:

- `code` (string, zorunlu): Değerler: `FORBIDDEN`.

Örnek (şemadan üretildi):

```json
{
  "code": "FORBIDDEN",
  "message": "string",
  "requestId": "string",
  "timestamp": "2026-01-01T10:00:00+03:00",
  "violations": [
    {
      "field": "string",
      "message": "string"
    }
  ],
  "rejectedItems": [
    {
      "index": 0,
      "recipient": "string",
      "reason": "INVALID_RECIPIENT"
    }
  ],
  "rejectedItemsTruncated": true
}
```

### 422 — İstek geçerli ama gönderim yapılamaz. `code` ve `message`:

| `code` | `message` | Anlamı ve çözüm |
|---|---|---|
| `REJECTED_HEADER` | `NO_HEADERS_REGISTERED` | Hesapta kayıtlı başlık yok. |
| `REJECTED_HEADER` | `NOT_REGISTERED` | `sender` hesaba kayıtlı değil. Kayıtlı başlıkları `GET /senders` ile görebilirsiniz. |
| `REJECTED_HEADER` | `CASE_MISMATCH` | Başlık kayıtlı ama büyük/küçük harfi farklı yazılmış; kayıtlı hâliyle göndermelisiniz. |
| `REJECTED_HEADER` | `ROUTING_CODE_MISSING` | Alfanümerik başlık için hesapta yönlendirme kodu tanımlı değil. |
| `REJECTED_HEADER` | `SUBSCRIBER_NUMBER_RESTRICTED` | Hesapta abone numarasından gönderim kısıtlı. |
| `REJECTED_IYS` | `sender header has no iys brand code` | `BRAND` ya da `RETAILER` filtresi uygulanacak ama başlığın İYS marka kodu yok. |
| `REJECTED_IYS` | `account is not in iys mode, filtering cannot be applied` | Hesap İYS modunda değil; `iysFilter` `BRAND` ya da `RETAILER` olamaz. |
| `NO_DELIVERABLE_MESSAGES` | `all N messages rejected` | Doldurulan kayıtların hepsi reddedildi; görev oluşturulmadı. Hangi kaydın neden reddedildiği `rejectedItems` içindedir. |

Abone numarasından gönderime onaylı olmayan bir hesapta, tamamı rakamlardan oluşan bir `sender`
için `NOT_REGISTERED` ve `NO_HEADERS_REGISTERED` mesajlarının sonuna
` (subscriber number headers were not offered)` eklenir.

Gövde (`application/json`): alanlar 400 yanıtıyla aynı; farklı olanlar:

- `code` (string, zorunlu): Değerler: `REJECTED_HEADER`, `REJECTED_IYS`, `NO_DELIVERABLE_MESSAGES`.

Örnek (şemadan üretildi):

```json
{
  "code": "REJECTED_HEADER",
  "message": "string",
  "requestId": "string",
  "timestamp": "2026-01-01T10:00:00+03:00",
  "violations": [
    {
      "field": "string",
      "message": "string"
    }
  ],
  "rejectedItems": [
    {
      "index": 0,
      "recipient": "string",
      "reason": "INVALID_RECIPIENT"
    }
  ],
  "rejectedItemsTruncated": true
}
```

### 429 — RATE_LIMITED, `message` `template send rate limit exceeded`. Hesap başına dakikada 1000 istek aşıldı; `Retry-After` kadar bekleyip yeniden denemelisiniz.

Başlıklar:

- `Retry-After` (integer): Yeniden denemeden önce beklenecek saniye.

Gövde (`application/json`): alanlar 400 yanıtıyla aynı; farklı olanlar:

- `code` (string, zorunlu): Değerler: `RATE_LIMITED`.

Örnek (şemadan üretildi):

```json
{
  "code": "RATE_LIMITED",
  "message": "string",
  "requestId": "string",
  "timestamp": "2026-01-01T10:00:00+03:00",
  "violations": [
    {
      "field": "string",
      "message": "string"
    }
  ],
  "rejectedItems": [
    {
      "index": 0,
      "recipient": "string",
      "reason": "INVALID_RECIPIENT"
    }
  ],
  "rejectedItemsTruncated": true
}
```

### 500 — INTERNAL_ERROR, `message` `internal error`. Beklenmeyen hata; bildirirken `requestId` değerini paylaşmanız gerekir.

Gövde (`application/json`): alanlar 400 yanıtıyla aynı; farklı olanlar:

- `code` (string, zorunlu): Değerler: `INTERNAL_ERROR`.

Örnek (şemadan üretildi):

```json
{
  "code": "INTERNAL_ERROR",
  "message": "string",
  "requestId": "string",
  "timestamp": "2026-01-01T10:00:00+03:00",
  "violations": [
    {
      "field": "string",
      "message": "string"
    }
  ],
  "rejectedItems": [
    {
      "index": 0,
      "recipient": "string",
      "reason": "INVALID_RECIPIENT"
    }
  ],
  "rejectedItemsTruncated": true
}
```

### 503 — SERVICE_UNAVAILABLE. Servis geçici olarak kullanılamıyor. Görev çoğunlukla kuyruğa alınmamıştır. Toplu gönderimi körlemesine yeniden göndermeden önce `GET /report` ile kontrol edebilirsiniz.

Gövde (`application/json`): alanlar 400 yanıtıyla aynı; farklı olanlar:

- `code` (string, zorunlu): Değerler: `SERVICE_UNAVAILABLE`.

Örnek (şemadan üretildi):

```json
{
  "code": "SERVICE_UNAVAILABLE",
  "message": "string",
  "requestId": "string",
  "timestamp": "2026-01-01T10:00:00+03:00",
  "violations": [
    {
      "field": "string",
      "message": "string"
    }
  ],
  "rejectedItems": [
    {
      "index": 0,
      "recipient": "string",
      "reason": "INVALID_RECIPIENT"
    }
  ],
  "rejectedItemsTruncated": true
}
```
