تاجر جني
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
API reference
Discounts

API documentation

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

Discounts

Create and manage discounts on your products — a percentage (optionally capped per unit) or a fixed amount, over all your products, a category, some of its subcategories, or products you pick. The same discounts the dashboard's Sales screen manages, honoured identically on the storefront. Scoped to your merchant account and signed with HMAC-SHA256.

List discounts

Lists every discount belonging to the authenticated merchant, newest first, each with a count of the products it applies to. Supports a free-text search on the discount name.

GET /api/v2.0/third-party/discounts
Signed
Query parameters
Field Type Required Description
query string Optional Search discounts whose name contains this value (case-insensitive LIKE match).
page integer Optional Page number. Results are paginated at 15 per page.
Response
[
    {
        "id": 42,
        "name": "Ramadan Sale",
        "code": "RAMADAN20",
        "percentage": 20,
        "type": "percentage",
        "amount": null,
        "max_amount": 5000,
        "scope": "category",
        "category_id": 3,
        "max_usage": 100,
        "used_count": 12,
        "expires_at": "2027-01-31T23:59:59.000000Z",
        "is_active": 1,
        "image": "https://cdn.gini.iq/discounts/ramadan.jpg",
        "products_count": 8
    }
]
Notes
  • The response is a bare JSON array; the query is paginated at 15 rows per page but no pagination envelope is returned.
  • products_count is the number of products the discount is attached to.
  • subcategory_ids is returned by Get a discount, not by this list.
  • Results are ordered by most recently created first.
Get a discount

Retrieves a single discount by its numeric id, including the products it applies to. Returns 404 if the discount does not exist or does not belong to the authenticated merchant.

GET /api/v2.0/third-party/discounts/{id}
Signed
Response
{
    "data": {
        "id": 42,
        "name": "Ramadan Sale",
        "code": "RAMADAN20",
        "percentage": 20,
        "type": "percentage",
        "amount": null,
        "max_amount": 5000,
        "scope": "subcategories",
        "category_id": 3,
        "subcategory_ids": [
            12,
            14
        ],
        "max_usage": 100,
        "used_count": 12,
        "expires_at": "2027-01-31T23:59:59.000000Z",
        "is_active": 1,
        "image": "https://cdn.gini.iq/discounts/ramadan.jpg",
        "products": [
            {
                "id": 101,
                "name": "Wireless Headphones"
            },
            {
                "id": 102,
                "name": "USB-C Cable"
            }
        ]
    }
}
Notes
  • The path parameter {id} is the numeric discount id.
  • products is every product the discount covers, whatever its scope.
  • A missing or non-owned discount returns HTTP 404 with {'message': 'Discount not found'}.
Create a discount

Creates a discount and attaches it to the products its scope covers. Sent as multipart/form-data because it carries a cover image. The body is signed as an empty string — see Authentication.

POST /api/v2.0/third-party/discounts
Signed
Body parameters (multipart/form-data)
Field Type Required Description
name string Required Discount name (3–255 chars).
code string Required Coupon code (3–50 chars, letters/numbers/dashes/underscores). Unique across all discounts; stored upper-cased.
type string Optional What it takes off each unit: percentage (default) or amount, a fixed IQD amount.
percentage integer Optional Discount percentage, 1–75. Required when type is percentage.
max_amount integer Optional The most a percentage discount may take off one unit, in IQD (at least 1). Omit for no cap. Ignored for type amount.
amount integer Optional IQD taken off each unit (at least 1). Required when type is amount. Must leave every covered product and variant at least 1 IQD to pay; the error names the cheapest one and the most you can set.
scope string Optional Which products it covers: products (default — the ones in product_ids), all (every product you have), category (every product in category_id), or subcategories (every product in subcategory_ids of category_id).
category_id integer Optional One of your categories (see Categories). Required for scope category and subcategories. With scope products it is optional, and every picked product must be in it.
subcategory_ids[] integer[] Optional Subcategories of category_id (at least 1). Required for scope subcategories. With scope products it is optional, and every picked product must be in one of them.
max_usage integer Required Maximum number of times the code may be used, 1–1000000.
expires_at date Required Expiry date (YYYY-MM-DD). Must be after today. The discount applies until the end of that day.
image file Required Cover image (jpg/png/webp, max 2 MB).
product_ids[] integer[] Optional Ids of products to apply the discount to (at least 1). Required for scope products; ignored for the other scopes. Each must belong to you.
is_active boolean Optional Whether the discount is active. Defaults to true.
Response
{
    "data": {
        "id": 42,
        "name": "Ramadan Sale",
        "code": "RAMADAN20",
        "percentage": 0,
        "type": "amount",
        "amount": 2500,
        "max_amount": null,
        "scope": "category",
        "category_id": 3,
        "subcategory_ids": [],
        "max_usage": 100,
        "used_count": 0,
        "expires_at": "2027-01-31T23:59:59.000000Z",
        "is_active": 1,
        "image": "https://cdn.gini.iq/discounts/ramadan.jpg",
        "products": [
            {
                "id": 101,
                "name": "Wireless Headphones"
            }
        ]
    }
}
Notes
  • Returns HTTP 201 on success with the created discount wrapped in a data object.
  • Discounts are worked out per unit. A percentage takes percentage% off each unit, never more than max_amount when one is set; an amount takes that many IQD off each unit. The discounted price is rounded to the nearest dinar.
  • A discount of type amount stores percentage as 0.
  • The scope is resolved to products when you save: products you add to a category later do not join it. Update the discount to take them in.
  • The storefront honours one discount per product: if any covered product is already on another of your active, unexpired discounts, the request is rejected with HTTP 422 naming those products. An expired or inactive discount does not block.
  • A scope that covers no products is rejected with HTTP 422.
  • A product, category or subcategory id that is not yours is rejected with HTTP 422.
  • A duplicate code (any merchant) is rejected with HTTP 422.
  • This route is idempotent — send an Idempotency-Key header; see Idempotency.
Update a discount

Updates a discount. Every field is optional — send only what you are changing and the rest keeps its stored value. Omitting image keeps the current cover.

PUT /api/v2.0/third-party/discounts/{id}
Signed
Notes
  • Send as multipart/form-data if you include a new image; the body is signed as an empty string.
  • Takes the same body parameters as Create a discount, all optional.
  • Every update resolves the scope again, so a category or subcategories discount takes in products added there since it was saved.
  • product_ids and subcategory_ids, when sent, fully replace their lists. Omit product_ids to keep the picked products.
  • Changing scope to products needs product_ids; changing scope to category or subcategories needs category_id (and subcategory_ids) unless the discount already has them.
  • Changing type needs the new kind's value: percentage for percentage, amount for amount.
  • The code uniqueness check and the one-discount-per-product check both ignore this discount.
  • A missing or non-owned discount returns HTTP 404 with {'message': 'Discount not found'}.
Delete a discount

Deletes a discount and detaches it from every product it applied to.

DELETE /api/v2.0/third-party/discounts/{id}
Signed
Response
{
    "message": "Discount deleted successfully"
}
Notes
  • On success returns HTTP 200 with a confirmation message (no data body).
  • A missing or non-owned discount returns HTTP 404 with {'message': 'Discount not found'}.