HesapOn Web API Dokümantasyonu

Mobil uygulama ve son kullanıcı entegrasyonları için REST API. Tüm iş uçları JWT Bearer ile korunur; firma bağlamı token içindeki company_code claim’inden okunur. Yetki modeli HesapOn Web ile aynıdır (paket özellikleri + menü izinleri).

Base: /api/v1 JSON JWT Bearer Token ömrü: 8 saat

Yetkilendirme

Önce login ile token alın. Birden fazla firma varsa select-company ile firma seçin; dönen yeni token’ı kullanın. Sonraki isteklerde header zorunludur:

Header
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Accept: application/json
Store kullanıcısı: yalnızca Auth, Companies ve StorePos uçlarına erişebilir. Paket/modül yoksa veya menü izni yoksa API 403 döner: {"error":"..."}

Hata formatı

Hata gövdesi genelde tek alanlıdır:

Örnek 401 / 403 / 400
{
  "error": "E-posta veya şifre hatalı."
}
HTTPAnlam
400Eksik/geçersiz istek gövdesi veya query
401Token yok/geçersiz veya kimlik doğrulama başarısız
403Paket, menü veya firma erişim yetkisi yok
404Kayıt bulunamadı
409Çakışma (ör. aynı stok kodu)

Sayfalama

Liste uçları ortak yapı kullanır:

PagedResult
{
  "page": 1,
  "pageSize": 25,
  "total": 120,
  "items": [ /* kayıtlar */ ]
}

page ≥ 1, pageSize 1–100 arası.

Auth

POST /api/v1/auth/login

E-posta ve şifre ile giriş. Auth header gerekmez. Tek firma veya son seçilen firma varsa token’a otomatik eklenir.

Request body

AlanTipAçıklama
emailstringzorunluKullanıcı e-posta
passwordstringzorunluŞifre

Örnek istek

JSON
{
  "email": "[email protected]",
  "password": "********"
}

Örnek yanıt · 200

JSON
{
  "accessToken": "eyJhbGciOi...",
  "tokenType": "Bearer",
  "expiresInSeconds": 28800,
  "requiresCompanySelection": false,
  "user": {
    "id": "a1b2c3d4-....",
    "username": "kasiyer",
    "email": "[email protected]",
    "adSoyad": "Ayşe Yılmaz",
    "role": "CompanyUser",
    "companyUserKind": "Full"
  },
  "selectedCompany": {
    "id": "....",
    "code": "ABC01",
    "name": "Demo Ticaret A.Ş."
  },
  "companies": [
    { "id": "....", "code": "ABC01", "name": "Demo Ticaret A.Ş." }
  ]
}
POST /api/v1/auth/select-company

Aktif firmayı seçer ve company_code claim’li yeni token döner. Bearer gerekir.

Request body

AlanTipAçıklama
companyCodestringzorunluFirma kodu (ör. ABC01)

Örnek istek

JSON
{ "companyCode": "ABC01" }

Örnek yanıt · 200

LoginResponse ile aynı şema; requiresCompanySelection: false, selectedCompany dolu, yeni accessToken.

GET /api/v1/auth/me

Oturumdaki kullanıcı ve token’daki seçili firma.

Örnek yanıt · 200

JSON
{
  "user": { "id": "...", "email": "...", "role": "CompanyUser", "companyUserKind": "Store" },
  "selectedCompany": { "code": "ABC01", "name": "Demo Ticaret A.Ş." },
  "companyUserKind": "Store"
}
GET /api/v1/companies

Kullanıcının erişebildiği aktif firmalar listesi.

Örnek yanıt · 200

JSON
[
  { "id": "....", "code": "ABC01", "name": "Demo Ticaret A.Ş." },
  { "id": "....", "code": "XYZ02", "name": "Şube 2" }
]

Stok

Paket: stock.core · Menü: ürünler. Firma seçili token gerekir.

GET /api/v1/stock-items

Ürün listesi (sayfalı, aramalı).

Query parametreleri

ParametreTipAçıklama
qstringopsiyonelKod veya ad içinde arama
pageintvarsayılan 1Sayfa
pageSizeintvarsayılan 25Sayfa boyutu (max 100)
activeOnlyboolvarsayılan trueSadece aktif ürünler

Örnek yanıt · 200

JSON
{
  "page": 1,
  "pageSize": 25,
  "total": 2,
  "items": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "code": "URN-001",
      "name": "Defter A5",
      "unitPrice": 45.5,
      "currencyCode": "TRY",
      "vatRate": 20,
      "isActive": true,
      "imageUrl": "/images/ABC01/....jpg",
      "categoryId": null,
      "unitId": "...."
    }
  ]
}
GET /api/v1/stock-items/{id}

