ElebneElebneDocs
Store API

Products

Create, read, update, and delete products in your shop catalog. Manage variants, options, images, and pricing.

Products

Full CRUD for your product catalog. Products support variants (size, color), options (extras), multiple images, and external ID mapping for POS/ERP sync.

Idempotency required

All write endpoints (create, update, delete) require an X-Idempotency-Key header. Use a unique key per request (e.g. your SKU or UUID). Safe to retry on network errors.

Create a product

POST /dev/store/products

Default status is DISABLED

Products created via API default to DISABLED unless you explicitly set "status": "ACTIVE". This prevents incomplete products from appearing in your shop.

Request body

FieldTypeRequiredDescription
namestringYesProduct name
priceintegerYesPrice in centimes. 50000 = 500.00 MRU
descriptionstringNoProduct description
externalIdstringNoSKU or external system ID (max 256 chars). Used for inventory sync and CSV matching.
categoryIdstringNoShop category ID
quantityintegerNoStock quantity. null = unlimited stock.
statusstringNoACTIVE or DISABLED. Default: DISABLED.
isFlexiblePricebooleanNoAllow customer to choose price
costBasisintegerNoCost basis in centimes (for MARGIN revenue model)
imagesarrayNo[{ url, sortOrder? }] -- image URLs with optional sort order
variantsarrayNoProduct variants (see below)
optionsarrayNoProduct options / extras (see below)
preparationTimeobjectNo{ value, unit } where unit is minutes, hours, or days

Variant object

FieldTypeRequiredDescription
typestringYesVariant type: COLOR, SIZE, or custom
labelstringYesDisplay label, e.g. "Rouge", "XL"
valuestringNoValue code, e.g. #FF0000
priceAdjustmentintegerNoPrice delta in centimes. Can be negative.
quantityintegerNoPer-variant stock. null = unlimited.

Option object

FieldTypeRequiredDescription
namestringYesOption name, e.g. "Sauce piquante"
priceAdjustmentintegerYesAdditional price in centimes
curl -X POST https://api.elebne.ai/api/v1/dev/store/products \
  -H "Authorization: Bearer sk_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: create-thieb-001" \
  -d '{
    "name": "Thiéboudienne Royale",
    "price": 50000,
    "description": "Le plat national mauritanien, poisson frais et légumes",
    "quantity": 20,
    "externalId": "THIEB-001",
    "status": "ACTIVE",
    "variants": [
      { "type": "SIZE", "label": "Normal", "priceAdjustment": 0, "quantity": 15 },
      { "type": "SIZE", "label": "Grand", "priceAdjustment": 10000, "quantity": 5 }
    ],
    "options": [
      { "name": "Sauce piquante", "priceAdjustment": 500 }
    ],
    "preparationTime": { "value": 30, "unit": "minutes" }
  }'
const response = await fetch('https://api.elebne.ai/api/v1/dev/store/products', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_test_YOUR_KEY',
    'Content-Type': 'application/json',
    'X-Idempotency-Key': 'create-thieb-001',
  },
  body: JSON.stringify({
    name: 'Thiéboudienne Royale',
    price: 50000,
    description: 'Le plat national mauritanien, poisson frais et légumes',
    quantity: 20,
    externalId: 'THIEB-001',
    status: 'ACTIVE',
    variants: [
      { type: 'SIZE', label: 'Normal', priceAdjustment: 0, quantity: 15 },
      { type: 'SIZE', label: 'Grand', priceAdjustment: 10000, quantity: 5 },
    ],
    options: [
      { name: 'Sauce piquante', priceAdjustment: 500 },
    ],
    preparationTime: { value: 30, unit: 'minutes' },
  }),
});

const { data } = await response.json();
console.log(data.id);     // "6612f..."
console.log(data.status); // "ACTIVE"
import requests

