تاجر جني
Which API?
Merchant API For merchants: manage your own catalogue, orders and finances through the v2 Third-Party API. Application API For partner apps: browse Gini merchants and products, send customers to checkout and follow their orders.

New: Application API docs

Pick the API you are reading here — the Merchant API for your own store, the Application API for partner apps.

Merchant dashboard
Resources
Changelog

API documentation

Everything you need to integrate with the Gini v2 Third-Party API.

Changelog

Every change to this API that you can observe from your side — new headers, changed payloads, fixed endpoints — newest first. Nothing here requires action unless an entry says so.

Changed 2026-10-21
Bulk price updates are now stock updates

POST /products/price-updates is now POST /products/stock-updates, and an item can set a quantity as well as a price. Action needed: send your requests to the new path; the old one now answers 405 Method Not Allowed.

Notes
  • An item needs a price, a quantity, an is_visible, or any of them together; only what you send changes.
  • quantity is a whole number of 0 or more, for a product or, with variant_id, for one variant.
  • A sku that is not one of your products, or a variant_id not under it, is now ignored instead of refusing the whole request with 422.
New 2026-10-20
Bulk delete products

POST /products/bulk-delete deletes up to 500 products by SKU, sent as a JSON list. The request is checked at once, the products are deleted in the background, and the result arrives on your webhook.

Notes
  • Send the SKUs as skus, a list of up to 500. A SKU you do not have is ignored.
  • Answers 202 Accepted with an id. Requires a configured webhook.
  • New webhook event product_bulk_delete_finished: the deleted count and every one of your products left in place, with the reason (has_orders or error).
New 2026-10-19
Bulk price updates switch products on and off

POST /products/price-updates now also takes is_visible on an item, to put a product on the storefront or take it off, with or without a new price. Requests you already send work as before.

Notes
  • An item needs a price, an is_visible, or both; price is no longer required on its own.
  • Products only: an item with variant_id and is_visible is refused with 422.
  • A product Gini is reviewing, or a pre-order product, stays hidden when you send is_visible true; a price in the same item is still set.
New 2026-10-14
Bulk price updates

POST /products/price-updates sets new prices for up to 500 products or variants in one request. The request is checked at once and the prices are written in the background.

Notes
  • Each item is a product's sku and a price, with an optional variant_id to price one variant of that product.
  • Prices are whole IQD and at least the minimum price.
  • Answers 202 Accepted; requests from one store are applied one at a time.
Changed 2026-10-11
Product descriptions are capped at 1000 words

A product description may now hold at most 1000 words. Action needed if you send longer ones: shorten them before sending.

Notes
  • POST /products, PUT /products/{sku} and POST /products/bulk answer 422 when a description is over the limit.
  • Words are counted by splitting on spaces and line breaks, so Arabic and English count the same way.
  • Products already stored are not touched. A PUT that sends their description must bring it under the limit.
Changed 2026-10-08
Reels accept MP4 only

Reel videos must now be MP4. MOV and WebM uploads are refused. Action needed if you send either: convert to MP4 before uploading.

Notes
  • POST /reels and PUT /reels/{reel} answer 422 with the message: The video file must be an MP4 file.
  • Reels already stored are not touched.
New 2026-10-06
Discounts by category, fixed amounts and a per-unit cap

A discount can now cover all your products, a category, or some of its subcategories, not only products you pick. It can take a fixed amount off each unit instead of a percentage, and a percentage can be capped per unit.

Notes
  • POST and PUT /discounts take new optional fields: type, amount, max_amount, scope, category_id and subcategory_ids. See Discounts.
  • Existing calls keep working unchanged: type defaults to percentage and scope to products.
  • Discount payloads, including the discount on an order item, now carry type, amount, max_amount, scope and category_id. Get a discount also returns subcategory_ids.
  • A discount whose products are on another active, unexpired discount is now refused only by those live discounts — an expired or inactive one no longer blocks.
New 2026-10-05
MCP server for your store

Claude and other AI assistants can now run your store through an MCP server, signed in with your dashboard login or your API key and secret.

Notes
  • See the MCP server page for how to connect and the full list of tools.
  • Its create, update and delete tools run the same code as the matching API endpoints.
Changed 2026-10-04
Orders are marked delivered by the delivery company or the customer

The merchant no longer sets an order to delivered. The delivery company marks it, or the customer confirms by scanning the order\'s QR code.