Ürün detayı. Path: id (guid).

Örnek yanıt · 200

JSON
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "code": "URN-001",
  "name": "Defter A5",
  "unitPrice": 45.5,
  "currencyCode": "TRY",
  "vatRate": 20,
  "isActive": true,
  "description": "Çizgili",
  "grammage": null,
  "grammageUnit": null,
  "variantType": 0,
  "createdAtUtc": "2026-09-01T10:00:00Z"
}
POST /api/v1/stock-items

Yeni ürün kartı. Aynı code varsa 409. variantType’a göre renk / beden varyantları oluşturulur.

variantType: 0 Normal (tek satır), 1 Renk (≥1 renk), 2 Renk+beden (≥1 renk ve ≥1 beden).
Renk: colorIds veya colorCodes. Beden: dimensionIds veya dimensionNames (S, M, L…).
SKU otomatik: {code}-{renk}-{beden}. Barkod bu uçta atanmaz.

Request body

AlanTipAçıklama
codestringzorunluStok kodu
namestringzorunluÜrün adı
unitPricedecimal0+Birim fiyat
currencyCodestringTRY3 harf
vatRatedecimal?KDV oranı
descriptionstring?Açıklama
unitId / categoryIdguid?Birim / kategori
grammage / grammageUnitGramaj (G, KG…)
imageUrlstring?Görsel URL
variantTypebyte00 / 1 / 2
colorIds / colorCodesarray*Tip 1–2’de zorunlu
dimensionIds / dimensionNamesarray*Tip 2’de zorunlu

Örnek istek · renk + beden

JSON
{
  "code": "TSHIRT-01",
  "name": "Basic Tişört",
  "unitPrice": 299.9,
  "vatRate": 20,
  "variantType": 2,
  "colorCodes": ["1001", "1002"],
  "dimensionNames": ["S", "M", "L"]
}

Örnek yanıt · 201

Ürün + variants[] (sku, colorCode, dimensionName…). Location: /api/v1/stock-items/{id}.

PUT /api/v1/stock-items/{id}

Ürün güncelleme. Body alanları POST ile aynı; ek olarak isActive (bool?) gönderilebilir. Dosya yüklemek için aşağıdaki images ucunu kullanın.

POST /api/v1/stock-items/{id}/images

Ürün görseli yükler. Media:StorageRoot = Web wwwroot yolu; dosya {StorageRoot}/images/{firmaKodu}/ altına yazılır (Web ile aynı disk). DB’de göreli yol; yanıtta PublicBaseUrl ile mutlak URL.

Content-Type: multipart/form-data — JSON değil. Form alanı adı: file. Opsiyonel: setAsPrimary=true.

Örnek (curl)

curl
curl -X POST "https://api.hesapon.com/api/v1/stock-items/{id}/images" \
  -H "Authorization: Bearer TOKEN" \
  -F "file=@./urun.jpg" \
  -F "setAsPrimary=true"

Örnek yanıt · 200

JSON
{
  "stockItemId": "....",
  "imageUrl": "https://portal.hesapon.com/images/ABC01/a1b2....jpg",
  "relativePath": "/images/ABC01/a1b2....jpg",
  "isPrimary": true
}

Renkler ve bedenler

Paket: stock.core. Ürün oluştururken colorCodes / dimensionNames kullanmadan önce burada tanımlayın.

GET /api/v1/colors

Query

ParametreTipAçıklama
qstringKod / ad
activeOnlybooltrue
page / pageSizeint100Max 200

Örnek yanıt · 200

JSON
{
  "items": [
    {
      "id": "....",
      "code": "1001",
      "name": "Kırmızı",
      "hexCode": "#FF0000",
      "displayInList": true,
      "isActive": true
    }
  ]
}
POST /api/v1/colors

code ve name zorunlu. Aynı aktif kod varsa 409.

Örnek istek

JSON
{
  "code": "1001",
  "name": "Kırmızı",
  "hexCode": "#FF0000"
}
GET /api/v1/dimensions

Firma stok ayarına göre varsayılan mod Size (beden) veya Measurement (ölçü). type ile override edilebilir.

Query

ParametreTipAçıklama
typestringfirma ayarıSize | Measurement
qstringAd araması

Örnek yanıt · 200

JSON
{
  "dimensionMode": "Size",
  "items": [
    { "id": "....", "name": "S", "sortOrder": 10 },
    { "id": "....", "name": "M", "sortOrder": 20 }
  ]
}
POST /api/v1/dimensions

