Kendi sistemlerinizi Cashod'a bağlayın.
Cashod API'si, kendi sistemlerinizin COD siparişlerini Cashod'a göndermesini, teslimat durumlarını geri okumasını ve ürün kataloğunuzla stoğunuzu senkronize tutmasını sağlar. JSON tabanlı bir HTTP API'sidir — SDK gerekmez.
https://api.cashod.ma/api/v1$ Tek anahtar, tek mağaza, yalnızca seçtiğiniz kapsamlar.
Cashod'da Ayarlar → Geliştiriciler altında bir anahtar oluşturun. Gizli anahtar yalnızca oluşturulurken bir kez gösterilir ve sonradan kurtarılamaz — kaybolursa anahtarı iptal edip yenisini oluşturun.
Her istekte bearer token olarak gönderin:
Authorization: Bearer csk_live_…Yalnızca o mağazanın verilerini görür ve değiştirir, bu yüzden hiçbir zaman mağaza kimliği göndermezsiniz — anahtar hangi mağazayı kastettiğinizi zaten söyler. Birden fazla mağaza işleten bir satıcı, her mağaza için ayrı bir anahtar oluşturur.
Her anahtar ayrıca oluştururken seçtiğiniz kapsamları taşır. Bir yazma kapsamı, karşılık gelen okuma iznini de verir; böylece sipariş oluşturan bir anahtar onları geri okuyabilir. Bir raporlama betiğine salt okunur bir anahtar verin; sızsa bile hiçbir şey oluşturamaz veya iptal edemez.
orders:readSiparişleri ve teslimat durumlarını okumaorders:writeSipariş oluşturma — orders:read iznini de verirproducts:readÜrün kataloğunu okumaproducts:writeÜrün oluşturma ve güncelleme — products:read iznini de verirstock:writeVaryant stok seviyelerini ayarlama
Üç adımda ilk siparişiniz.
- 01Bir anahtar oluşturun
Cashod'da Ayarlar → Geliştiriciler'i açın, ihtiyacınız olan kapsamları seçin ve gizli anahtarı kopyalayın — yalnızca bir kez gösterilir.
- 02Bir sipariş gönderin
Siparişi kendi
external_iddeğerinizle/v1/ordersadresine POST edin; böylece tekrarlanan bir istek asla ikinci bir koliye dönüşmez. - 03Teslimatını takip edin
Kargo firması koliyi ilerlettikçe
/v1/orders/{id}üzerindenstatus,confirmation_statusvetracking_numberalanlarını geri okuyun.
curl -X POST https://api.cashod.ma/api/v1/orders \
-H "Authorization: Bearer csk_live_…" \
-H "Content-Type: application/json" \
-d '{
"external_id": "order-77",
"customer": { "name": "Amine Berrada", "phone": "+212600000000" },
"shipping": { "address": "12 Rue Tarik Ibn Ziad", "city": "Casablanca" },
"items": [ { "sku": "MON-001-M-NOIR", "quantity": 2 } ]
}'Bir öğleden sonrada öğrenebileceğiniz küçük bir yüzey.
Her şey https://api.cashod.ma/api/v1 altında.
/v1/ordersÖdeme sayfanızdan sipariş oluşturun. external_id ile güvenle yeniden denenebilir.
Kapsamorders:write/v1/orders/{id}Tek bir siparişi okuyun: kargo durumu, onay durumu ve takip numarası.
Kapsamorders:read/v1/ordersSiparişleri en yeniden başlayarak listeleyin, created_after ile filtreleyin, imleçle sayfalayın.
Kapsamorders:read/v1/productsSKU'ya göre bir ürünü varyantları ve görselleriyle oluşturun veya güncelleyin.
Kapsamproducts:write/v1/products/{productId}/variants/{variantId}/stockBir varyantın stoğunu mutlak bir seviyeye ayarlayın.
Kapsamstock:write/v1/openapi.jsonReferansın üretildiği, makine tarafından okunabilir OpenAPI belgesi.
Bir yapay zekâ asistanıyla geliştirin
Yapay zekâ kodlama asistanı mı kullanıyorsunuz? Sayfayı elle aktarmak yerine bu istemi kopyalayın. Asla bir anahtar içermez: ürettiği kod sizinkini CASHOD_API_KEY ortam değişkeninden okur.
ChatGPT, Claude, Cursor veya Lovable'a yapıştırın. Bu sayfanın kurallarını ve OpenAPI belgesinin bağlantısını taşır; böylece asistan entegrasyonu sizin altyapınız için yazar.
You are helping me integrate my system (online store, landing page or ERP) with the Cashod API, a cash-on-delivery order platform. Write the integration code following the specification below exactly. Ask me which language/framework I use if you cannot tell from context, and which of the tasks below I need.
The full machine-readable specification is at https://api.cashod.ma/api/v1/openapi.json. Fetch it if you can, and trust it over this summary for field details.
## Rules
- Call the API from my SERVER only, never from browser JavaScript: the key must not reach visitors.
- Read the key from an environment variable named CASHOD_API_KEY (it looks like csk_live_…). Never hard-code it. I create it in Cashod under Settings → Developers; one key belongs to one store.
- Every request: header "Authorization: Bearer <CASHOD_API_KEY>", JSON bodies with "Content-Type: application/json".
- Send only the fields listed here or in the OpenAPI document: unknown fields are rejected with 400 invalid_request.
- Rate limit: 120 requests per minute per key.
- Never let a Cashod failure break my customer's checkout: log it and retry in the background.
## Task 1: send an order
POST https://api.cashod.ma/api/v1/orders (scope orders:write)
{
"external_id": "order-77",
"customer": { "name": "Amine Berrada", "phone": "+212600000000" },
"shipping": { "address": "12 Rue Tarik Ibn Ziad", "city": "Casablanca" },
"items": [ { "sku": "MON-001-M-NOIR", "quantity": 2, "unit_price": 149 } ]
}
- Always send external_id, set to my own order id. It makes the call idempotent: a retry returns the existing order (200) instead of creating a second one (201). Without it a retried timeout becomes a second real parcel.
- customer.name, customer.phone, shipping.address, shipping.city are required. shipping.phone is optional (defaults to the customer's).
- items: at least one. Each needs quantity (integer ≥ 1) and one of: sku (variant or product SKU, the usual way), product_id (Cashod product id), or name (free text for an item not in the catalogue; unit_price is then required).
- unit_price: what the customer actually paid per unit. Omit it to use the catalogue price.
- Optional: total (final agreed total; the difference is recorded as a discount), discount, notes, scheduled_for (ISO 8601), source_name (e.g. "Landing page Ramadan"), confirmed (true ONLY if I already confirmed the order with the customer; it skips the confirmation call).
## Task 2: read delivery status
GET https://api.cashod.ma/api/v1/orders/{id} or GET https://api.cashod.ma/api/v1/orders?created_after=<ISO 8601> (scope orders:read)
- An order has status (shipping, e.g. NOT_SHIPPED, SHIPPED, DELIVERED, RETURNED), confirmation_status (e.g. PENDING, CONFIRMED, CANCELLED) and tracking_number once shipped.
- Lists are cursor-paginated, newest first: read data, has_more and next_cursor, and pass ?cursor=<next_cursor> for the next page. limit defaults to 50, max 100.
- Poll at most once a minute.
## Task 3: sync products
POST https://api.cashod.ma/api/v1/products (scope products:write)
- Keyed on sku: an unknown sku creates the product (201), a known one updates it (200). Safe to send the whole catalogue on a schedule.
- Required: sku, name, category, price, cost_price. Optional: description, sale_price, is_active, track_stock, min_stock, images (URLs), variants.
- variants: each needs sku; optional size, color, price, sale_price, cost_price, stock. Variants are matched by sku; one I do not list is left untouched, never deleted.
- images: omit to keep the existing ones, send a list to replace them, send [] to clear them.
- Setting stock here also needs the stock:write scope.
- 409 conflict: another product already uses this name, or a variant sku belongs to another product. Report it to me; do not retry.
## Task 4: keep stock in sync
PATCH https://api.cashod.ma/api/v1/products/{productId}/variants/{variantId}/stock (scope stock:write)
{ "stock": 12 }
- Stock is set to an absolute level, never a delta, so a retried call cannot double-apply.
## Errors
Every failure looks like {"error": {"type": "...", "message": "..."}}. Branch on error.type, never on the message text.
- invalid_request (400): fix the payload, do not retry.
- authentication_error (401): missing, unknown or revoked key. Do not retry.
- permission_error (403): the key lacks the scope, or the subscription is not active. Do not retry.
- not_found (404): no such order or product in this key's store.
- rate_limit_exceeded (429) and server_error (500), plus timeouts and network errors: retry with backoff (for example 1 min, 5 min, 30 min). Orders are safe to retry because of external_id.
New optional fields may appear in responses: ignore what you do not recognise.
## When you are done
Show me where the calls happen in my code, the environment variable to set, and a curl command to send one test order.Alan listesinin size söyleyemedikleri.
Sipariş oluşturma
Her kalemi SKU'suyla tanımlayın — hangisiyle satıyorsanız varyant SKU'su veya ürün SKU'su. unit_price göndermezseniz katalog fiyatı kullanılır; gönderirseniz o geçerli olur — açılış sayfasında bir kampanya varsa istediğiniz de budur.
curl -X POST https://api.cashod.ma/api/v1/orders \
-H "Authorization: Bearer csk_live_…" \
-H "Content-Type: application/json" \
-d '{
"external_id": "order-77",
"customer": { "name": "Amine Berrada", "phone": "+212600000000" },
"shipping": { "address": "12 Rue Tarik Ibn Ziad", "city": "Casablanca" },
"items": [ { "sku": "MON-001-M-NOIR", "quantity": 2 } ]
}'İdempotans — external_id gönderin
Sipariş için kendi kimliğinizi external_id olarak gönderin; çağrı güvenle yeniden denenebilir hale gelir. Aynı external_id tekrar gelirse Cashod ikinci bir sipariş oluşturmak yerine zaten oluşturduğu siparişi döndürür:
- 201 Created — artık yeni bir sipariş var.
- 200 OK — bu
external_idiçin zaten bir sipariş vardı; değiştirilmeden döndürülür.
Bu göründüğünden daha önemlidir. Zaman aşımına uğrayan bir istek genellikle yine de işlenmiştir; external_id olmadan yeniden denemeniz gerçek bir kargo firmasına gönderilen ikinci bir gerçek siparişe dönüşür ve müşteri iki koli alır.
Teslimat durumunu okuma
Bir sipariş status (kargo), confirmation_status (onay araması) ve gönderildikten sonra tracking_number taşır.
curl https://api.cashod.ma/api/v1/orders/ORDER_ID \
-H "Authorization: Bearer csk_live_…"Çok sayıda siparişi takip etmek için GET /v1/orders ile listeleyin ve created_after ile filtreleyin. Dakikada birden sık sorgulamayın; durumlar kargo hızında değiştiği için daha sık döngülerin bir faydası yoktur.
Ürün oluşturma ve güncelleme
sku anahtarlı tek bir uç nokta: bilinmeyen bir SKU ürünü oluşturur, bilinen bir SKU günceller. Böylece neyin zaten var olduğunu kontrol etmeden tüm kataloğunuzu düzenli olarak gönderebilirsiniz.
- 201 Created — bu SKU yeniydi.
- 200 OK — bu SKU'ya sahip bir ürün vardı ve güncellendi.
curl -X POST https://api.cashod.ma/api/v1/products \
-H "Authorization: Bearer csk_live_…" \
-H "Content-Type: application/json" \
-d '{
"sku": "MON-001",
"name": "Montre Or",
"category": "Accessoires",
"price": 199,
"cost_price": 60,
"images": [ "https://cdn.example/montre.jpg" ],
"variants": [
{ "sku": "MON-001-M-NOIR", "size": "M", "color": "Noir", "stock": 7 }
]
}'Varyantlar SKU'ya göre eşleştirilir ve listelemediğiniz bir varyanta dokunulmaz — asla silinmez. Bir varyantın arkasında genellikle sipariş geçmişi vardır; bu yüzden bir varyantı atlayan bir senkronizasyon onu yok etmemelidir. Varyantları Cashod panelinden silin.
Görseller ise tersine çalışır: images alanını göndermezseniz mevcut görsellere dokunulmaz; bir liste gönderirseniz yeni set o olur; temizlemek için [] gönderin.
cost_price zorunludur. Cashod COGS ve kârı bundan hesaplar; bu alan olmadan oluşturulan bir ürün, yer aldığı her siparişte %100 marj raporlar. Güncellemede, ürün için stok satın alındıktan sonra yok sayılır — bu noktada değiştirmek elinizdeki stoğun maliyetini yeniden yazar.
Bu uç nokta üzerinden bir varyantta stock ayarlamak ayrıca stock:write kapsamını gerektirir. Bu kapsam olmadan çağrı, stok dışındaki her şeyi sessizce kaydetmek yerine reddedilir — böylece bir katalog anahtarı katalog anahtarı olarak kalır.
İki çakışmayı ele almaya değer: mağazadaki başka bir ürün zaten o adı kullanıyor (adlar, SKU'lardan bağımsız olarak mağaza başına benzersizdir) ya da varyant SKU'larınızdan biri başka bir ürüne ait. Her ikisi de çakışmayı belirten 409 conflict olarak döner.
Stoğu senkronize tutma
Stok bir fark değeriyle ayarlanmaz, mutlak bir seviyeye ayarlanır. Varyantın sahip olmasını istediğiniz seviyeyi gönderin — böylece yeniden denenen bir çağrı iki kez uygulanamaz; bir -1 ise bunu sessizce yapardı.
curl -X PATCH https://api.cashod.ma/api/v1/products/PRODUCT_ID/variants/VARIANT_ID/stock \
-H "Authorization: Bearer csk_live_…" \
-H "Content-Type: application/json" \
-d '{ "stock": 12 }'Sayfalama
Liste uç noktaları imleçle, en yeniden başlayarak sayfalanır. Bir yanıt data, has_more ve next_cursor taşır. Sonraki sayfa için o imleci ?cursor= olarak geri gönderin. Sayfa boyutu varsayılan olarak 50, en fazla 100'dür.
Sayfa numarası yerine imleç, çünkü siparişler sürekli gelir: ofset kullanıldığında yeni bir sipariş sonraki her sayfayı bir kaydırır, böylece geçmişi gezen bir istemci bazı satırları sessizce kaçırır, bazılarını ise tekrar okur.
Hatalar
Her hata aynı biçimdedir:
{
"error": {
"type": "invalid_request",
"message": "No product or variant with SKU \"GHOST-1\" in this store."
}
}Mantığınızı message metnine değil, type alanına göre kurun — metin iyileştirilebilir, tür değişmez.
invalid_requestGönderdiğiniz verideki bir sorun. Mesaj hangisi olduğunu belirtir.authentication_errorEksik, bilinmeyen veya iptal edilmiş anahtar.permission_errorAnahtarda kapsam eksik veya hesabın aboneliği aktif değil.not_foundBu anahtarın mağazasında böyle bir sipariş veya ürün yok.rate_limit_exceededBekleyin ve yeniden deneyin.server_errorBizden kaynaklı. Yeniden denemek güvenlidir; external_id gönderdiyseniz iki kez denemek de güvenlidir.Hız sınırları
Anahtar başına dakikada 120 istek. Sınır IP adresine göre değil anahtara göredir; böylece aynı barındırmadaki başka bir istemci sizin kotanızı harcayamaz.
Sürümleme
Sürüm yolda yer alır: /v1. Bir sürüm içinde yanıtlara yeni isteğe bağlı alanlar eklenebilir; bu yüzden savunmacı şekilde ayrıştırın ve tanımadıklarınızı yok sayın. Mevcut bir entegrasyonu bozacak her şey bunun yerine yeni bir sürüme girer.
Her uç nokta, her alan.
Backend'in kendi OpenAPI belgesinden üretilir; böylece her dağıtımda kodla birlikte değişir ve asla eskimez.