response = requests.post(
    'https://api.elebne.ai/api/v1/dev/store/products',
    headers={
        'Authorization': 'Bearer sk_test_YOUR_KEY',
        'Content-Type': 'application/json',
        'X-Idempotency-Key': 'create-thieb-001',
    },
    json={
        'name': 'Thiéboudienne Royale',
        'price': 50000,
        'description': 'Le plat national mauritanien, poisson frais et légumes',
        'quantity': 20,
        'externalId': 'THIEB-001',
        'status': 'ACTIVE',
        'variants': [
            {'type': 'SIZE', 'label': 'Normal', 'priceAdjustment': 0, 'quantity': 15},
            {'type': 'SIZE', 'label': 'Grand', 'priceAdjustment': 10000, 'quantity': 5},
        ],
        'options': [
            {'name': 'Sauce piquante', 'priceAdjustment': 500},
        ],
        'preparationTime': {'value': 30, 'unit': 'minutes'},
    },
)

data = response.json()['data']
print(data['id'])     # "6612f..."
print(data['status']) # "ACTIVE"

Get a product

Retrieve a single product by its MongoDB ID or externalId (SKU). The endpoint resolves both.

GET /dev/store/products/{id}
# By MongoDB ID
curl https://api.elebne.ai/api/v1/dev/store/products/6612f1a2b3c4d5e6f7890123 \
  -H "Authorization: Bearer pk_test_YOUR_KEY"

# By external ID (SKU)
curl https://api.elebne.ai/api/v1/dev/store/products/THIEB-001 \
  -H "Authorization: Bearer pk_test_YOUR_KEY"
const response = await fetch(
  'https://api.elebne.ai/api/v1/dev/store/products/THIEB-001',
  {
    headers: { 'Authorization': 'Bearer pk_test_YOUR_KEY' },
  }
);

const { data } = await response.json();
console.log(data.name);     // "Thiéboudienne Royale"
console.log(data.price);    // 50000
console.log(data.quantity); // 20
response = requests.get(
    'https://api.elebne.ai/api/v1/dev/store/products/THIEB-001',
    headers={'Authorization': 'Bearer pk_test_YOUR_KEY'},
)

data = response.json()['data']
print(data['name'])     # "Thiéboudienne Royale"
print(data['price'])    # 50000
print(data['quantity']) # 20

List products

Paginated list with search, status filter, and sorting.

GET /dev/store/products?search=thieb&status=ACTIVE&sortBy=newest&page=1
ParameterTypeDefaultDescription
searchstring--Full-text search on product name
statusstringallACTIVE, DISABLED, or ALL
sortBystring--Sort order: newest, oldest, price_asc, price_desc
categoryIdstring--Filter by shop category
pageinteger1Page number
curl "https://api.elebne.ai/api/v1/dev/store/products?status=ACTIVE&sortBy=newest&page=1" \
  -H "Authorization: Bearer pk_test_YOUR_KEY"
const params = new URLSearchParams({
  status: 'ACTIVE',
  sortBy: 'newest',
  page: '1',
});

const response = await fetch(
  `https://api.elebne.ai/api/v1/dev/store/products?${params}`,
  {
    headers: { 'Authorization': 'Bearer pk_test_YOUR_KEY' },
  }
);

const { items, total, hasMore } = await response.json();
console.log(`${total} products found`);
response = requests.get(
    'https://api.elebne.ai/api/v1/dev/store/products',
    headers={'Authorization': 'Bearer pk_test_YOUR_KEY'},
    params={'status': 'ACTIVE', 'sortBy': 'newest', 'page': 1},
)

result = response.json()
print(f"{result['total']} products found")

Update a product

Full replace with PUT or partial update with PATCH. Both accept the same body -- PATCH only changes the fields you send.

PUT  /dev/store/products/{id}
PATCH /dev/store/products/{id}
curl -X PATCH https://api.elebne.ai/api/v1/dev/store/products/THIEB-001 \
  -H "Authorization: Bearer sk_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: update-thieb-price" \
  -d '{
    "price": 55000,
    "description": "Thiéboudienne premium avec poisson frais du jour"
  }'