Beden modunda name zorunlu (S, M, L…). Ölçü modunda width/height/depth zorunlu; ad otomatik üretilir.

Örnek istek · beden

JSON
{
  "name": "L",
  "sortOrder": 30
}

Cari hesaplar

Paket: customercards.core.

GET /api/v1/customer-accounts

Query parametreleri

ParametreTipAçıklama
qstringAd, unvan, VKN/TCKN, e-posta
accountTypestringÖr. Customer, Supplier
includeRetailboolfalsePerakende carileri dahil et
page / pageSizeintSayfalama

Örnek yanıt · 200

JSON
{
  "page": 1,
  "pageSize": 25,
  "total": 1,
  "items": [
    {
      "id": "....",
      "name": "Örnek Müşteri",
      "title": "Örnek Ltd.",
      "taxIdNumber": "1234567890",
      "accountTypes": "Customer",
      "balance": 1500.00,
      "currency": "TRY",
      "phone": "0532...",
      "email": "[email protected]",
      "city": "İstanbul",
      "isRetail": false
    }
  ]
}
GET /api/v1/customer-accounts/{id}

Detayda ek alanlar: adres, vergi dairesi, ad-soyad, isActive, createdAtUtc.

POST /api/v1/customer-accounts

Örnek istek

JSON
{
  "name": "Yeni Cari",
  "title": "Yeni Cari A.Ş.",
  "taxIdNumber": "1234567890",
  "accountTypes": "Customer",
  "currency": "TRY",
  "city": "Ankara",
  "district": "Çankaya",
  "street": "Atatürk Cad. No:1",
  "email": "[email protected]",
  "phone": "0312...",
  "isRetail": false
}
PUT /api/v1/customer-accounts/{id}

Güncelleme. Body POST ile aynı; isActive opsiyonel.

Faturalar

Paket: invoice.core. Oluşturma uçları e-belge göndermez; kayıt Status = Saved.

GET /api/v1/invoices

Query parametreleri

ParametreTipAçıklama
directionstringSalesSales veya Purchase
qstringFatura no / cari adı
statusstringÖr. Saved, Sent
startDate / endDatedateDüzenleme tarihi aralığı
page / pageSizeintSayfalama

Örnek yanıt · 200

JSON
{
  "page": 1,
  "pageSize": 25,
  "total": 1,
  "items": [
    {
      "id": "....",
      "invoiceNumber": "DRAFT-....",
      "invoiceDirection": "Sales",
      "invoiceTypeCode": "SATIS",
      "issueDate": "2026-09-29",
      "customerPartyName": "Örnek Müşteri",
      "payableAmount": 1200.00,
      "documentCurrencyCode": "TRY",
      "status": "Saved",
      "profileID": "EARSIVFATURA"
    }
  ]
}
GET /api/v1/invoices/{id}

Başlık + lines[] (kalemler).

Örnek yanıt (özet)

JSON
{
  "id": "....",
  "invoiceNumber": "DRAFT-....",
  "uuid": "....",
  "payableAmount": 1200.00,
  "taxAmount": 200.00,
  "lineExtensionAmount": 1000.00,
  "lines": [
    {
      "lineNumber": 1,
      "itemName": "Defter A5",
      "stockItemId": "....",
      "invoicedQuantity": 10,
      "unitCode": "C62",
      "unitPrice": 100,
      "taxRate": 20,
      "lineExtensionAmount": 1000,
      "taxAmount": 200
    }
  ]
}
POST /api/v1/invoices/sales

Satış faturası oluşturur (çekirdek alanlar). En az bir geçerli satır gerekir.

Stok bağlamak için stockItemId (Guid) yerine tercih edilen alanlar: stockCode (stok kartı kodu), sku (varyant SKU / stok kodu) veya barcode. Öncelik: stockVariantId → barcode → sku → stockCode → stockItemId. itemName / fiyat / KDV verilmezse stok kartından doldurulur.

Örnek istek

JSON
{
  "customerAccountId": "....",
  "issueDate": "2026-09-29",
  "currency": "TRY",
  "note": "API ile oluşturuldu",
  "lines": [
    {
      "barcode": "8690123456789",
      "quantity": 10,
      "unitPrice": 100
    },
    {
      "stockCode": "DEFTER-A5",
      "quantity": 2
    }
  ]
}

Yanıt · 201

Oluşturulan fatura detayı.

Siparişler

Paket: orderdespatch.core. Kayıt Status = Open; numara SIP-S- / SIP-A- serisinden alınır (Web ile aynı).

