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).
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:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... Content-Type: application/json Accept: application/json
{"error":"..."}
Hata formatı
Hata gövdesi genelde tek alanlıdır:
{
"error": "E-posta veya şifre hatalı."
}
| HTTP | Anlam |
|---|---|
400 | Eksik/geçersiz istek gövdesi veya query |
401 | Token yok/geçersiz veya kimlik doğrulama başarısız |
403 | Paket, menü veya firma erişim yetkisi yok |
404 | Kayıt bulunamadı |
409 | Çakışma (ör. aynı stok kodu) |
Sayfalama
Liste uçları ortak yapı kullanır:
{
"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
| Alan | Tip | Açıklama | |
|---|---|---|---|
email | string | zorunlu | Kullanıcı e-posta |
password | string | zorunlu | Şifre |
Örnek istek
{
"email": "[email protected]",
"password": "********"
}
Örnek yanıt · 200
{
"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
| Alan | Tip | Açıklama | |
|---|---|---|---|
companyCode | string | zorunlu | Firma kodu (ör. ABC01) |
Örnek istek
{ "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
{
"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
[
{ "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
| Parametre | Tip | Açıklama | |
|---|---|---|---|
q | string | opsiyonel | Kod veya ad içinde arama |
page | int | varsayılan 1 | Sayfa |
pageSize | int | varsayılan 25 | Sayfa boyutu (max 100) |
activeOnly | bool | varsayılan true | Sadece aktif ürünler |
Örnek yanıt · 200
{
"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
{
"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.
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
| Alan | Tip | Açıklama | |
|---|---|---|---|
code | string | zorunlu | Stok kodu |
name | string | zorunlu | Ürün adı |
unitPrice | decimal | 0+ | Birim fiyat |
currencyCode | string | TRY | 3 harf |
vatRate | decimal? | KDV oranı | |
description | string? | Açıklama | |
unitId / categoryId | guid? | Birim / kategori | |
grammage / grammageUnit | Gramaj (G, KG…) | ||
imageUrl | string? | Görsel URL | |
variantType | byte | 0 | 0 / 1 / 2 |
colorIds / colorCodes | array | * | Tip 1–2’de zorunlu |
dimensionIds / dimensionNames | array | * | Tip 2’de zorunlu |
Örnek istek · renk + beden
{
"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.
multipart/form-data — JSON değil.
Form alanı adı: file. Opsiyonel: setAsPrimary=true.
Örnek (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
{
"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
| Parametre | Tip | Açıklama | |
|---|---|---|---|
q | string | Kod / ad | |
activeOnly | bool | true | |
page / pageSize | int | 100 | Max 200 |
Örnek yanıt · 200
{
"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
{
"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
| Parametre | Tip | Açıklama | |
|---|---|---|---|
type | string | firma ayarı | Size | Measurement |
q | string | Ad araması |
Örnek yanıt · 200
{
"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
{
"name": "L",
"sortOrder": 30
}
Cari hesaplar
Paket: customercards.core.
GET /api/v1/customer-accounts
Query parametreleri
| Parametre | Tip | Açıklama | |
|---|---|---|---|
q | string | Ad, unvan, VKN/TCKN, e-posta | |
accountType | string | Ör. Customer, Supplier | |
includeRetail | bool | false | Perakende carileri dahil et |
page / pageSize | int | Sayfalama |
Örnek yanıt · 200
{
"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
{
"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
| Parametre | Tip | Açıklama | |
|---|---|---|---|
direction | string | Sales | Sales veya Purchase |
q | string | Fatura no / cari adı | |
status | string | Ör. Saved, Sent | |
startDate / endDate | date | Düzenleme tarihi aralığı | |
page / pageSize | int | Sayfalama |
Örnek yanıt · 200
{
"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)
{
"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.
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
{
"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
| Parametre | Tip | Açıklama | |
|---|---|---|---|
direction | string | Sales | Sales veya Purchase |
q | string | Sipariş no / cari / özel kod | |
status | string | Ör. Open, Closed, Cancelled | |
startDate / endDate | date | Sipariş tarihi aralığı | |
page / pageSize | int | Sayfalama |
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.
barcode, sku, stockCode veya stockItemId.
Fatura satırları ile aynı çözümleme kuralları.
Örnek istek
{
"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.
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
[
{
"id": "....",
"code": "MGZ01",
"name": "Merkez Mağaza",
"defaultRetailCustomerId": "....",
"cashBankId": "...."
}
]
GET /api/v1/store-pos/sales
Query parametreleri
| Parametre | Tip | Açıklama | |
|---|---|---|---|
storeId | guid | zorunlu | Mağaza |
q | string | Fiş no / cari | |
payment | string | cash | card | |
type | string | sale | return (iade) | |
startDate / endDate | date | bugün | Tarih aralığı |
page / pageSize | int | Sayfalama |
Örnek yanıt · 200
{
"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
| Alan | Tip | Açıklama | |
|---|---|---|---|
storeId | guid | zorunlu | Mağaza |
customerAccountId | guid | zorunlu | Perakende cari |
paymentMeans | string | CASH | CASH | CARD |
isReturn | bool | false | İade ise true |
salesRepresentative | string? | Satıcı | |
posMovementTypeId | guid? | Hareket tipi | |
lines | array | zorunlu | Sepet satırları |
lines[] alanları
| Alan | Tip | Açıklama | |
|---|---|---|---|
stockItemId | guid | zorunlu | Ürün |
stockVariantId | guid? | Varyant | |
itemName | string? | Görünen ad | |
quantity | decimal | > 0 | Miktar |
unitPrice | decimal | Birim fiyat | |
taxRate | decimal | KDV % | |
unitCode | string? | C62 | Birim kodu |
salesRepresentative | string? | Satır satıcısı |
Örnek istek
{
"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
{
"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.