const response = await fetch(
  'https://api.elebne.ai/api/v1/dev/store/products/THIEB-001',
  {
    method: 'PATCH',
    headers: {
      'Authorization': 'Bearer sk_test_YOUR_KEY',
      'Content-Type': 'application/json',
      'X-Idempotency-Key': 'update-thieb-price',
    },
    body: JSON.stringify({
      price: 55000,
      description: 'Thiéboudienne premium avec poisson frais du jour',
    }),
  }
);

const { data } = await response.json();
console.log(data.price); // 55000
response = requests.patch(
    'https://api.elebne.ai/api/v1/dev/store/products/THIEB-001',
    headers={
        'Authorization': 'Bearer sk_test_YOUR_KEY',
        'Content-Type': 'application/json',
        'X-Idempotency-Key': 'update-thieb-price',
    },
    json={
        'price': 55000,
        'description': 'Thiéboudienne premium avec poisson frais du jour',
    },
)

data = response.json()['data']
print(data['price']) # 55000

Delete a product

Soft-deletes the product. It will no longer appear in your shop or API responses.

DELETE /dev/store/products/{id}
curl -X DELETE https://api.elebne.ai/api/v1/dev/store/products/THIEB-001 \
  -H "Authorization: Bearer sk_test_YOUR_KEY"
const response = await fetch(
  'https://api.elebne.ai/api/v1/dev/store/products/THIEB-001',
  {
    method: 'DELETE',
    headers: { 'Authorization': 'Bearer sk_test_YOUR_KEY' },
  }
);

const { data } = await response.json();
console.log(data.deleted); // true
response = requests.delete(
    'https://api.elebne.ai/api/v1/dev/store/products/THIEB-001',
    headers={'Authorization': 'Bearer sk_test_YOUR_KEY'},
)

data = response.json()['data']
print(data['deleted']) # True

Upload a product image

Upload an image file (JPEG, PNG, or WebP, max 5 MB) as multipart form data.

POST /dev/store/products/{id}/images
curl -X POST https://api.elebne.ai/api/v1/dev/store/products/THIEB-001/images \
  -H "Authorization: Bearer sk_test_YOUR_KEY" \
  -F "[email protected]"
const formData = new FormData();
formData.append('file', fs.createReadStream('thieboudienne.jpg'));

const response = await fetch(
  'https://api.elebne.ai/api/v1/dev/store/products/THIEB-001/images',
  {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk_test_YOUR_KEY',
    },
    body: formData,
  }
);

const { data } = await response.json();
console.log(data.url);     // "https://..."
console.log(data.imageId); // "6612f..."
with open('thieboudienne.jpg', 'rb') as f:
    response = requests.post(
        'https://api.elebne.ai/api/v1/dev/store/products/THIEB-001/images',
        headers={'Authorization': 'Bearer sk_test_YOUR_KEY'},
        files={'file': ('thieboudienne.jpg', f, 'image/jpeg')},
    )

data = response.json()['data']
print(data['url'])     # "https://..."
print(data['imageId']) # "6612f..."

Error responses

Error codeHTTPDescription
IDEMPOTENCY_KEY_REQUIRED400Missing X-Idempotency-Key header
NO_FILE400No file uploaded for image endpoint
INVALID_FILE_TYPE400File type not JPEG, PNG, or WebP
FILE_TOO_LARGE400File exceeds 5 MB
PRODUCT_NOT_FOUND404Product not found by ID or externalId
SHOP_NOT_FOUND404Shop not found for this business

Next steps

  • Inventory -- update stock levels for individual products or in bulk
  • CSV Import -- bulk import products from a spreadsheet
  • Webhook Events -- get notified on stock changes

Was this page helpful?

On this page