GET /api/v1/orders

Query parametreleri

ParametreTipAçıklama
directionstringSalesSales veya Purchase
qstringSipariş no / cari / özel kod
statusstringÖr. Open, Closed, Cancelled
startDate / endDatedateSipariş tarihi aralığı
page / pageSizeintSayfalama
GET /api/v1/orders/{id}

Başlık + lines[].

POST /api/v1/orders/sales

customerAccountId ve requestedDeliveryDate zorunlu. En az bir geçerli satır gerekir.

Stok: barcode, sku, stockCode veya stockItemId. Fatura satırları ile aynı çözümleme kuralları.

Örnek istek

JSON
{
  "customerAccountId": "....",
  "orderDate": "2026-09-29",
  "requestedDeliveryDate": "2026-10-05",
  "currency": "TRY",
  "note": "API siparişi",
  "specialCode": "REF-1",
  "lines": [
    {
      "sku": "DEFTER-A5-KIRMIZI-M",
      "quantity": 10,
      "unitPrice": 100,
      "taxRate": 20
    }
  ]
}

Yanıt · 201

Oluşturulan sipariş detayı (orderNumber örn. SIP-S-001).

POST /api/v1/orders/purchase

Body POST /orders/sales ile aynı; numara SIP-A- serisinden.

Mağaza / POS

Paket: store.core. Mağaza lisansı gerekir.

complete-sale: fatura kaydı oluşturur; ÖKC ve e-belge gönderimi bu sürümde yoktur (Web POS’taki tam akış sonraki turda bağlanacak). Perakende cari (isRetail: true) zorunludur.
GET /api/v1/store-pos/stores

Erişilebilir aktif mağazalar. CompanyUser için store ataması varsa filtrelenir.

Örnek yanıt · 200

JSON
[
  {
    "id": "....",
    "code": "MGZ01",
    "name": "Merkez Mağaza",
    "defaultRetailCustomerId": "....",
    "cashBankId": "...."
  }
]
GET /api/v1/store-pos/sales

Query parametreleri

ParametreTipAçıklama
storeIdguidzorunluMağaza
qstringFiş no / cari
paymentstringcash | card
typestringsale | return (iade)
startDate / endDatedatebugünTarih aralığı
page / pageSizeintSayfalama

Örnek yanıt · 200

JSON
{
  "page": 1,
  "pageSize": 25,
  "total": 1,
  "items": [
    {
      "id": "....",
      "invoiceNumber": "DRAFT-....",
      "issueDate": "2026-09-29",
      "issueTime": "14:32:00",
      "customerPartyName": "Perakende",
      "payableAmount": 250.00,
      "paymentMeansCode": "10",
      "invoiceTypeCode": "SATIS",
      "status": "Saved",
      "salesRepresentative": "Ali"
    }
  ]
}

paymentMeansCode: 10 nakit, 48 kart.

POST /api/v1/store-pos/complete-sale

Minimal POS satış / iade kaydı.

Request body

AlanTipAçıklama
storeIdguidzorunluMağaza
customerAccountIdguidzorunluPerakende cari
paymentMeansstringCASHCASH | CARD
isReturnboolfalseİade ise true
salesRepresentativestring?Satıcı
posMovementTypeIdguid?Hareket tipi
linesarrayzorunluSepet satırları

lines[] alanları

AlanTipAçıklama
stockItemIdguidzorunluÜrün
stockVariantIdguid?Varyant
itemNamestring?Görünen ad
quantitydecimal> 0Miktar
unitPricedecimalBirim fiyat
taxRatedecimalKDV %
unitCodestring?C62Birim kodu
salesRepresentativestring?Satır satıcısı

Örnek istek

JSON
{
  "storeId": "....",
  "customerAccountId": "....",
  "paymentMeans": "CASH",
  "isReturn": false,
  "salesRepresentative": "Ali",
  "lines": [
    {
      "stockItemId": "....",
      "itemName": "Defter A5",
      "quantity": 2,
      "unitPrice": 45.5,
      "taxRate": 20,
      "unitCode": "C62"
    }
  ]
}

Örnek yanıt · 200

JSON
{
  "success": true,
  "invoiceId": "....",
  "invoiceNumber": "DRAFT-....",
  "payableAmount": 109.20,
  "message": "Satış kaydedildi (ÖKC/e-belge bu uçta henüz yok)."
}

Sonraki sürümler

İrsaliye, tahsilat/ödeme, çek, masraf, personel, raporlar, ayarlar, pazaryeri ve tam POS ÖKC/e-belge uçları aynı doküman formatında eklenecek.