Docs / API reference

GP codes

A GP code is a short string that resolves to a place. Each code has a public page at https://gpcode.com/<code> and a QR image that encodes it. You can manage every code you created through the API.

Addressing a codeEndpoints take the code string in the path: /gp-codes/12345. Codes are unique per country, so if you hold the same custom string in more than one country, add ?country=MT — otherwise you get 409 ambiguous_code with the list of countries.

The GP code object

idinteger

GP Code's id for the code.

codestring

The code string, e.g. 12345 or ACMEDEPOT01.

typeenum

standard (assigned from the GP Code pool) or custom (a string you chose).

statusenum

One summary value, first match wins:

expired

Outside its validity period.

pending_approval

Custom code awaiting GP Code review.

quota_suspended

Switched off because the allowance was exceeded.

inactive

is_active is false.

active

Live and resolving.

locationstring · null

Short place name, e.g. "Front door". Defaults to the code itself.

addressstring · null

Full address — as you sent it, or found by reverse geocoding.

lat, lngnumber

Coordinates in decimal degrees.

countryobject · null

{ code, name } — ISO alpha-2 code and English name.

ownerobject

Who the code belongs to.

kindenum

user — one of your users · partner — your partner account · personal — the customer moved it to their own GP Code account (read-only for you).

user_idinteger · null

Set when kind is user.

external_refstring · null

Your reference of the owning customer.

detailsobject

Optional postal and contact details, all free text or null: zip_code, city, street, house_number, place_number, phone_country_code, phone_number.

is_activeboolean

Your on/off switch for the code.

approvedboolean

False while a custom code awaits review.

quota_suspendedboolean

True when switched off because the allowance was exceeded.

valid_from, valid_todate-time

Validity period. New codes are valid from creation for the number of days in your partner settings (365 by default).

urluri

Public page of the code. This is what the QR encodes.

created_at, updated_atdate-time

Timestamps.

qrobject

Only on single-code responses (create, retrieve), unless ?qr=false.

formatstring

Always png.

encodesuri

The URL in the QR.

data_urlstring

400×400 PNG as a base64 data URL, ready for <img src>. Same image as on GP Code's printable PDF.


                        {
                          "id": 91733,
                          "code": "12345",
                          "type": "standard",
                          "status": "active",
                          "location": "Front door",
                          "address": "Triq il-Kbira 5, Valletta",
                          "lat": 35.8989,
                          "lng": 14.5146,
                          "country": { "code": "MT", "name": "Malta" },
                          "owner": {
                            "kind": "user",
                            "user_id": 8842,
                            "external_ref": "cust-4417"
                          },
                          "details": {
                            "zip_code": null,
                            "city": "Valletta",
                            "street": null,
                            "house_number": "5",
                            "place_number": null,
                            "phone_country_code": null,
                            "phone_number": null
                          },
                          "is_active": true,
                          "approved": true,
                          "quota_suspended": false,
                          "valid_from": "2026-11-01T10:00:00+00:00",
                          "valid_to": "2027-11-01T10:00:00+00:00",
                          "url": "https://gpcode.com/12345",
                          "created_at": "2026-11-01T10:00:00+00:00",
                          "updated_at": "2026-11-01T10:00:00+00:00"
                        }
                        

Create a code

POST/gp-codescodes:write

Creates one standard or custom code from coordinates, an address or both, and books it against your allowance.

  • Standard codes take the next code from the GP Code pool and are active at once.
  • Custom codes need gp_code. The string is checked for availability in its country, against the reserved list and by content screening. The code starts as pending_approval and goes live once GP Code approves it.

Location

  • lat + lng: the pin decides the country. address is stored as sent; without it, it is filled by reverse geocoding.
  • address only: geocoded to coordinates; 422 address_not_found if that fails.
  • country narrows the address search and is the fallback when the pin's country cannot be determined.

Owner

  • user: { external_ref, name } — the user is found or created (user_created in the response tells you which);
  • user_id — one of your existing users (404 otherwise);
  • neither — the code belongs to your partner account.

Headers

Idempotency-Keystring

Recommended. Your unique id for this creation (max. 191). For 24 h a retry with the same key returns the first code with 200. See Idempotency.

