New: Application API docs
Pick the API you are reading here — the Merchant API for your own store, the Application API for partner apps.
Everything you need to integrate with the Gini v2 Third-Party API.
The API is stateless. Every request must carry an API key and an HMAC-SHA256 signature computed from the request itself, so the signature is unique per request and cannot be replayed after it expires.
| Header | Description |
|---|---|
| X-API-KEY | Your API Client ID (the public key from Settings → Integrations). |
| X-TIMESTAMP | Current Unix time in seconds. Must be within 300 seconds of server time. |
| X-SIGNATURE | Lowercase hex HMAC-SHA256 of the signing string, keyed with your API secret. |
Concatenate the HTTP method, the request path, the raw body, and the timestamp — in that exact order, with no separators — then HMAC-SHA256 it with your API secret.
signing_string = METHOD + PATH + BODY + TIMESTAMP
signature = HMAC_SHA256(signing_string, api_secret) // lowercase hex
$method = 'GET';
$path = '/api/v2.0/third-party/products';
$body = ''; // raw JSON for POST/PUT/PATCH, empty for GET/DELETE and multipart
$timestamp = (string) time();
$signingString = $method . $path . $body . $timestamp;
$signature = hash_hmac('sha256', $signingString, $apiSecret);
$headers = [
'X-API-KEY: ' . $apiClientId,
'X-TIMESTAMP: ' . $timestamp,
'X-SIGNATURE: ' . $signature,
];
File uploads are the one case that trips people up. A multipart/form-data body is not part of the signature — sign an empty body, exactly as you would for a GET.
$method = 'POST';
$path = '/api/v2.0/third-party/products';
$timestamp = (string) time();
// The multipart body is NOT signed — use an empty string for BODY.
$signingString = $method . $path . '' . $timestamp;
$signature = hash_hmac('sha256', $signingString, $apiSecret);
$headers = [
'X-API-KEY: ' . $apiClientId,
'X-TIMESTAMP: ' . $timestamp,
'X-SIGNATURE: ' . $signature,
// Do NOT set Content-Type yourself — cURL must add the multipart boundary.
];
$fields = [
'name' => 'My Product',
'price' => 15000,
'quantity' => 10,
'images[0]' => new CURLFile('/path/to/image.jpg'),
];
$ch = curl_init('https://merchant.gini.iq' . $path);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $fields,
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
TIMESTAMP=$(date +%s)
# No body — sign METHOD + PATH + TIMESTAMP only.
SIGNING_STRING="POST/api/v2.0/third-party/products${TIMESTAMP}"
SIGNATURE=$(echo -n "$SIGNING_STRING" | openssl dgst -sha256 -hmac "$API_CLIENT_SECRET" | cut -d' ' -f2)
curl -X POST https://merchant.gini.iq/api/v2.0/third-party/products \
-H "X-API-KEY: $API_CLIENT_ID" \
-H "X-SIGNATURE: $SIGNATURE" \
-H "X-TIMESTAMP: $TIMESTAMP" \
-F "name=My Product" \
-F "price=15000" \
-F "quantity=10" \
-F "images[]=@/path/to/image.jpg"