Notes
  • PUT /orders/{id} with status delivered now answers 400: This action is not allowed for the current order status.
  • The code field on PUT /orders/{id} is gone; it is ignored if sent.
Changed 2026-09-27
A handoff can no longer be undone

Once an order is handed to delivery it stays handed over, from the dashboard and from the API.

Notes
  • POST /orders/{id}/handoff/reset now always answers 400 with the message: A handoff cannot be undone. The URL is kept so existing calls get a clear refusal rather than a 404.
New 2026-09-11
Reels: manage your short videos over the API

The promotional videos customers watch in the SuperQi app can now be listed, created, updated and deleted over the API. Previously they were dashboard-only.

Notes
  • GET/POST /reels and GET/PUT/DELETE /reels/{reel}. Create and update are multipart/form-data because they carry the video file.
  • Every route needs reels enabled for your account and returns 403 when it is not. GET /profile now tells you whether you have it.
  • A product can carry at most one reel. Omitting the video or preview on an update keeps the stored file rather than clearing it.
New 2026-09-11
GET /profile reports what your account is entitled to

An entitlements block listing the features switched on for you: reels, pre-orders, wholesale selling, multiple branches, product labels, installment months and on-site installments.

Notes
  • Read this before assuming a write failed. The API enforces entitlements by silently DROPPING fields you may not set, so an unentitled value looks identical to a bug in your payload.
  • entitlements.reels false means every /reels route will 403.
New 2026-09-11
The product and order lists take the dashboard filters

The product list accepted only search and per_page. It now takes the same nine filters the dashboard offers, plus sorting — and because the exports reuse them, POST /exports/products is no longer limited to a text search either.

Notes
  • Products: visibility, stock, installments, installment_same_price, category_id, branch_id, wholesale, pre_order, sort and direction.
  • Orders: shipping_method, delivery_company, handoff and pre_order.
  • Default ordering is unchanged (sort_order first, then newest). Ordering only differs when you send sort, so existing pagination is unaffected.
  • delivery_company=0 means self delivery and also matches orders with no carrier assigned yet.
New 2026-09-11
Pre-order products can be configured over the API

The API could read is_pre_order but never set it, and the customer-facing terms were unreachable. Create and update now accept the toggle and its terms, and the terms come back on the product.

Notes
  • is_pre_order plus availability_date, estimated_shipping_days, pre_order_quantity, terms_and_conditions and product_cancellation_policy. The terms are required only when the toggle is on.
  • Needs pre-orders enabled for your account — check entitlements.pre_orders.
  • Turning the toggle on forces is_visible to 0: a pre-order product never shows on the storefront.
  • Turning it off retires the terms rather than deleting them, so a customer who already ordered can still see what they agreed to. The pre_order block then reads null.
New 2026-09-11
Three more exports, and you choose the columns

Abandoned carts, wholesale orders and the restock stock template can now be queued over the API. The wholesale group had no export at all.

Notes
  • POST /exports/abandoned-carts, /exports/wholesale-orders and /exports/stock-template. Poll GET /exports/{id} as before.
  • orders, products and wholesale-orders accept a columns array. Omit it and you get every column, exactly as before.
  • The stock template takes the product filters, so you can pull one for a single branch or only for what is running low.
New 2026-09-11
Wholesale buyers directory

Every merchant that has ordered from your wholesale catalogue, with their lifetime figures.

Notes
  • GET /api/v1.0/third-party/wholesale/buyers, with search and sort (spend, orders, recent, name).
  • lifetime_spend counts DELIVERED orders only — a cancelled order raises orders_count and contributes nothing to spend.
  • Returns data and meta but no links, because the rows are an aggregate rather than a model query.
New 2026-09-11
The product payload says when Gini is holding a listing

While a listing check is under review, an is_visible you send is dropped. The response now tells you so, instead of looking like a write that quietly did nothing.

Notes
  • visibility_locked is true while the hold is in place, and visibility_lock_reason carries the sentence to show.
  • You can still hide a held product — what you cannot do is put it back on sale until the review is settled.
Fixed 2026-09-11
Product responses: months, the create shape, and Arabic search

Three fixes to what the product endpoints hand back, each of which made a correct request look like a failed one.