Query parameters

qrboolean

Default true. false leaves the QR out of the response.

Body parameters

typeenumrequired

standard or custom.

gp_codestring

Required for custom, not allowed for standard. 4–30 letters and digits (A–Z, 0–9).

lat, lngnumber

Required together, unless address is sent. −90…90 and −180…180.

addressstring

Required unless lat/lng are sent. Max. 500.

countrystring

ISO 3166-1 alpha-2 hint, e.g. MT. The pin's own country wins.

locationstring

Short place name, max. 500. Defaults to the code.

userobject

Owner by your reference. Not together with user_id.

external_refstringrequired

Max. 191, no @.

namestring

Max. 250. Used only when the user is created.

user_idinteger

Owner by id; must be one of your users. Not together with user.

zip_codestring

Max. 30.

city, streetstring

Max. 255 each.

house_number, place_numberstring

Max. 50 each.

phone_country_codestring

Max. 16, e.g. +356.

phone_numberstring

Max. 50.

Detail fields are sent at the top level and come back under details.

Returns

201 with the GP code plus user_created (boolean) and qr. 200 with the same shape for an idempotent replay.

StatusError code
402quota_exhausted — nothing was written
404not_found — user_id is not one of yours
409code_unavailable, code_protected, code_rejected, request_in_progress
422invalid_request, address_not_found, country_unresolved
503pool_depleted, client_misconfigured

                        # Standard code from a map pin, for a new or existing user
                        curl {{BASE}}/gp-codes \
                          -H "X-GoPeeko-Key: $GPCODE_KEY" \
                          -H "Authorization: Bearer $GPCODE_SECRET" \
                          -H "Content-Type: application/json" \
                          -H "Idempotency-Key: order-4417-a" \
                          -d '{
                            "type": "standard",
                            "lat": 35.8989,
                            "lng": 14.5146,
                            "location": "Front door",
                            "house_number": "5",
                            "user": { "external_ref": "cust-4417", "name": "Depot 4417" }
                          }'
                        

                        // Standard code from a map pin, for a new or existing user
                        $res = $client->post('gp-codes', [
                            'headers' => ['Idempotency-Key' => 'order-4417-a'],
                            'json'    => [
                                'type'         => 'standard',
                                'lat'          => 35.8989,
                                'lng'          => 14.5146,
                                'location'     => 'Front door',
                                'house_number' => '5',
                                'user'         => ['external_ref' => 'cust-4417', 'name' => 'Depot 4417'],
                            ],
                        ]);

                        $code = json_decode($res->getBody(), true)['data'];
                        file_put_contents("{$code['code']}.png", base64_decode(explode(',', $code['qr']['data_url'])[1]));
                        

                        // Standard code from a map pin, for a new or existing user
                        const res = await fetch(`${BASE}/gp-codes`, {
                          method: 'POST',
                          headers: { ...headers, 'Content-Type': 'application/json', 'Idempotency-Key': 'order-4417-a' },
                          body: JSON.stringify({
                            type: 'standard',
                            lat: 35.8989,
                            lng: 14.5146,
                            location: 'Front door',
                            house_number: '5',
                            user: { external_ref: 'cust-4417', name: 'Depot 4417' },
                          }),
                        });

                        const { data: code } = await res.json();
                        const png = Buffer.from(code.qr.data_url.split(',')[1], 'base64');
                        

                        # Standard code from a map pin, for a new or existing user
                        import base64

                        res = session.post(
                            f"{BASE}/gp-codes",
                            headers={"Idempotency-Key": "order-4417-a"},
                            json={
                                "type": "standard",
                                "lat": 35.8989,
                                "lng": 14.5146,
                                "location": "Front door",
                                "house_number": "5",
                                "user": {"external_ref": "cust-4417", "name": "Depot 4417"},
                            },
                        )

                        code = res.json()["data"]
                        png = base64.b64decode(code["qr"]["data_url"].split(",", 1)[1])
                        

                        // From an address, owned by your partner account
                        {
                          "type": "standard",
                          "address": "Triq il-Kbira 12, Birkirkara",
                          "country": "MT"
                        }

                        // Custom code for an existing user
                        {
                          "type": "custom",
                          "gp_code": "ACMEDEPOT01",
                          "lat": 35.8971,
                          "lng": 14.4611,
                          "user_id": 8842
                        }
                        

                        {
                          "data": {
                            "id": 91733,
                            "code": "12345",
                            "type": "standard",
                            "status": "active",
                            "location": "Front door",
                            "address": "Triq il-Kbira 5, Valletta",
                            "lat": 35.8989,
                            "lng": 14.5146,
                            "country": { "code": "MT", "name": "Malta" },
                            "owner": { "kind": "user", "user_id": 8842, "external_ref": "cust-4417" },
                            "details": { "zip_code": null, "city": null, "street": null, "house_number": "5", "place_number": null, "phone_country_code": null, "phone_number": null },
                            "is_active": true,
                            "approved": true,
                            "quota_suspended": false,
                            "valid_from": "2026-11-01T10:00:00+00:00",
                            "valid_to": "2027-11-01T10:00:00+00:00",
                            "url": "https://gpcode.com/12345",
                            "created_at": "2026-11-01T10:00:00+00:00",
                            "updated_at": "2026-11-01T10:00:00+00:00",
                            "user_created": true,
                            "qr": {
                              "format": "png",
                              "encodes": "https://gpcode.com/12345",
                              "data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA…"
                            }
                          }
                        }
                        

