تاجر جني
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
Getting started
Troubleshooting

API documentation

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

Troubleshooting

Six unrelated mistakes all return 401 Invalid signature. Rather than leave you guessing between them, an invalid-signature or expired rejection carries a diagnostics object naming the likely cause, and the ping endpoint confirms your setup before you write a single business call.

Start here: ping

Call ping before anything else. It is signed exactly like every other endpoint and does nothing else, so its answer tells you which layer is failing.

GET /api/v2.0/third-party/ping
Signed
Ping returns What it means
200, JSON The edge, your key, your clock and your signing all work. Anything failing after that is the endpoint itself, not your setup.
403, HTML page saying Request blocked The edge stopped the request before it reached the API. See Blocked before the API below.
502, 503 or 504, HTML page from cloudfront The edge could not reach the API. The fault is ours — retry with backoff.
401 or 403, JSON Your key or your signing. Read the error and its diagnostics below.
429 Rate limit. Wait the seconds in Retry-After, then retry.
200 OK
{
    "data": {
        "ok": true,
        "merchant": {
            "id": 1513,
            "business_name": "Your Store"
        },
        "time": {
            "server_time": 1755690000,
            "timestamp_used": "1755690000",
            "skew_seconds": 0,
            "window_seconds": 300
        },
        "signature": {
            "signed_method": "GET",
            "signed_path": "/api/v2.0/third-party/ping",
            "body_signed": false,
            "body_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
            "string_to_sign": "GET/api/v2.0/third-party/ping1755690000",
            "string_to_sign_sha256": "b1946ac92492d2347c6235b4d2611184..."
        },
        "capabilities": {
            "wholesale_seller": true,
            "multi_stores": false,
            "webhook_configured": true,
            "webhook_url": "https://example.com/gini/webhook"
        }
    }
}

Compare string_to_sign with the string your own code built. If they differ, that difference is your bug.

Blocked before the API

Every request passes an edge firewall before it reaches the API. When the firewall blocks one, you get an HTML page instead of the JSON every API error uses.

403 Forbidden
<HTML><HEAD><TITLE>ERROR: The request could not be satisfied</TITLE></HEAD>
<BODY><H1>403 ERROR</H1><H2>The request could not be satisfied.</H2>
Request blocked.
<PRE>Generated by cloudfront (CloudFront)
Request ID: kP3xV9_eQ2-Tb7hWnY4sLc8mRz1uJd6fAo5gXq0iHe3NvKbM2ZwCtA==</PRE>
</BODY></HTML>

Your key and your signature played no part, so changing them will not help. Send Gini support the Request ID from the page together with your server's public IP address — the Request ID lets us find the exact rule that stopped you.

How to recognise it
  • Content-Type is text/html, not application/json, and the page says Generated by cloudfront.
  • There is no X-Request-Id header. The API adds one to everything it answers, so its absence means the API never saw the request.
  • The Request ID is also in the x-amz-cf-id response header, so your client can log it without parsing the page.
Common causes
  • Your server sits in a cloud or hosting-provider range (Hetzner, DigitalOcean, AWS and similar). The edge blocks those until the address is allow-listed. If the same request works from your own machine and fails from your server, this is why.
  • The request has no User-Agent header. Send one — any value your HTTP client sets by default is fine.
  • A 502, 503 or 504 page from cloudfront is different: the edge could not reach the API. That fault is ours, so retry with backoff.
When a request is rejected

An invalid-signature or expired 401 carries a diagnostics object (the missing-headers, invalid-key and unconfigured-secret 401s do not). It describes what the server signed and, where the cause can be established, names it outright.

401 Unauthorized
{
    "error": "Invalid signature",
    "diagnostics": {
        "signed_method": "GET",
        "signed_path": "/api/v2.0/third-party/products",
        "body_signed": false,
        "body_bytes": 0,
        "body_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
        "timestamp_used": "1755690000",
        "server_time": 1755690002,
        "skew_seconds": 2,
        "string_to_sign_sha256": "9f86d081884c7d659a2feaa0c55ad015...",
        "string_to_sign_preview": "GET/api/v2.0/third-party/products1755690000",
        "hints": [
            "Your signature matches PATH with the query string appended. Sign the path only - /api/v2.0/third-party/products - and leave ?page=2 out."
        ]
    }
}

That first hint is produced by re-signing a list of known-wrong signing strings and checking whether any of them reproduces the signature you sent. When one does, your mistake is named exactly rather than guessed.

What each rejection means
Error Status What to check
Missing authentication headers 401 No diagnostics object — anonymous callers are deliberately not told which headers were missing.
Invalid API key 401 X-API-KEY must be the API Client ID from Settings, Integrations - not the secret.
Client account is inactive 403 The account is deactivated. Signing is fine; the account is not.
Request expired 401 Your clock drifted over 300 seconds. Read X-Server-Time from the response and resync.
Invalid signature 401 Read the hints. If none matched, your secret is stale - reissue it.
Client signature key not configured 401 No secret is stored for this account. Reissue credentials.
Request blocked. 403 An HTML page from the edge, not the API. See Blocked before the API.
Response headers
Header Description
X-Request-Id Present on every response the API sends — an edge rejection never reached the API, so it has none. Quote it in a support ticket and we can find your exact request. Send your own to correlate with your tracing - up to 64 characters of letters, digits, dot, dash or underscore.
X-Server-Time Server Unix time. Returned on ping and on Request expired, so a client can correct its own clock.
X-RateLimit-Remaining Calls left in the current minute. The budget is per merchant, so another integration cannot exhaust yours.
What diagnostics never contain
  • Your API secret is never returned, in any field, under any condition.
  • The signature the server expected is never returned. Only your own request is described back to you, so a caller without the secret learns nothing from a rejection.
  • A hint that names your mistake can only appear for a caller who already holds the secret - matching a known-wrong variant still requires signing it correctly.
  • string_to_sign_preview is truncated at 256 bytes. Compare string_to_sign_sha256 instead when the body is large.