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.
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.
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.
/api/v2.0/third-party/ping
| 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. |
{
"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.
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.
<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.
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.
{
"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.
| 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. |
| 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. |