Docs / Get started

Errors

The API uses HTTP status codes for the class of problem and a stable error.code for the exact cause. Branch on error.code; the message is for humans and its wording may change.

Error format


                {
                  "error": {
                    "code": "ambiguous_code",
                    "message": "This code exists in more than one country; repeat with ?country=.",
                    "details": { "countries": ["MT", "IT"] }
                  }
                }
                
FieldDescription
error.codeStable, machine-readable identifier (see below).
error.messageHuman-readable explanation in English.
error.detailsOptional. Extra data, e.g. fields for validation errors or countries for ambiguous_code.

Validation errors

422 invalid_request lists each problem per field in details.fields. Nested fields use dot notation (user.external_ref).


                {
                  "error": {
                    "code": "invalid_request",
                    "message": "The request is invalid.",
                    "details": {
                      "fields": {
                        "lng": ["The lng field is required when lat is present."],
                        "user.external_ref": ["Use your own customer or order id, not an email address."]
                      }
                    }
                  }
                }
                

HTTP status codes

StatusMeaning
200 OKSuccess. Also an idempotent replay of a creation.
201 CreatedA resource was created.
204 No ContentDeleted. No body.
401 UnauthorizedCredentials missing, wrong, revoked or suspended.
402 Payment RequiredYour code allowance is used up.
403 ForbiddenYour account lacks the scope this endpoint needs.
404 Not FoundNo such record among yours.
409 ConflictThe request conflicts with the current state.
422 Unprocessable EntityThe input is invalid or cannot be resolved.
429 Too Many RequestsRate limit reached; see Rate limits.
500 / 503Problem on GP Code's side; retry later.

Error codes

Authentication & access

CodeHTTPCause and fix
unauthenticated401A header is missing, the key is unknown, the secret is wrong, or the request comes from outside your IP allowlist. Check both headers and your server's egress IP.
key_revoked401The key was revoked. Switch to your current key.
client_suspended401Your partner account is suspended. Contact GP Code.
forbidden_scope403The endpoint needs a scope your account does not have.

Request & lookup

CodeHTTPCause and fix
invalid_request422Validation failed. See details.fields.
not_found404The user or code does not exist among yours — including a user_id that belongs to someone else.
ambiguous_code409You have this code string in several countries. Repeat with ?country=; details.countries lists them.
request_in_progress409A request with the same Idempotency-Key is still running. Retry shortly.

Location

CodeHTTPCause and fix
address_not_found422The address could not be geocoded. Send lat and lng instead, or add a country hint.
country_unresolved422No country could be determined from the pin, address or hint. Send country.

GP codes

CodeHTTPCause and fix
code_unavailable409The custom code already exists in that country, or a move would place the code in a country where the same string exists. Choose another string or location.
code_protected409The string is on GP Code's reserved list. Choose another.
code_rejected409The string failed content screening. Choose another.
not_manageable409The customer moved this code to their own GP Code account. You can still read it, but no longer change or delete it.
validity_expired422The code is outside its validity period and can no longer be changed.

Allowance & service

CodeHTTPCause and fix
quota_exhausted402Your allowance for this code type is used up. Nothing was written. Delete unused codes or contact GP Code to extend your contract. Check quota first.
pool_depleted503No standard code is available right now. Retry later.
client_misconfigured503Your partner account has no allowance set up. Contact GP Code.
server_error500Unexpected error. Retry with back-off (same Idempotency-Key); contact support if it persists.

Handling errors


                # -w prints the status after the body; -s hides the progress bar.
                curl -s -w "\n%{http_code}\n" {{BASE}}/gp-codes/NOPE \
                  -H "X-GoPeeko-Key: $GPCODE_KEY" \
                  -H "Authorization: Bearer $GPCODE_SECRET"
                

                use GuzzleHttp\Exception\ClientException;

                try {
                    $client->post('gp-codes', ['json' => $payload]);
                } catch (ClientException $e) {
                    $error = json_decode($e->getResponse()->getBody(), true)['error'] ?? null;

                    match ($error['code'] ?? null) {
                        'quota_exhausted'  => notifyAccountManager(),
                        'code_unavailable' => askCustomerForAnotherCode(),
                        'invalid_request'  => logFields($error['details']['fields']),
                        default            => throw $e,
                    };
                }
                

                const res = await fetch(`${BASE}/gp-codes`, { method: 'POST', headers, body: JSON.stringify(payload) });

                if (!res.ok) {
                  const body = await res.json().catch(() => ({}));
                  switch (body.error?.code) {
                    case 'quota_exhausted': return notifyAccountManager();
                    case 'code_unavailable': return askCustomerForAnotherCode();
                    case 'invalid_request': return logFields(body.error.details.fields);
                    default: throw new Error(`GP Code API ${res.status}: ${body.error?.message ?? body.message}`);
                  }
                }
                

                res = session.post(f"{BASE}/gp-codes", json=payload)

                if not res.ok:
                    error = res.json().get("error", {})
                    code = error.get("code")
                    if code == "quota_exhausted":
                        notify_account_manager()
                    elif code == "code_unavailable":
                        ask_customer_for_another_code()
                    elif code == "invalid_request":
                        log_fields(error["details"]["fields"])
                    else:
                        res.raise_for_status()