# Bakiye sorgulama

> Hesabın kalan paketlerini, kampanyalarını ve kredi bakiyesini döner.

- **İstek:** `GET https://ext-api.netgsm.com.tr/sms/v3/balance`
- **Canlı ortam:** `https://ext-api.netgsm.com.tr/sms/v3`
- **Kimlik doğrulama:** `Authorization: Bearer <token>`
- **HTML sayfası:** https://api-docs.netgsm.com.tr/get-balance/
- **OpenAPI tanımı:** https://api-docs.netgsm.com.tr/openapi/sms-v3.yaml (operationId: `getBalance`)

## Açıklama

Hesabın kalan paketlerini, kampanyalarını ve kredi bakiyesini döner. Hesap token'dan alınır.
Yanıt gövdesi API'nin genel kurallarına uymaz: `amount` metin ya da sayı olabilir,
kredi virgüllü metindir ve `stip=2` yanıtında bazı alanlar hiç bulunmayabilir.

Geçerli bir token yeterlidir; ayrıca bir yetki istenmez.

**Önbellek.** Aynı hesap, aynı `stip` ve aynı `service` için yanıt 10 saniye saklanır; bir
gönderimden hemen sonra okunan bakiye en fazla 10 saniye eski olabilir.

**İstek sınırı.** Hesap başına dakikada 60 istek. Pencere ilk istekle başlar ve sabittir; her
istek sayılır. Aşıldığında 429 `Retry-After` başlığıyla döner.

## İstek

### Parametreler

- `stip` (sorgu, integer, isteğe bağlı): `1`: varlıklar türlerine göre. `2`: tek satırlık özet. `3`: varlıklar ve kredi bakiyesi. Başka bir değer ya da hiç verilmemesi `1` sayılır; hata dönmez. Değerler: `1`, `2`, `3`. Varsayılan: `1`.
- `service` (sorgu, string, isteğe bağlı): Listeyi bir hizmet grubuyla sınırlar: yalnızca o gruptaki hizmetlerin paket ve kampanyaları döner. Verilmezse liste SMS gönderim hizmetinin varlıklarıdır. Büyük/küçük harf fark etmez. Kredi bakiyesi bir hizmete ait değildir; listede yer almaz, `stip=3` onu her zaman sona ekler. `stip=2` özeti bu parametreden etkilenmez. Değerler: `sms`, `otp`, `ses`, `fax`, `whatsapp`, `santral`, `netsipp`, `gsm`, `internet`, `numara`, `ai`, `ucret`.

  | Değer | Grup |
  |---|---|
  | `sms` | SMS (giden, gelen, SMPP, flash, kısa numara, 0800 SMS, GSM ve M2M SMS); OTP SMS paketleri `otp`'dadır |
  | `otp` | OTP ve OTP SMS paketleri |
  | `ses` | Ses (giden, gelen, sesli mesaj, yönlendirme, telesekreter, 0800 çağrı, otomatik arama) |
  | `fax` | Faks (giden, gelen) |
  | `whatsapp` | WhatsApp |
  | `santral` | Santral, SIP / NGN trunk, IP FCT, THK |
  | `netsipp` | Netsipp |
  | `gsm` | GSM hat, data, M2M, NetIOT, SIM kart |
  | `internet` | İnternet, statik IP, alan adı, sunucu barındırma |
  | `numara` | Numara kullanımı, özel numara, numara devri, MSISDN sorgulama |
  | `ai` | Yapay zeka entegrasyonları |
  | `ucret` | Tek seferlik ücretler ve fatura işlemleri |

## Örnek istek

```bash
curl -X GET 'https://ext-api.netgsm.com.tr/sms/v3/balance' \
  -H 'Authorization: Bearer <token>'
```

## Yanıtlar

### 200 — `stip` 1 ve 3 için `BalanceListResponse`, `stip` 2 için `BalanceSummaryResponse`.

Gövde (`application/json`):

- oneOf → **BalanceListResponse**
- `balance` (array<object>, zorunlu): Her varlık türü için bir kayıt. Hiç varlık yoksa boş liste. `stip=3` sonuna her zaman `Kredi Bakiye` kaydını ekler.
- `balance[].amount` (string | number, zorunlu): Kalan miktar. Metin ya da sayı olabilir: bir türün tek kaydı varsa metin (`"767"`), birden fazla kaydı toplanmışsa sayı (`277`, `242.5`). `Kredi Bakiye` kaydında TL cinsinden, virgülle ve üç ondalık basamakla metin (`"92,150"` = 92,15 TL).
- `balance[].amount` oneOf → **seçenek 1**
- `balance[].amount` oneOf → **seçenek 2**
- `balance[].balance_name` (string, zorunlu): Varlığın adı, örneğin `Adet SMS`, `Adet OTP SMS`, `Dakika`, `Kredi Bakiye`. Liste sabit değildir; hesaptaki ürünlere göre değişir.
- oneOf → **BalanceSummaryResponse**: `stip=2` özeti. `balance` ve `type` yalnızca ilgili durumda bulunur.
- `code` (string, zorunlu): Değerler: `00`.
- `balance` (string, isteğe bağlı): Faturasız hesapta kredi varsa TL kredi (`"92,150"`), yoksa kalan SMS adedi (`"277"`, `type` ile birlikte). Faturalı hesapta her zaman `"0"`. Faturasız hesapta ikisi de yoksa bulunmaz.
- `type` (string, isteğe bağlı): Yalnızca `balance` SMS adedi olduğunda bulunur. Değerler: `AdetSMS`.

Örnek: Varlıklar ve kredi (stip=3)

```json
{
  "balance": [
    {
      "amount": 277,
      "balance_name": "Adet SMS"
    },
    {
      "amount": "767",
      "balance_name": "Adet OTP SMS"
    },
    {
      "amount": "242",
      "balance_name": "Dakika"
    },
    {
      "amount": "92,150",
      "balance_name": "Kredi Bakiye"
    }
  ]
}
```

Örnek: Özet, faturasız hesap, kredi var (stip=2)

```json
{
  "code": "00",
  "balance": "92,150"
}
```

Örnek: Özet, faturasız hesap, kredi yok, SMS var (stip=2)

```json
{
  "code": "00",
  "balance": "277",
  "type": "AdetSMS"
}
```

Örnek: Özet, faturalı hesap (stip=2)

```json
{
  "code": "00",
  "balance": "0"
}
```

### 400 — INVALID_REQUEST, `message` `service must be one of sms, otp, ses, ...`. `service` tabloda olmayan bir değer.

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

### 429 — RATE_LIMITED, `message` `balance rate limit exceeded`. Hesap başına dakikada 60 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. 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
}
```