Notes
  • months is now returned. It was accepted, validated and stored, and both documented examples showed it, but it was never serialized — so you could set installment months and never read them back.
  • POST /products now returns the same shape as GET /products/{sku}. It used to return 25 keys where show returned 39, with category_id and subcategory_id as strings rather than integers.
  • search now folds Arabic spelling variants, matching the dashboard. Searching the correct spelling of your own product could previously return nothing.
Fixed 2026-09-11
Delivery actions refuse clearly when you deliver your own orders

If you have no delivery company there is no carrier to hand an order to. Both delivery-network actions now say that, instead of failing confusingly.

Notes
  • POST /orders/{id}/handoff used to stamp a handoff that never happened, which then showed the order as out with a captain.
  • POST /orders/{id}/request-cancellation used to return an opaque 400 from the carrier, which read as though your reason had been rejected.
  • Both now return 400 naming the real reason. GET /profile tells you which you are through is_self_delivery.
Corrected 2026-09-11
Documentation: profile fields and the list envelope

Two places where the docs described something the API does not do.

Notes
  • The profile section had no response example at all, and its prose named two fields that do not exist: verification (the field is is_verified, and it reports PHONE verification) and delivery mode (it is is_self_delivery). The full response is now documented.
  • The introduction claimed every list returns links + meta. Five do not: categories is unpaginated, discounts and attributes return a bare array, and abandoned-carts and wholesale buyers return meta without links.
New 2026-09-09
Bulk notification actions and filtered settlement export

Two more bulk actions from the dashboard: mark or delete several notifications at once by id, and queue one export of every transaction across the settlements matching your rail/date filter.

Notes
  • POST /notifications/read and DELETE /notifications take an ids array.
  • POST /settlements/export takes the same types/from/to filters as the settlements list.
  • Operator 2FA is intentionally not exposed on the API (last_login is).
New 2026-09-09
Finance depth: transactions, stats and settlement export

The finance endpoints now go beyond the settlement and debt lists: a transaction ledger, aggregate stats for settlements and debts, rail/date filters on settlements, and a queued export of a settlement's transactions.

Notes
  • GET /transactions is the money ledger; GET /settlements/stats and /debts/stats are the headline aggregates.
  • GET /settlements now accepts types[] and from/to; the stats endpoint honours the same filters.
  • POST /settlements/{id}/export queues an .xlsx and notifies you when it is ready.
New 2026-09-09
More order operations

The order endpoints now cover the rest of the dashboard's order actions: list the valid rejection reasons, hand an order off to delivery and pull it back, and request a delivery cancellation.

Notes
  • GET /orders/rejection-reasons gives the reject_reason_id values (with which one needs a note).
  • Handoff and reset apply to accepted orders; request-cancellation applies to accepted or in-transit orders.
  • Order receipts were already available at the signed /print/receipt/{order} URL — see the Orders section.
New 2026-09-09
Notification feed endpoints

Read your notification feed over the API — list (with the dashboard's type filters), unread count, mark one or all read, and delete — matching the dashboard's notifications screen.

Notes
  • data is the raw event payload; act on it rather than the dashboard's icons and links.
  • Notifications are system-generated — there is no create.
New 2026-09-09
More product fields on create and update

Product create and update now accept the remaining dashboard fields: is_used, allow_multiple_addons, sort_order, product_label, client_store_id (branch assignment), and the wholesale fields is_b2b_enabled, b2b_price and b2b_min_quantity. They are also returned on product reads.

Notes
  • All optional and additive — existing product calls are unaffected.
  • client_store_id must be one of your own branches, or the request is rejected with HTTP 422.
  • product_label, client_store_id and the b2b_* fields require the same store features as the dashboard (product label, multi-store, wholesale seller); without them they are ignored, exactly as the dashboard ignores them.
New 2026-09-09
Product add-on endpoints

Manage a product's add-ons over the API — the optional extras a customer can attach — as a per-product sub-resource under /products/{sku}/addons, matching the dashboard's add-ons editor.

Notes
  • Full CRUD per add-on; an add-on with existing orders cannot be deleted (HTTP 422).
  • Send JSON unless you are uploading an image; POST is idempotent.
New 2026-09-09
Profile endpoints

Read and update your own store profile over the API — business details, location, logo and cover — the same data the dashboard's Profile screen manages.

Notes
  • GET/PUT /profile for the fields; POST /profile/logo and /profile/cover for the images.
  • The field update is JSON so the signature covers it; logo and cover are multipart replaces.
New 2026-09-09
Dashboard stats endpoint

The dashboard's headline figures — wallet stages, order mix, payout totals and abandoned-cart numbers — are now one read on the API, computed the same way the dashboard computes them.

Notes
  • Returns the figures, not the chart time-series. Pass year to scope the order breakdown.
New 2026-09-09
Settings endpoints: opening hours, ordering schedule, delivery

Read and set your store settings over the API: weekly opening hours, the weekly ordering schedule, and delivery settings (fulfilment toggles plus, for self-delivery merchants, per-state charges).

Notes
  • Opening hours and the ordering schedule are per store — omit store_id for your main store.
  • A full week is sent on update; a closed day carries no times, and overnight ranges are not supported.
  • Shipment charges apply only to self-delivery merchants; a null cost leaves a state unpriced, 0 is free.
New 2026-09-09
Manage endpoints: collections, influencers, branches, operators

Four more dashboard areas are now full CRUD on the API: product collections, influencers, store branches, and operators (staff sub-accounts).

Notes
  • Collections and branches take a multipart body where an image is involved; create is idempotent.
  • An influencer with attributed orders cannot be deleted.
  • Operators: a password is required on create and never returned; branch and operator phones are stored in normalised Iraqi form.
New 2026-09-09
Read endpoints: abandoned carts, reviews, settlements, debts

Four read-only resources are now on the API, matching the dashboard's own screens: your abandoned carts (with the customer's reachable contact number), your reviews, your settlement batches, and your debts wallet.