List codes

GET/gp-codescodes:read

Every code you created, newest first, paginated. Deleted codes are excluded. Lists carry no QR; retrieve a code for its QR.

Query parameters

user_idinteger

Only codes owned by this user of yours.

external_refstring

Only codes created for the customer with this reference.

typeenum

standard or custom.

countrystring

ISO alpha-2, e.g. MT.

pageinteger

Default 1.

per_pageinteger

1–100. Default 25.

Returns

200 with data (array of GP codes) and meta.


                        curl "{{BASE}}/gp-codes?external_ref=cust-4417&type=standard" \
                          -H "X-GoPeeko-Key: $GPCODE_KEY" \
                          -H "Authorization: Bearer $GPCODE_SECRET"
                        

                        $res = $client->get('gp-codes', [
                            'query' => ['external_ref' => 'cust-4417', 'type' => 'standard'],
                        ]);
                        $codes = json_decode($res->getBody(), true)['data'];
                        

                        const qs = new URLSearchParams({ external_ref: 'cust-4417', type: 'standard' });
                        const res = await fetch(`${BASE}/gp-codes?${qs}`, { headers });
                        const { data: codes, meta } = await res.json();
                        

                        body = session.get(
                            f"{BASE}/gp-codes",
                            params={"external_ref": "cust-4417", "type": "standard"},
                        ).json()
                        codes, meta = body["data"], body["meta"]
                        

                        {
                          "data": [
                            {
                              "id": 91733,
                              "code": "12345",
                              "type": "standard",
                              "status": "active",
                              "location": "Front door",
                              "country": { "code": "MT", "name": "Malta" },
                              "owner": { "kind": "user", "user_id": 8842, "external_ref": "cust-4417" },
                              "url": "https://gpcode.com/12345",
                              "…": "…"
                            }
                          ],
                          "meta": { "current_page": 1, "per_page": 25, "last_page": 1, "total": 1 }
                        }
                        

Retrieve a code

GET/gp-codes/{code}codes:read

One of your codes, with its QR. Also returns codes your customer has since moved to their own GP Code account (owner.kind = personal); those are read-only.

Path parameters

codestringrequired

The code string, letters and digits only.

Query parameters

countrystring

Needed only when you hold the same string in several countries.

qrboolean

Default true. false leaves the QR out.

Returns

