تاجر جني
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
Product Variants

API documentation

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

Product Variants

Manage the attribute-combination SKUs of a product — preview the cartesian-product matrix, upsert variants non-destructively, and read or delete individual variants. Every endpoint is HMAC-signed and scoped to your own catalog: the product is resolved by SKU or numeric id from the caller's own products, and any submitted attribute must belong to the same merchant. The v2 API exposes only the attribute-based variant system; the legacy colors/sizes endpoint from v1 does not exist here.

Generate Variant Combinations

Takes a set of attributes with their values and returns every possible combination (cartesian product) as unsaved variant rows with null sku/price/quantity. Use it to preview the matrix before filling in prices/stock and posting back to the save endpoint. Nothing is persisted.

POST /api/v2.0/third-party/products/variants/generate
Signed
Body parameters
Field Type Required Description
attributes array Required Array of attribute definitions to combine (min 1).
attributes.*.attribute_id integer Required ID of an existing product attribute (must exist in product_attributes).
attributes.*.name string Required Display name of the attribute, echoed back on each combination (e.g. Color).
attributes.*.values array Required The values to combine for this attribute (min 1).
attributes.*.values.* string Required A single attribute value (e.g. Red).
Request body
{
    "attributes": [
        {
            "attribute_id": 1,
            "name": "Color",
            "values": [
                "Red",
                "Blue"
            ]
        },
        {
            "attribute_id": 2,
            "name": "Size",
            "values": [
                "S",
                "M"
            ]
        }
    ]
}
Response
{
    "data": [
        {
            "sku": null,
            "price": null,
            "quantity": null,
            "attributes": [
                {
                    "attribute_id": 1,
                    "value": "Red",
                    "name": "Color"
                },
                {
                    "attribute_id": 2,
                    "value": "S",
                    "name": "Size"
                }
            ]
        },
        {
            "sku": null,
            "price": null,
            "quantity": null,
            "attributes": [
                {
                    "attribute_id": 1,
                    "value": "Red",
                    "name": "Color"
                },
                {
                    "attribute_id": 2,
                    "value": "M",
                    "name": "Size"
                }
            ]
        },
        {
            "sku": null,
            "price": null,
            "quantity": null,
            "attributes": [
                {
                    "attribute_id": 1,
                    "value": "Blue",
                    "name": "Color"
                },
                {
                    "attribute_id": 2,
                    "value": "S",
                    "name": "Size"
                }
            ]
        },
        {
            "sku": null,
            "price": null,
            "quantity": null,
            "attributes": [
                {
                    "attribute_id": 1,
                    "value": "Blue",
                    "name": "Color"
                },
                {
                    "attribute_id": 2,
                    "value": "M",
                    "name": "Size"
                }
            ]
        }
    ]
}
Notes
  • Returns raw unsaved rows (NOT wrapped in the variant Resource) — the shape is exactly {sku, price, quantity, attributes[]} with attributes carrying {attribute_id, value, name}.
  • Nothing is written to the database; this is a preview/helper only.
  • The number of returned rows is the product of every attribute's value count (2 x 2 = 4 above).
Save (Upsert) Variants

