# Gönderim istatistiklerini sorgulama (Sandbox)

> Kimliği doğrulanmış hesabın bir görevi için, gönderildiği ay üzerinden toplanmış, her farklı sonuç başına bir satır.

- **İstek:** `GET https://ext-sandbox.netgsm.com.tr/sms/v3/stats`
- **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-stats/
- **OpenAPI tanımı:** https://api-docs.netgsm.com.tr/openapi/sandbox-v3.yaml (operationId: `readStats`)

## Açıklama

Kimliği doğrulanmış hesabın bir görevi için, gönderildiği ay üzerinden toplanmış, her farklı
sonuç başına bir satır. Ay varsayılan olarak içinde bulunulan aydır. Satır yoksa 404
NOT_FOUND döner - bilinmeyen bir görev, başka bir hesabın görevi ve yanlış ay aynı görünür.
200 yanıtında her zaman en az bir satır bulunur.

## İstek

### Parametreler

- `jobId` (sorgu, string, zorunlu): Örnek: `179309912974612345062827`.
- `month` (sorgu, string, isteğe bağlı, desen `^\d{4}-\d{2}$`): Görevin gönderildiği ay. Varsayılan, içinde bulunulan ay. Örnek: `2026-09`.

## Örnek istek

```bash
curl -X GET 'https://ext-sandbox.netgsm.com.tr/sms/v3/stats?jobId=179309912974612345062827&month=2026-09' \
  -H 'Authorization: Bearer <token>'
```

## Yanıtlar

### 200 — Görevin toplamları; en az bir satır.

Gövde (`application/json`):

- `jobId` (string, zorunlu)
- `month` (string, zorunlu): Aranan ay, yyyy-MM.
- `count` (integer, zorunlu)
- `stats` (array<object>, zorunlu, en az kayıt 1)
- `stats[].status` (string, null olabilir, zorunlu): Sonuç; `GET /report` yanıtındaki `status` ile aynı değerler. Değerler: `PENDING`, `DELIVERED`, `NOT_DELIVERED`.
- `stats[].failReason` (string, null olabilir, zorunlu): İletilmeme nedeni. Yalnızca `status` `NOT_DELIVERED` olduğunda dolu, aksi halde `null`. Değerler `GET /report` yanıtındaki `failReason` ile aynı adlardır, ancak istatistiklerde yalnızca bu listedekiler görülür. Tanınmayan bir neden `UNKNOWN` olarak döner. Değerler: `INVALID_NUMBER`, `NOT_SENT_TO_OPERATOR`, `OPERATOR_REJECTED`, `OPERATOR_ERROR`, `SEND_ERROR`, `DUPLICATE`, `INSUFFICIENT_CREDIT`, `BLACKLISTED`, `IYS_REJECTED`, `MESSAGE_NOT_CREATED`, `TIMEOUT`, `UNKNOWN`.

  Aynı neden birden fazla satırda yer alabilir: satırlar, ücretlendirmenin yanı sıra iç sonuç koduna göre
  de ayrılır ve birden fazla iç kod aynı nedene karşılık gelebilir (örneğin iki ayrı `INVALID_NUMBER`
  satırı, ikisi de `charged`). Neden başına toplam için aynı `failReason` değerini taşıyan
  satırların `messageCount` değerlerini toplamalısınız.

- `stats[].messageCount` (integer, null olabilir, zorunlu): Bu şekilde sonuçlanan alıcı sayısı.
- `stats[].totalParts` (integer, int64, null olabilir, zorunlu): Bu alıcıların kapladığı SMS parçalarının toplamı.
- `stats[].domestic` (boolean, zorunlu)
- `stats[].charging` (string, null olabilir, zorunlu): Ücretlendirme durumu - `charged`, `refunded` ya da `uncharged`. Örnek: `charged`.
- `stats[].chargeDescription` (string, null olabilir, zorunlu): Ücretlendirme hakkında bir cümle. Hiçbir ücret alınmadıysa `null`.

Örnek:

```json
{
  "jobId": "179309912974612345062827",
  "month": "2026-09",
  "count": 2,
  "stats": [
    {
      "status": "DELIVERED",
      "failReason": null,
      "messageCount": 4,
      "totalParts": 4,
      "domestic": true,
      "charging": "charged",
      "chargeDescription": "1.26 TL from: credit balance"
    },
    {
      "status": "NOT_DELIVERED",
      "failReason": "INVALID_NUMBER",
      "messageCount": 1,
      "totalParts": 0,
      "domestic": true,
      "charging": "uncharged"
    }
  ]
}
```

### 400 — INVALID_REQUEST. `jobId` eksik ya da boş veya `month` yyyy-MM değil; `message` parametreyi belirtir.

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

### 404 — NOT_FOUND, `message` `job not found`. Bu görev için ayda satır yok - bilinmeyen kimlik, başka bir hesabın görevi ya da farklı bir ayda gönderilmiş. Bu durumlar birbirinden ayrılmaz.

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

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

Örnek (şemadan üretildi):

```json
{
  "code": "NOT_FOUND",
  "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 bu görev numarası son 10 dakika içinde sorulmuş; `message` hangisi olduğunu söyler. İkisi de okuma sonunda 404 ya da 503 ile bitse bile sayılır.

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. Hiçbir şey yapılmadı; yeniden denemek güvenlidir.

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