Notes
  • All read-only and paginated. See each section for its filters.
  • Financial ids are only ever resolved within your own account; another merchant's id returns 404.
New 2026-09-09
Discounts endpoints

You can now create, list, update and delete product discounts over the API — the same discounts the dashboard manages, honoured identically on the storefront. See the Discounts section.

Notes
  • Full CRUD under /discounts. Writes are multipart/form-data because a discount carries a cover image.
  • The storefront honours one discount per product; a product already discounted is rejected with HTTP 422.
  • POST /discounts is idempotent — send an Idempotency-Key header.
New 2026-08-26
Idempotency-Key on the creating endpoints

Retrying a creating request after a dropped connection used to risk a duplicate. Send an Idempotency-Key header and the retry returns the original response instead of creating a second record.

Notes
  • Optional. Requests without the header behave exactly as before, so nothing you have built needs to change.
  • Accepted on POST /products, /products/bulk, /attributes, /exports/orders, /exports/products, /wholesale/cart and /wholesale/orders/buyer.
  • A replayed response carries the header Idempotency-Replayed: true.
  • Worth adopting if you create anything programmatically. See the Idempotency section for the rules.
Fixed 2026-08-26
The attributes endpoints work again

Every method on /attributes returned 500 because the API queried a relation that does not exist. If you tried to list, read, create, update or delete an attribute and gave up, it works now.

Notes
  • No contract change — the request and response shapes are what the Attributes section always documented.
  • If you built a workaround, you can remove it.
Corrected 2026-08-26
Two corrections to the webhook documentation

The Webhooks section described two things that were not true. The code did not change; the documentation did.

Notes
  • Deliveries are not signed. The section claimed every webhook request was HMAC-signed. It never was — the Authorization header built from your auth_value is what proves a delivery came from Gini. If you wrote a signature check, it was rejecting every delivery.
  • Basic credentials are encoded for you. The docs said basic sends your auth_value verbatim; it is base64-encoded before sending. If you stored an already-encoded string it was encoded twice and never authenticated — store username:password raw.
Improved 2026-08-23
A rejected signature now tells you why

A 401 used to say only that the signature was invalid, which covered six unrelated mistakes. The rejection now carries a diagnostics object naming the likely cause.

Notes
  • It names the specific mistake where it can: a signed query string, a signed full URL, the wrong field order, a stale secret, or a clock out of step with the server.
  • A clock-skew rejection also returns the server time, in the body and in the X-Server-Time header, so you can correct without guessing.
  • The secret and the expected signature are never returned. See Troubleshooting for the full list.
How to follow changes
  • This page lists only what is observable from your side. Internal work that cannot change your integration is left out.
  • Additive changes — a new optional header, a new key in a response — ship without notice. Treat response objects as append-only and match on the keys you need rather than asserting an exact shape.
  • Anything that would break a working integration is announced here before it ships, not alongside it.