# İletim raporlarını sorgulama (Sandbox)

> Kimliği doğrulanmış hesabın iletim raporlarından bir sayfa.

- **İstek:** `GET https://ext-sandbox.netgsm.com.tr/sms/v3/report`
- **Sandbox:** `https://ext-sandbox.netgsm.com.tr/sms/v3`
- **Kimlik doğrulama:** `Authorization: Bearer <token>`
- **HTML sayfası:** https://api-docs.netgsm.com.tr/sandbox/read-reports/
- **OpenAPI tanımı:** https://api-docs.netgsm.com.tr/openapi/sandbox-v3.yaml (operationId: `readReports`)

## Açıklama

Kimliği doğrulanmış hesabın iletim raporlarından bir sayfa. Bir sayfada en fazla 100 rapor
bulunur.
Tüm raporlar için `count` 100'ün altında dönene kadar sonraki sayfalar istenebilir. Boş sayfa `count` 0 ile 200 döner.

Raporlar kaydedildikleri sırayla döner. Sayfalar gezilirken yeni raporlar eklenebilir; bu yüzden
aynı sorgunun sayfaları farklı zamanlarda istendiğinde sonuçlar değişebilir.

Gönderdiğiniz bir alıcı raporlarda yoksa iletim raporu henüz gelmemiştir: mesaj hâlâ
bekliyordur. Raporu daha sonra yeniden sorgulayabilirsiniz. Karşı operatör ya da son kullanıcı adresi kaynaklı nedenlerle
yeni gelen bir raporun yanıta yansıması 5 dakikayı bulabilir.

`startAt` ve `stopAt`, mesajın gönderildiği zamana uygulanır. İkisini birlikte göndermeli
ya da hiçbirini göndermemelisiniz. Hiçbiri gönderilmezse içinde bulunulan ayın bugüne kadarki kısmı, yani
ayın 1'i 00:00'dan (Europe/Istanbul) şimdiye kadar kullanılır. Aralık 1 ayı aşamaz. Aralık
dakika hassasiyetindedir: `startAt`'ın bulunduğu dakikanın başından `stopAt`'ın bulunduğu
dakikanın sonuna kadar gönderilen mesajlar döner. Saat dilimi farkındaki `+` işaretini URL'de
`%2B` olarak kodlamalısınız.

## İstek

### Parametreler

- `jobIds` (sorgu, array<string>, isteğe bağlı): Tekrarlanabilir ya da virgülle ayrılmış. Gönderilmezse aralıktaki tüm görevler döner. Örnek: `["179309912974612345062827"]`.
- `startAt` (sorgu, string, date-time, isteğe bağlı): Yalnızca `stopAt` ile birlikte. Örnek: `2026-08-01T00:00:00+03:00`.
- `stopAt` (sorgu, string, date-time, isteğe bağlı): Yalnızca `startAt` ile birlikte. Ondan önce ya da ondan 1 aydan fazla sonra olamaz. Örnek: `2026-08-31T23:59:59+03:00`.
- `page` (sorgu, integer, isteğe bağlı, en az 0): Varsayılan: `0`.

## Örnek istek

```bash
curl -X GET 'https://ext-sandbox.netgsm.com.tr/sms/v3/report?jobIds=179309912974612345062827&startAt=2026-08-01T00%3A00%3A00%2B03%3A00&stopAt=2026-08-31T23%3A59%3A59%2B03%3A00' \
  -H 'Authorization: Bearer <token>'
```

## Yanıtlar

### 200 — Raporlardan bir sayfa; boş olabilir.

Gövde (`application/json`):

- `count` (integer, zorunlu)
- `page` (integer, zorunlu)
- `reports` (array<object>, zorunlu)
- `reports[].jobId` (string, null olabilir, zorunlu)
- `reports[].number` (string, null olabilir, zorunlu): Alıcı numarası; yurt içi numaralar 90 öneki olmadan yazılır.
- `reports[].status` (string, null olabilir, zorunlu): İletim sonucu. Durum bilgisi yoksa `null`. Değerler: `PENDING`, `DELIVERED`, `NOT_DELIVERED`.

  | Değer | Anlamı |
  |---|---|
  | `PENDING` | Bekliyor |
  | `DELIVERED` | İletildi |
  | `NOT_DELIVERED` | İletilmedi; nedeni `failReason` alanındadır |