200 with the GP code plus qr. Errors: 404 not_found · 409 ambiguous_code.


                        curl {{BASE}}/gp-codes/12345 \
                          -H "X-GoPeeko-Key: $GPCODE_KEY" \
                          -H "Authorization: Bearer $GPCODE_SECRET"
                        

                        $code = json_decode($client->get('gp-codes/12345')->getBody(), true)['data'];
                        echo '<img src="' . $code['qr']['data_url'] . '" alt="GP code ' . $code['code'] . '">';
                        

                        const res = await fetch(`${BASE}/gp-codes/12345`, { headers });
                        const { data: code } = await res.json();
                        

                        code = session.get(f"{BASE}/gp-codes/12345").json()["data"]
                        

                        {
                          "data": {
                            "id": 91733,
                            "code": "12345",
                            "status": "active",
                            "url": "https://gpcode.com/12345",
                            "…": "…",
                            "qr": {
                              "format": "png",
                              "encodes": "https://gpcode.com/12345",
                              "data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA…"
                            }
                          }
                        }
                        

Update a code

PATCH/gp-codes/{code}codes:write

Send only what changes. The code string, type and owner cannot change.

  • Move the code: send lat + lng and/or address (same rules as on create). If the new location is in another country where the same string already exists: 409 code_unavailable.
  • Details: a detail field sent as null or "" is cleared; fields you leave out are kept.
  • location sent empty resets it to the code string.

Refused with 422 validity_expired outside the code's validity period and with 409 not_manageable when the customer has moved the code to their own GP Code account.

Query parameters

countrystring

Needed only for an ambiguous code string.

Body parameters

locationstring · null

Short place name, max. 500.

is_activeboolean

Switch the code on or off.

lat, lngnumber

New pin. Together.

addressstring

New address, max. 500.

countrystring

ISO alpha-2 hint for the new location.

zip_code, city, street, house_number, place_number, phone_country_code, phone_numberstring · null

Detail fields, same limits as on create.

Returns

200 with the updated GP code (no QR — the QR never changes, since the code string doesn't).

Errors: 404 not_found · 409 ambiguous_code | code_unavailable | not_manageable · 422 invalid_request | address_not_found | country_unresolved | validity_expired.


                        curl -X PATCH {{BASE}}/gp-codes/12345 \
                          -H "X-GoPeeko-Key: $GPCODE_KEY" \
                          -H "Authorization: Bearer $GPCODE_SECRET" \
                          -H "Content-Type: application/json" \
                          -d '{ "location": "Back gate", "is_active": false, "city": "Valletta" }'
                        

                        $res = $client->patch('gp-codes/12345', [
                            'json' => ['location' => 'Back gate', 'is_active' => false, 'city' => 'Valletta'],
                        ]);
                        $code = json_decode($res->getBody(), true)['data'];
                        

                        const res = await fetch(`${BASE}/gp-codes/12345`, {
                          method: 'PATCH',
                          headers: { ...headers, 'Content-Type': 'application/json' },
                          body: JSON.stringify({ location: 'Back gate', is_active: false, city: 'Valletta' }),
                        });
                        const { data: code } = await res.json();
                        

                        code = session.patch(
                            f"{BASE}/gp-codes/12345",
                            json={"location": "Back gate", "is_active": False, "city": "Valletta"},
                        ).json()["data"]
                        

                        {
                          "data": {
                            "id": 91733,
                            "code": "12345",
                            "status": "inactive",
                            "location": "Back gate",
                            "details": { "city": "Valletta", "house_number": "5", "…": "…" },
                            "is_active": false,
                            "…": "…"
                          }
                        }
                        

Delete a code

DELETE/gp-codes/{code}codes:write

The code stops resolving immediately and its place in your allowance is freed. A deleted code no longer appears in lists or lookups.

Refused with 409 not_manageable when the customer has moved the code to their own GP Code account.

Query parameters

countrystring

Needed only for an ambiguous code string.

Returns

204 No Content. Errors: 404 not_found · 409 ambiguous_code | not_manageable.

Deletion cannot be undone through the API. Printed QR codes for a deleted code stop working.


                        curl -X DELETE {{BASE}}/gp-codes/12345 \
                          -H "X-GoPeeko-Key: $GPCODE_KEY" \
                          -H "Authorization: Bearer $GPCODE_SECRET"
                        

                        $client->delete('gp-codes/12345'); // 204
                        

                        const res = await fetch(`${BASE}/gp-codes/12345`, { method: 'DELETE', headers });
                        console.log(res.status); // 204
                        

                        res = session.delete(f"{BASE}/gp-codes/12345")
                        assert res.status_code == 204
                        

                        # empty body