Create and update multiple variants for a product in one request. To create a variant, omit the id (image required). To update, include the id (image optional — omitted keeps the existing one). Variants not present in the payload are left unchanged (non-destructive — unlike the dashboard's full-matrix reconcile). The product is resolved by SKU or numeric id from the caller's catalog.

PUT /api/v2.0/third-party/products/{sku}/variants
Signed
Body parameters
Field Type Required Description
variants array Required Array of variant objects to create or update (min 1).
variants.*.id integer Optional Existing variant ID — include to update, omit to create. Unknown IDs on this product are silently skipped.
variants.*.sku string Required Variant SKU. Must be unique within your account, across your own products and product_variants — ignoring this variant on update, and ignoring its own parent product, so a single-variant product may reuse its parent SKU. Spaces are normalized to hyphens.
variants.*.price numeric Required Variant price. Minimum is config services.product.min_price (PRODUCT_MIN_PRICE, default 0).
variants.*.quantity numeric Required Available stock quantity (0 or greater).
variants.*.b2b_price numeric Optional Optional B2B/wholesale price (0 or greater). Stored as decimal:2.
variants.*.image file Optional Variant image. Required when creating (no id); optional on update where omitting keeps the current image. Must be an uploaded file: jpg, jpeg, png, or webp, max 5120 KB (5MB).
variants.*.attributes array Required Attribute assignments for this variant (min 1). Every attribute must belong to the product's merchant.
variants.*.attributes.*.attribute_id integer Required Product attribute ID (must exist in product_attributes and be owned by the merchant).
variants.*.attributes.*.value string Required The attribute value for this variant (e.g. Red).
Request body (multipart/form-data)
// multipart/form-data — send as POST with _method=PUT because file uploads are involved.
// JSON-equivalent of the fields:
{
    "variants": [
        {
            "id": 58,
            "sku": "WH-RED-S-V2",
            "price": 47000,
            "quantity": 25,
            "b2b_price": 42000,
            "image": "<binary file — optional on update>",
            "attributes": [
                { "attribute_id": 1, "value": "Red" },
                { "attribute_id": 2, "value": "S" }
            ]
        },
        {
            "sku": "WH-BLUE-M",
            "price": 48000,
            "quantity": 30,
            "image": "<binary file — required on create>",
            "attributes": [
                { "attribute_id": 1, "value": "Blue" },
                { "attribute_id": 2, "value": "M" }
            ]
        }
    ]
}
Response
{
    "data": {
        "created": 1,
        "updated": 1,
        "variants": [
            {
                "id": 59,
                "product_id": 142,
                "image": "https://s3.amazonaws.com/qi/product_variants/9f2c1b.jpg",
                "sku": "WH-BLUE-M",
                "price": 48000,
                "b2b_price": null,
                "formatted_amounts": {
                    "price": "IQD 48,000",
                    "discounted_price": "IQD 0"
                },
                "quantity": 30,
                "attributes": [
                    {
                        "id": 1,
                        "product_variant_id": 59,
                        "product_attribute_id": 1,
                        "value": "Blue",
                        "name": "Color"
                    },
                    {
                        "id": 2,
                        "product_variant_id": 59,
                        "product_attribute_id": 2,
                        "value": "M",
                        "name": "Size"
                    }
                ]
            },
            {
                "id": 58,
                "product_id": 142,
                "image": "https://s3.amazonaws.com/qi/product_variants/abc123.jpg",
                "sku": "WH-RED-S-V2",
                "price": 47000,
                "b2b_price": "42000.00",
                "formatted_amounts": {
                    "price": "IQD 47,000",
                    "discounted_price": "IQD 42,300"
                },
                "quantity": 25,
                "attributes": [
                    {
                        "id": 1,
                        "product_variant_id": 58,
                        "product_attribute_id": 1,
                        "value": "Red",
                        "name": "Color"
                    },
                    {
                        "id": 2,
                        "product_variant_id": 58,
                        "product_attribute_id": 2,
                        "value": "S",
                        "name": "Size"
                    }
                ]
            }
        ]
    }
}
Notes
  • Uses multipart/form-data to support image uploads — send as POST with _method=PUT (method spoofing). Encode arrays as variants[0][sku], variants[0][attributes][0][attribute_id], etc.
  • Non-destructive upsert: rows with id update, rows without id create; existing variants NOT in the payload are left unchanged.
  • Tenant isolation: if any submitted attribute_id is not owned by the product's merchant the whole request is rejected with 422 Invalid attribute for this product. before any write.
  • Each save fully replaces a variant's attribute pivots (detach then re-attach), so the attributes array must be the complete set for that variant.
  • SKU is normalized (spaces become hyphens) and must be unique within your account, across your own products and product_variants; duplicates fail validation. A SKU another merchant already uses is accepted, and a variant may carry its own parent product SKU.
  • Newly uploaded variant images may take a few minutes to become publicly visible (S3/CDN propagation) even though the API returns the URL immediately.
  • The whole batch runs in a single DB transaction — if one row fails, none are persisted.
  • b2b_price is cast to decimal:2 so it serializes as a string such as 42000.00; when unset it is null.
  • formatted_amounts.discounted_price reflects the product's first available discount, or IQD 0 when no discount applies.
  • Returned variants are ordered newest-first (latest).
List Variants

Returns a paginated list of the product's variants, newest first. The product can be identified by SKU or numeric ID and must belong to the caller. Each variant includes its image URL, price, optional B2B price, formatted amounts, stock, and pivoted attributes.

GET /api/v2.0/third-party/products/{sku}/variants
Signed
Query parameters
Field Type Required Description
page integer Optional Page number to retrieve (default 1).
Response
{
    "data": [
        {
            "id": 58,
            "product_id": 142,
            "image": "https://s3.amazonaws.com/qi/product_variants/abc123.jpg",
            "sku": "WH-RED-S",
            "price": 45000,
            "b2b_price": "40000.00",
            "formatted_amounts": {
                "price": "IQD 45,000",
                "discounted_price": "IQD 40,500"
            },
            "quantity": 30,
            "attributes": [
                {
                    "id": 1,
                    "product_variant_id": 58,
                    "product_attribute_id": 1,
                    "value": "Red",
                    "name": "Color"
                },
                {
                    "id": 2,
                    "product_variant_id": 58,
                    "product_attribute_id": 2,
                    "value": "S",
                    "name": "Size"
                }
            ]
        }
    ],
    "links": {
        "first": "https://api.qi.iq/api/v2.0/third-party/products/WH-001/variants?page=1",
        "last": "https://api.qi.iq/api/v2.0/third-party/products/WH-001/variants?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "path": "https://api.qi.iq/api/v2.0/third-party/products/WH-001/variants",
        "per_page": 15,
        "to": 6,
        "total": 6
    }
}
Notes
  • Page size is fixed at 15 (the model default); there is no per_page override — only the page query param is honored.
  • Variants are ordered newest-first (latest).
  • Returns 404 with a Product not found. message when the SKU or ID does not resolve to one of the caller's products.
  • b2b_price serializes as a decimal string such as 40000.00, or null; image is the resolved S3 URL.
Show Variant

Returns a single variant of the given product by its numeric variant ID, including its attributes and discount-formatted amounts. Both the product (by SKU or ID) and the variant must belong to the caller.

GET /api/v2.0/third-party/products/{sku}/variants/{variant}
Signed
Response
{
    "data": {
        "id": 58,
        "product_id": 142,
        "image": "https://s3.amazonaws.com/qi/product_variants/abc123.jpg",
        "sku": "WH-RED-S",
        "price": 45000,
        "b2b_price": "40000.00",
        "formatted_amounts": {
            "price": "IQD 45,000",
            "discounted_price": "IQD 40,500"
        },
        "quantity": 30,
        "attributes": [
            {
                "id": 1,
                "product_variant_id": 58,
                "product_attribute_id": 1,
                "value": "Red",
                "name": "Color"
            },
            {
                "id": 2,
                "product_variant_id": 58,
                "product_attribute_id": 2,
                "value": "S",
                "name": "Size"
            }
        ]
    }
}
Notes
  • Single item is wrapped in a top-level data object.
  • Returns 404 with a Product not found. message if the product is not the caller's, or a Variant not found. message if the variant ID does not belong to that product.
  • The {variant} path segment is the numeric variant ID, not its SKU.
Delete Variant

Permanently deletes a variant of the given product (by numeric variant ID). Detaches its attribute pivots first. Blocked when the variant has been sold in any order.

DELETE /api/v2.0/third-party/products/{sku}/variants/{variant}
Signed
Response
{
    "message": "Variant deleted successfully."
}
Notes
  • Returns 422 with a Cannot delete this variant because it has associated orders. message if any order item references the variant — sold variants cannot be deleted.
  • Returns 404 with a Product not found. or Variant not found. message when the product or variant does not resolve for the caller.
  • Attribute pivot rows are detached before the variant is deleted.
  • The {variant} path segment is the numeric variant ID.