- `reports[].operator` (string, null olabilir, zorunlu): Mesajın gittiği operatör. VODAFONE, TURK_TELEKOM, TURKCELL, NETGSM, AKONTEL, yurt dışı numaralar için INTERNATIONAL; tanınmayan bir operatör için UNKNOWN. Operatör bilgisi yoksa `null`. Değerler: `VODAFONE`, `TURK_TELEKOM`, `TURKCELL`, `NETGSM`, `AKONTEL`, `INTERNATIONAL`, `UNKNOWN`.
- `reports[].messageLength` (integer, null olabilir, zorunlu): Mesajın kapladığı SMS parça sayısı.
- `reports[].deliveredAt` (string, date-time, null olabilir, zorunlu): İletildiği zaman. Yalnızca `status` DELIVERED olduğunda dolu; başarısızlar dahil diğer tüm satırlarda `null`.
- `reports[].failReason` (string, null olabilir, zorunlu): İletilmeme nedeni. Yalnızca `status` `NOT_DELIVERED` olduğunda dolu, aksi halde `null`. Değerler: `INVALID_NUMBER`, `NOT_SENT_TO_OPERATOR`, `OPERATOR_REJECTED`, `OPERATOR_ERROR`, `SEND_ERROR`, `DUPLICATE`, `INSUFFICIENT_CREDIT`, `BLACKLISTED`, `IYS_REJECTED`, `IYS_ERROR`, `MESSAGE_NOT_CREATED`, `INVALID_NUMBER_FORMAT`, `NTS_NOT_APPROVED`, `REPORT_NOT_FORWARDED`, `NO_INTERNATIONAL_PERMISSION`, `TIMEOUT`, `SMS_DISABLED_ON_DEVICE`, `DEVICE_NOT_SUPPORTED`, `ROUTING_FAILED`, `OPERATOR_TIMEOUT`, `UNKNOWN_SUBSCRIBER`, `ILLEGAL_SUBSCRIBER`, `SYSTEM_ERROR`, `UNKNOWN`.

  | Değer | Anlamı |
  |---|---|
  | `INVALID_NUMBER` | Hatalı numara. Yurt dışı bir numarada, hesabın yurt dışı gönderim izni olmadığı anlamına da gelebilir: izin gönderim sırasında denetlenir (`NO_INTERNATIONAL_PERMISSION`), ama kayıt kabul edildikten sonra izin kaldırılırsa kayıt burada bu kodla döner. |
  | `NOT_SENT_TO_OPERATOR` | Operatöre gönderilemedi |
  | `OPERATOR_REJECTED` | Operatör kabul etmedi |
  | `OPERATOR_ERROR` | Operatör hatası |
  | `SEND_ERROR` | Gönderim hatası |
  | `DUPLICATE` | Mükerrer gönderim |
  | `INSUFFICIENT_CREDIT` | Kredi yetersiz |
  | `BLACKLISTED` | Numara kara listede |
  | `IYS_REJECTED` | İYS reddi |
  | `IYS_ERROR` | İYS kontrolü yapılamadı |
  | `MESSAGE_NOT_CREATED` | Mesaj oluşturulamadı |
  | `INVALID_NUMBER_FORMAT` | Hatalı numara biçimi |
  | `NTS_NOT_APPROVED` | NTS onayı alınamadı |
  | `REPORT_NOT_FORWARDED` | Rapor iletilemedi |
  | `NO_INTERNATIONAL_PERMISSION` | Hesabın yurt dışı gönderim izni yok |
  | `TIMEOUT` | Zaman aşımı |
  | `SMS_DISABLED_ON_DEVICE` | Mobil cihaz SMS almaya kapalı |
  | `DEVICE_NOT_SUPPORTED` | Mobil cihaz desteklemiyor |
  | `ROUTING_FAILED` | Yönlendirme başarısız |
  | `OPERATOR_TIMEOUT` | Hedef operatörde zaman aşımı oluştu |
  | `UNKNOWN_SUBSCRIBER` | Tanımlanamayan abone |
  | `ILLEGAL_SUBSCRIBER` | Uyumsuz abone |
  | `SYSTEM_ERROR` | Sistemsel hata |
  | `UNKNOWN` | Tanımlı olmayan bir neden |

- `reports[].referenceId` (string, null olabilir, zorunlu): İletim raporunun referansı. Yoksa `null`.

Örnek:

```json
{
  "count": 2,
  "page": 0,
  "reports": [
    {
      "jobId": "179309912974612345062827",
      "number": "5xxxxxxxxx",
      "status": "DELIVERED",
      "operator": "VODAFONE",
      "messageLength": 1,
      "deliveredAt": "2026-08-31T12:34:56+03:00",
      "failReason": null,
      "referenceId": "8f3a1c"
    },
    {
      "jobId": "179309912974612345062827",
      "number": "5xxxxxxxxx",
      "status": "NOT_DELIVERED",
      "operator": "VODAFONE",
      "messageLength": 1,
      "deliveredAt": null,
      "failReason": "TIMEOUT",
      "referenceId": null
    }
  ]
}
```

### 400 — INVALID_REQUEST. Sınırlardan biri ISO-8601 değil ya da `page` sayı değil (`message` parametreyi belirtir); `page` negatif; `startAt` ve `stopAt`'tan yalnızca biri gönderilmiş; `stopAt`, `startAt`'tan önce; aralık 1 aydan geniş.

Gövde (`application/json`):

- `code` (string, zorunlu): Değerler: `INVALID_REQUEST`.
- `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 (şemadan üretildi):

```json
{
  "code": "INVALID_REQUEST",
  "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
}
```

### 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. SMS menüsü olmayan bir alt kullanıcı.

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
}
```

### 429 — RATE_LIMITED. Hesap için dakikada 60 istek aşıldı ya da istekteki bir görev numarası son bir dakika içinde sorulmuş; `message` hangisi olduğunu söyler.

Başlıklar:

- `Retry-After` (integer): Engelleyen sayacın süresi dolana kadar kalan 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. İstek daha sonra yeniden gönderilebilir.

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
}
```
