Kartlo.App
FeaturesPricingDocsContact
Sign inStart free
  • Getting started
    • Create your store
    • Your desk
    • The Kartlo phone app
    • Stores and organizations
    • Plans and billing
    • AI credits
    • Team and permissions
    • Getting help
  • Products
    • Add products
    • Variants
    • Media library
    • Import products
    • Collections
    • Inventory
    • Stock counts
    • Transfers
    • Purchase orders
    • Gift cards
    • Product labels
    • Product reviews
  • Orders
    • Order lifecycle
    • Draft orders
    • Order payments
    • Fulfilment
    • Returns and refunds
    • Abandoned carts
  • Customers
    • Customers and shopper accounts
    • Segments
    • Companies
    • Subscribers and customer emails
    • Loyalty points
  • Discounts
  • Online store
    • Themes
    • Design Studio
    • Pages
    • Navigation menu
    • Store address and custom domains
    • Publish and password-protect your storefront
    • SEO and sharing
  • Payments
    • Stripe card payments
    • Cash on delivery and manual methods
    • Hosted payment gateways
    • Tabby instalments
    • Finance and payouts
  • Shipping and delivery
  • Markets and languages
  • Arabic and English
  • Taxes
  • Analytics and Live View
    • Analytics
    • Live View
    • Scheduled email reports
  • Settings
  • Security
  • Developers
    • API and webhooks
    • Headless Storefront API
  • Kartlo Apps
    • Remove or cancel an app
    • Accounting - United Arab Emirates
    • CRM
    • Social Sales Channels
    • Messenger and WhatsApp
  1. Documentation
  2. Developers
  3. API and webhooks

API and webhooks

On this page
  • API keys
  • Endpoints
  • Pagination and rate limits
  • Webhooks
  • Checking the signature

The Kartlo API and webhooks let a developer connect your store to other systems, such as a warehouse, an ERP or an accounting tool. Both live in Settings › Developers and work for one store at a time.

Important

Webhooks and the public API are included on the Pro plan and above. If a store moves to a lower plan, API requests are refused and webhooks stop sending until it is back on Pro.

API keys#

  1. Go to Settings › Developers and click Create API key.
  2. Name the key after the app that will use it and tick only the permissions it needs.
  3. Copy the key. Kartlo shows it once and keeps only a fingerprint of it, so nobody at Kartlo can read it back.

Send the key on every request as a Bearer token:

curl https://kartlo.app/api/v1/orders?limit=20 \
  -H "Authorization: Bearer kartlo_sk_..."
PermissionAllows
read_productsGET /api/v1/products and /api/v1/products/{id}
read_ordersGET /api/v1/orders and /api/v1/orders/{id}
read_customersGET /api/v1/customers and /api/v1/customers/{id}
read_inventoryGET /api/v1/inventory
write_inventoryPATCH /api/v1/inventory/{variantId}, and also reading inventory

Tip

If a key leaks, click Revoke. It stops working on the next request.

Endpoints#

Method and pathReturns
GET /api/v1/productsProducts with their variants and prices, newest first. Cost prices are never included.
GET /api/v1/products/{id}One product.
GET /api/v1/ordersOrders with their line items, totals, addresses and statuses, newest first.
GET /api/v1/orders/{id}One order.
GET /api/v1/customersCustomers with contact details and order totals, newest first.
GET /api/v1/customers/{id}One customer.
GET /api/v1/inventoryStock per variant: available, reserved, tracking and backorders.
PATCH /api/v1/inventory/{variantId}Sets the available quantity. The change is saved as a stock count correction in the stock history.
curl -X PATCH https://kartlo.app/api/v1/inventory/VARIANT_ID \
  -H "Authorization: Bearer kartlo_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"available": 12}'

Every answer is JSON. A list answer has data and next_cursor; a single record answer has data. An error answer has error.code and error.message.

StatusMeaning
401The key is missing, wrong or revoked.
402The store is not on the Pro plan or above.
403The key does not have the permission for this endpoint, or the subscription has ended (changes only).
404No record with this id in the store of the key.
422The body is not valid, for example a negative quantity.
429Too many requests. Wait a minute and retry.

Pagination and rate limits#

Lists return up to limit records (1 to 100, 50 by default). When there are more, the answer has a next_cursor. Send it back as ?cursor= to get the next page, and stop when next_cursor is null.

Each key can make 120 requests a minute. Above that the API answers 429 with a Retry-After header.

Webhooks#

  1. Go to Settings › Developers and click Add webhook.
  2. Enter an address that starts with https:// and pick the events you want.
  3. Copy the signing secret. Kartlo shows it once.
  4. Click Send test event to check your server answers.
EventSent when
order.createdAn order is placed, at checkout or by your team.
order.paidAn order becomes paid in full.
order.fulfilledEvery item of an order is fulfilled.
order.cancelledAn order is cancelled.
product.createdA product is added.
product.updatedA product or one of its variants changes.
customer.createdA customer is added.
inventory.updatedThe available quantity of a variant changes.

Kartlo sends a POST with a JSON body and the headers X-Kartlo-Event, X-Kartlo-Delivery (unique per delivery, use it to ignore repeats) and X-Kartlo-Signature. The record in data has the same shape as the API returns, read when the event is first sent.

{
  "id": "4f0c...",
  "event": "order.paid",
  "created_at": "2026-10-24T09:12:03.120Z",
  "store_id": "8a1d...",
  "data": { "id": "...", "order_number": "1042", "payment_status": "paid", "items": [ ... ] }
}

Answer with any 2xx status within 10 seconds. Otherwise Kartlo retries after 1, 5 and 15 minutes, then 1, 3, 6 and 12 hours, 8 attempts in all. Deliveries go out within about 5 minutes of the event. After 50 failed deliveries in a row the webhook turns off and Settings shows why; turn it back on once your server is fixed. Recent deliveries in the webhook shows each attempt, and Retry now sends one again; if that fails too, the normal retries carry on. Delivery history is kept for 30 days.

Note

Webhooks are sent only to public addresses. Addresses on a private network, and redirects, are refused.

Checking the signature#

X-Kartlo-Signature looks like t=1792831923,v1=5f2c.... v1 is the HMAC-SHA256, in hex, of the timestamp, a dot and the raw request body, keyed with your signing secret. Compare it in constant time and refuse timestamps older than 5 minutes. Always check the raw body, before parsing the JSON.

import crypto from 'node:crypto';

export function verifyKartlo(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  const got = Buffer.from(parts.v1 ?? '', 'hex');
  return got.length === 32 && crypto.timingSafeEqual(got, Buffer.from(expected, 'hex'));
}
import hmac, hashlib, time

def verify_kartlo(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t = int(parts.get("t", "0"))
    if abs(time.time() - t) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))
PreviousDevelopersNextHeadless Storefront API

On this page

  • API keys
  • Endpoints
  • Pagination and rate limits
  • Webhooks
  • Checking the signature
FeaturesPricingDocsContactPrivacyTerms© 2026 Louward Labs FZC LLC