On this page
Developers
Genel Kurye POS API
This API lets a POS or order system send a restaurant's orders to the Genel Kurye courier pool for delivery.
It is published by S.S. Genel Kurye Kooperatifi (the cooperative).
Terms used in this document:
- Order: the order in the POS. Delivery: the courier job Genel Kurye opens for an order. The Turkish messages say sipariş for order and teslimat for delivery.
- Store: one restaurant's connection to your POS, identified by its Store ID.
- Keys: a restaurant's Store ID, API Key and API Secret, together.
- Pool: where open deliveries wait for a courier.
- Platform courier: a courier of the marketplace the order came from, for example a Yemeksepeti courier, who delivers the order instead of a Genel Kurye courier.
Contact: iletisim@genelkuryerkooperatifi.org
Plain Markdown: https://www.genelkuryerkooperatifi.org/developers.md
Last updated 2 November 2026 · v1
View as MarkdownEndpoints at a glance
Basics
How it works
A restaurant connects its POS with three values, and from then on the POS sends its orders to Genel Kurye.
- In the Genel Kurye panel, the restaurant opens the page for your POS under Ayarlar → Entegrasyonlar (Settings → Integrations) and creates its keys: a Store ID, an API Key and an API Secret.
- The restaurant enters the keys in the Genel Kurye fields in the POS. When
the POS saves them, it can check them with
GET /store. - When the restaurant sends an order to Genel Kurye, the POS calls
POST /deliveries. The delivery appears in the cooperative's courier pool. - As the delivery's status changes, Genel Kurye sends signed webhook
events to the webhook URL registered for the POS. The POS can also read the
status with
GET /deliveries/{id}. - If the restaurant cancels the order, the POS calls
POST /deliveries/{id}/cancel.
If the POS sends every order of the restaurant, including the ones a
platform courier delivers, it marks those with "delivery_by": "platform".
Genel Kurye then records the order and sends no courier (see
Platform deliveries).
The pickup address is always the address saved in the restaurant's Genel Kurye profile. The POS sends only the drop-off address.
Basics
Getting access
The cooperative sets up your access by hand.
Write to iletisim@genelkuryerkooperatifi.org. The cooperative sends you these through a secure channel:
- a test store: a Store ID, an API Key and an API Secret,
- your partner key,
- your webhook secret.
Send the cooperative your webhook URL. It must start with https:// and must
not contain a user name or password. One URL serves all your stores. Events are
not sent to any other kind of URL.
Basics
Base URL and format
Every request goes to one base URL, over HTTPS, with JSON bodies.
| Setting | Value |
|---|---|
| Base URL | https://api.genelkuryerkooperatifi.org/api/pos/v1 |
| Protocol | HTTPS only |
| Body | JSON, UTF-8, Content-Type: application/json |
| Time | ISO 8601. Times in responses are UTC (2026-10-17T10:30:00.000Z). Always include a time zone (+03:00 or Z) in the times you send |
Paths in the endpoint reference are relative to the base URL. The examples show
the full request. A path or method that is not in the reference gets
404 unknown_endpoint. The one exception is a body that is not valid JSON
or is larger than 64 KB: it gets the 400 or 413 described at the end of
Errors, whatever the path.
Basics
Authentication
Every request carries four headers: one from the POS company and three from the restaurant.
Every example in this reference shows all four.
- X-Partner-Keystringrequired
One key per POS company, the same for all its restaurants. The cooperative gives it to the POS company. Keep it on your server and never show it to the restaurant. Never put it in a desktop or mobile build. When the cooperative changes the key, the old key and the new key both work until the cooperative turns the old one off. Example:
5f0c… - X-Store-Idstringrequired
The restaurant's Store ID: 8 digits, not starting with 0. Example:
48213907 - X-Api-Keystringrequired
The restaurant's API Key:
gk_followed by 24 letters or digits. Example:gk_XXXXXXXXXXXXXXXXXXXXXXXX - X-Api-Secretstringrequired
The restaurant's API Secret:
gks_followed by 40 letters or digits
- The partner key makes sure a restaurant's keys work only from this POS. If it
is missing or wrong, the response is
401 unauthorized. - The restaurant's three values are checked together. If any one is wrong, the
response is
401 unauthorized, and it does not say which. - The restaurant can see its API Secret again at any time in the Genel Kurye panel. Genel Kurye stores the secret encrypted and checks each request against its SHA-256 hash.
- If the restaurant creates new keys in the panel, the old API Key and API Secret stop working immediately. The Store ID does not change.
- If the restaurant removes the connection, its keys are deleted and every
later request gets
401. A restaurant that reconnects gets a new Store ID. Order IDs are unique within a store, so the sameexternal_order_idopens a new delivery under the new Store ID.
Basics
Testing
Test against the same base URL, with a test store.
There is no separate test server. The cooperative opens a test store for your engineering team and sends you its keys.
Every order sent to the test store must include "test": true. An order
without it is rejected with 403 test_order_required and nothing about its
customer is stored, so if the test keys are ever entered in a real
restaurant's POS by mistake, no real customer data is accepted. A real store rejects
"test": true with 400 invalid_request.
Use drop-off addresses within 50 km of the test store's pickup address. An
order farther away is rejected with 422 address_too_far.
Deliveries opened by the test store are visible only to test couriers, so no real courier is sent. The cooperative's test couriers can accept these deliveries and move them through every status, so you receive every status event. Ask the cooperative to move a test delivery forward when you want to see a status event.
Basics
Errors
Errors share one JSON shape, with one exception described at the end of this section.
codeis stable. Branch on it in your code.messageis Turkish text that you can show to the restaurant unchanged. Do not parse it.fieldis present only for errors about a field in the body or about theexternal_order_idquery parameter.
Error codes
The Message column shows one sentence the server sends with each code. Some codes also have other sentences.
- 400
The body or a field is invalid (
fieldsays which)Message: payment.method şunlardan biri olmalı: online, cash, card, meal_card.
- 401
- 403
The restaurant's Genel Kurye account is closed. Normally the keys are removed with the account, and requests get
401insteadMessage: İşletmenin Genel Kurye hesabı kapalı.
- 403
The restaurant paused the connection in the panel.
POST /deliveriesis refused; the other endpoints still workMessage: İşletme Genel Kurye bağlantısını duraklatmış; sipariş alınmadı.
- 403
The cooperative has not approved this restaurant for order transfer yet
Message: Kooperatif bu işletme için sipariş aktarımını henüz açmadı; sipariş alınmadı.
- 403
The restaurant's profile is missing a pickup address, province, location or business type
Message: İşletmenin Genel Kurye profilinde alış adresi, ili, konumu ya da işletme türü eksik; sipariş alınmadı.
- 403
The connection was stopped. The restaurant must create new keys
Message: Bu bağlantı durdurulmuş. İşletme Genel Kurye panelinden yeni anahtar oluşturmalı.
- 403
An order was sent to a test store without
"test": trueMessage: Test mağazası yalnızca "test": true işaretli siparişleri alır; sipariş kaydedilmedi.
- 404
There is no such delivery for this restaurant
Message: Bu mağazada böyle bir teslimat yok.
- 404
The path is not an endpoint of this API, or the endpoint does not take this method. The headers do not change this response, but a body that is not valid JSON gets the
400described below first. Check the base URL, the path and the methodMessage: Geçersiz endpoint: istek gönderdiğiniz yol ya da HTTP yöntemi POS API'de yok. API dokümantasyonunu kontrol edin.
- 409
The delivery is
delivered, so it cannot be cancelledMessage: Teslimat tamamlanmış; iptal edilemez.
- 409
The package cannot be handed over: no courier is assigned yet (
pending), the delivery iscancelled,returningorreturned, or a platform courier delivers it (platform)Message: Bu teslimata henüz kurye atanmadı.
- 409
A platform status was sent for a delivery that is not
platformMessage: Bu teslimatı platform kuryesi götürmüyor; platform durumu yazılamaz.
- 422
address.latandaddress.lngare missing or outside Turkey, and the address lookup found nothing or did not answer (see Location)Message: Teslim adresi haritada bulunamadı; teslimat açılmadı. Teslimatı Genel Kurye panelindeki Yeni teslimat formundan elle açın.
- 422
The drop-off point is more than 50 km from the pickup address, and the address lookup found no point within 50 km (see Location).
fieldisaddress. The distance is inmessageMessage: Teslim adresi alış adresine 50 km'den uzak (yaklaşık 530 km); teslimat açılmadı. Adresi ve işletmenin Genel Kurye profilindeki alış adresini kontrol edin.
- 422
The delivery could not be opened. The reason is in
messageMessage: İşletme türün artık kullanılmıyor. Profil'de güncelle.
- 429
The per-minute request limit was exceeded (see the
Retry-Afterheader)Message: Çok fazla istek gönderildi. Biraz sonra tekrar deneyin.
- 429
The hourly delivery limit was exceeded (
Retry-After)Message: Bu mağaza için son bir saatte açılabilecek teslimat sınırına ulaşıldı. Biraz sonra tekrar deneyin.
- 429
The store used up its address lookups for orders that need one (
Retry-After)Message: Bu mağaza için adres arama sınırı doldu; teslimat açılmadı. Biraz sonra tekrar deneyin ya da teslimatı Genel Kurye panelinden elle açın.
- 500
Unexpected error. Send the same request again
Message: Beklenmeyen bir hata oldu. Biraz sonra tekrar deneyin.
- 503
The address lookup is busy (
Retry-After)Message: Adres şu an konuma çevrilemiyor. Birkaç saniye sonra tekrar deneyin.
Basics
Limits
Limits apply to each store. The body size applies to every request.
| Limit | Value | When exceeded |
|---|---|---|
| Request body | 64 KB | 413, in the server's own format (see the end of Errors) |
| Requests | 120 per minute | 429 rate_limited |
| New deliveries | 100 per hour | 429 hourly_limit |
| Address lookups | For orders that need one (Location): 20 per 10 minutes, retries included | 429 geocode_limit. 503 busy if the store already has a lookup running, or if the wait for a free lookup slot would pass 3 seconds |
Endpoints
Check the connection
Confirms that the keys work and tells you whether the store is accepting orders right now.
Call it when the restaurant saves the keys on your settings screen.
name is the restaurant's name in Genel Kurye, or null. provider is the ID
of your POS at Genel Kurye. test is true for a test store.
If accepting_orders is false, reason is one of paused, not_approved or
profile_incomplete. These are the codes POST /deliveries returns in the
same situations.
Endpoints
Create a delivery
Opens a delivery for an order.
Body parameters
Free text longer than its limit is cut. An identifier or a code that is too
long is rejected with 400 invalid_request.
- external_order_idstringrequired
The order's ID in the POS: 1–64 printable ASCII characters, no spaces. Turkish letters and other non-ASCII characters are rejected. Unique within a store
- testbooleanrequired in a test store
truemarks a test order. Send it only to a test store: a real store returns400 invalid_request - platformstring
Where the order came from:
yemeksepeti,trendyol,getir,migros,tiklagelsin,phone,web, or another short name of up to 40 characters (a–z, digits,-,_). Turkish letters are converted to a–z - platform_order_codestring
A short code the courier uses to match the package: the platform's order code or the receipt number. Up to 20 printable ASCII characters. Turkish letters are rejected
- brandstring
The brand the order was sold under, when one restaurant sells under several brands from one kitchen, for example
Beast Burger. Couriers see it next to the order code and use it to find the right package at the counter. Up to 60 characters - ordered_atstring
When the order was placed, with a time zone:
2026-10-17T10:30:00+03:00 - prep_minutesinteger
The kitchen's estimated preparation time, 0–240. The courier sees it as "about 15 minutes"
- package_countinteger
Number of packages, 1–50. Default 1
- delivery_bystring
Who delivers the order:
restaurant(a Genel Kurye courier) orplatform(a platform courier, see Platform deliveries). Defaultrestaurant; an empty string counts as not sent. Upper and lower case are the same. Any other value returns400 invalid_requestwithfieldset todelivery_by - customer.namestring
Shown to the courier as "Ayşe Y." once the courier accepts the delivery. Up to 120 characters
- customer.phonestring
7–15 digits, up to 30 characters in all.
+is allowed only at the start; spaces,-and parentheses are allowed. A masked number from a platform is accepted, also as one line with the code to dial after a slash:0212 365 34 03 / 117 678 795 07. A phone that is not a number does not refuse the order: the courier sees it as sent, once the package is picked up - customer.phone_codestring
A code to dial after a masked number connects: up to 20 digits,
*and#. Ignored withoutcustomer.phone - address.linestringrequired unless delivery_by is platform
Street and door number, for example
Moda Cad. No:12. Up to 300 characters - address.neighborhoodstring
Neighborhood. Up to 80 characters
- address.districtstring
District. Up to 60 characters
- address.citystring
City or province. Up to 60 characters
- address.buildingstring
Building. Up to 30 characters
- address.floorstring
Floor. Up to 30 characters
- address.apartmentstring
Apartment. Up to 30 characters
- address.directionsstring
Directions to the address. Up to 300 characters
- address.latnumberrecommended
Latitude of the drop-off point. Send it together with
address.lng: sending only one returns400 invalid_request - address.lngnumberrecommended
Longitude of the drop-off point. A latitude and longitude of
0and0count as no location - payment.methodstring
online(already paid),cash(cash at the door),card(card at the door) ormeal_card(meal card at the door) - payment.amount_to_collectnumber
Amount to collect at the door, in ₺: from 0 to less than 1,000,000. Ignored for
online. An amount above 0 requirespayment.method - itemsarray
The order's lines. Only the first 30 are stored. Item names are visible to couriers while the delivery is in the pool. A
noteinside an item is ignored - items[].namestringrequired
The item's name. Up to 120 characters
- items[].quantityinteger
1–99. Default 1
- notestring
The customer's note. Up to 300 characters
Location
Always send the location that the platform gives you. A location outside Turkey (latitude 35–43, longitude 25–45) counts as no location.
Genel Kurye looks the address up on a map when there is no location, or when the location is more than 50 km in a straight line from the pickup address. The lookup is slow and does not find every address. It is limited to 20 orders per store in 10 minutes. Every order that needs a lookup counts, retries included.
- If there is no location, and the lookup finds nothing or does not answer,
the response is
422 address_not_found. - If the location you sent, or the point the lookup found, is more than 50 km
from the pickup address, and the lookup found no point within 50 km, the
response is
422 address_too_far.messagegives the distance in whole kilometres, rounded up: the distance of the location you sent if it was too far, otherwise that of the first point the lookup found. Check the drop-off address. Also check the pickup address in the restaurant's Genel Kurye profile.
Genel Kurye remembers what a lookup found for 10 minutes and does not look the same address up again during that time. The remembered point is compared with the restaurant's current pickup address on every request, so a corrected pickup address takes effect at once.
Response
A new delivery returns 201 Created. The body is a
delivery object.
Sending the same order again
A second request with the same external_order_id does not open a new
delivery. It returns the delivery the first request opened, with 200 OK, and
ignores the other fields in the body. If a request times out or fails on the
network, send the same request again. If the order changes, cancel the delivery
and send it again with a new external_order_id.
Platform deliveries
Some orders are delivered by a platform courier, not by Genel Kurye. If your
POS sends all of a restaurant's orders, send these with
"delivery_by": "platform".
- The response is
201 Createdwithstatusplatformanddelivery_byplatform. No courier is sent, the address is not looked up, and the delivery never appears in the pool. - Only the order's own fields are read:
external_order_id,test,platform,platform_order_code,brand,ordered_at,prep_minutes,package_count,payment,items,address.districtandaddress.city.customer, the rest ofaddressandnoteare not needed. If you send them, they are ignored and not stored. - The restaurant sees the order in its Genel Kurye panel, marked as a platform delivery.
platformis final. No webhook is sent for it.- To pass on what the platform courier does, call
POST /deliveries/{id}/platform-status. - An order sent without
delivery_bygets a Genel Kurye courier. If the restaurant tells the cooperative that a platform courier took it, the cooperative can move the delivery toplatformwhile it is stillpending. Your POS sees the change on its nextGET /deliveries/{id}. No webhook is sent. - The new-delivery limit (100 per hour) counts platform deliveries too.
Endpoints
Get a delivery
Returns one delivery by its ID.
You can only read deliveries that this restaurant opened through your POS. The
ID of any other delivery returns 404 not_found.
Path parameters
- idstringrequired
The
idthatPOST /deliveriesreturned
Endpoints
Find a delivery by order ID
Looks up a delivery by the POS's own order ID.
The response is a list with at most one item. The example is shortened: each
item in the list is a full delivery object. If there is no such order, the
response is { "data": [] }.
Query parameters
- external_order_idstringrequired
The order's ID in the POS, as sent to
POST /deliveries. Without a valid ID the response is400 invalid_requestwithfieldset toexternal_order_id
Endpoints
Cancel a delivery
Cancels a delivery. If the courier already has the package, they bring it back
to the restaurant. On a platform delivery, it records that the order was
cancelled on the platform.
Path parameters
- idstringrequired
The
idthatPOST /deliveriesreturned
Body parameters
- reasonstring
Why the order was cancelled. Genel Kurye puts it into a fixed sentence, which the courier and the restaurant see:
Sipariş Örnek POS'ta iptal edildi (Müşteri vazgeçti).(in English: "Order cancelled in Örnek POS (customer changed their mind)."). The sentence names your POS. Yourreasongoes between the brackets unchanged, so send a short Turkish phrase, not a code such ascustomer_cancelled. Without areasonthe brackets are left out. Text longer than 160 characters is cut
Result by status
| Delivery status | Result |
|---|---|
pending, assigned | cancelled. If a courier is assigned, they are notified |
picked_up, in_transit | returning. The courier already has the package, so they bring it back to the restaurant, and then the delivery becomes returned |
cancelled, returning, returned | No change. The response is 200 with the unchanged delivery |
delivered | 409 not_cancellable |
platform | status stays platform and platform_status becomes cancelled, as with Report platform progress. reason is not stored. If platform_status is already delivered or cancelled, nothing changes. The response is 200 either way |
Endpoints
Hand over a package
Tells Genel Kurye that the restaurant gave the package to the courier.
This is the same as the "I gave the package to the courier" button in the restaurant's panel. Use it when the courier cannot enter the pickup code, for example because their phone is off.
Path parameters
- idstringrequired
The
idthatPOST /deliveriesreturned
Result by status
| Delivery status | Result |
|---|---|
assigned | picked_up |
picked_up, in_transit, delivered | No change. The response is 200 with the unchanged delivery |
pending | 409 not_assigned: no courier is assigned yet |
cancelled, returning, returned | 409 not_assigned |
platform | 409 not_assigned: a platform courier delivers it, so no Genel Kurye courier is assigned. The message is Bu teslimatı platform kuryesi götürüyor; paketin alındığını POST /deliveries/:id/platform-status ile bildirin. |
Endpoints
Report platform progress
Tells Genel Kurye what the platform courier did with a platform delivery.
The restaurant sees it in its panel. Nothing else changes: the delivery
stays platform, and no webhook is sent. Calling it is optional.
Path parameters
- idstringrequired
The
idthatPOST /deliveriesreturned
Body parameters
- statusstringrequired
picked_up(the platform courier took the package),delivered(the platform courier delivered it) orcancelled(the order was cancelled on the platform). Upper and lower case are the same. A missing or unknown value returns400 invalid_requestwithfieldset tostatus - occurred_atstring
When it happened, with a time zone:
2026-10-17T10:52:00+03:00. Without it, the time Genel Kurye received the request. A time in the future counts as now. A value that is not a time returns400 invalid_requestwithfieldset tooccurred_at
Result by status
The platform status only moves forward: picked_up, then delivered or
cancelled. You can skip picked_up. delivered and cancelled are final.
| Delivery | Result |
|---|---|
platform, and the new status moves forward | platform_status and platform_status_at are set. The response is 200 |
platform, and the new status does not move forward (the same status again, or picked_up after delivered) | No change. The response is 200 with the unchanged delivery |
| Any other status | 409 not_platform |
Reference
Delivery object
The delivery as every endpoint and every webhook event returns it.
- idstring
The delivery's ID in Genel Kurye
- codestring
The delivery's code (
KRY-…). The courier and the restaurant see this code - store_idstring
The Store ID, the same value as
X-Store-Id - external_order_idstring
The ID the POS sent
- statusstring
One of the statuses
- testboolean
Whether the delivery belongs to a test store
- courierobject | null
{ "name", "phone" }.phoneis the courier's own number. See Personal data.nulluntil a courier is assigned - pickup_codestring | null
The 6-digit pickup code.
nullunless the status isassigned - cancel_reasonstring | null
Why the delivery was cancelled, including a cancel after pickup
- fail_reasonstring | null
Why a package came back undelivered:
not_home,wrong_address,refused,phone_unreachableorother. Today it is alwaysnullfor deliveries opened through this API - created_atstring
When the delivery was opened
- assigned_atstring | null
When a courier accepted the delivery
- picked_up_atstring | null
When the courier took the package
- delivered_atstring | null
When the package was delivered
- cancelled_atstring | null
When the delivery was cancelled
- returned_atstring | null
When the package was brought back to the restaurant
- delivery_bystring
platformwhen a platform courier delivers the order (statusplatform), otherwiserestaurant - platform_statusstring | null
The last platform status you reported:
picked_up,deliveredorcancelled.nulluntil you report one, and on every delivery that is notplatform - platform_status_atstring | null
When that platform status happened
A time is null until its step happens. There is no time field for
in_transit or returning.
Pickup code
When the courier arrives at the restaurant, the restaurant tells them this
6-digit code, and they enter it in their app to take the package. The Genel
Kurye panel shows the code to the restaurant. The POS receives it in the
delivery.assigned event and should show it to the restaurant staff.
Reference
Statuses
A delivery moves through these statuses.
status | Meaning | Suggested status in your POS |
|---|---|---|
pending | In the pool, waiting for a courier | Hazırlanıyor (preparing) |
assigned | A courier accepted the delivery and is coming to the restaurant | Hazırlanıyor (preparing) |
picked_up | The courier has the package | Yolda (on the way) |
in_transit | The courier is heading to the customer | Yolda (on the way) |
delivered | The package was handed to the customer | Teslim Edildi (delivered) |
cancelled | Cancelled before pickup. The package is still at the restaurant | İptal (cancelled) |
returning | Cancelled after pickup, by the restaurant or by the cooperative. The package is on its way back to the restaurant | İade (returned), or İptal if there is no equivalent |
returned | The package was brought back to the restaurant | İade (returned), or İptal if there is no equivalent |
platform | A platform courier delivers the order. Genel Kurye sends no courier. Never sent as a webhook; read it with Get a delivery | The platform's own status, or platform_status if you report it |
A delivery moves through pending → assigned → picked_up → in_transit → delivered. It can become cancelled only before the package is picked up, and
returning or returned only after.
A delivery is platform from the start when you send
"delivery_by": "platform". A pending delivery can also become platform
when the cooperative learns that a platform courier took the order. A
platform delivery never changes status again.
Reference
Webhooks
Genel Kurye sends a signed POST to your webhook URL when the status of one of
your deliveries changes.
Not every change is sent: see Sending and retries. There
is one URL for all stores, and the delivery.store_id field in the body says
which store a delivery belongs to. The cooperative creates the webhook secret,
the key that signs each request, and sends it to the POS through a secure
channel.
Event format
- Content-Type
Always
application/json - User-Agent
GenelKurye-Webhook/1 - X-GK-Event
The event, for example
delivery.picked_up - X-GK-Event-Id
The event ID, for example
evt_b1tqwff2g8n4kpg36kbhhmcf_picked_up_17 - X-GK-Timestamp
When the request was signed, in Unix seconds, for example
1792224770 - X-GK-Signature
The signature, for example
sha256=5d41402abc4b2a76…
X-GK-Event and X-GK-Event-Id are not signed. Use type and event_id
from the body. The two headers repeat them for routing and logs.
The body:
The example is shortened: delivery is always a full delivery object, as it
was when the event was sent.
Events: delivery.assigned, delivery.picked_up, delivery.in_transit,
delivery.delivered, delivery.cancelled, delivery.returning,
delivery.returned. There is no event for platform.
Event ID. evt_<delivery ID>_<status>, with _<courier ID> added at the
end when the delivery has a courier. The courier ID is an internal number that
is not in the delivery object, so treat the whole event ID as one opaque
string. Every attempt to send the same event uses the same event ID. If the
cooperative moves a delivery to another courier, the status does not change,
but the same status is sent once more with the new courier's details and a new
event ID.
Event time. occurred_at is when the delivery reached that status. For
in_transit and returning it is the time of the delivery's last update,
because the delivery object has no time field for them. When a status is sent
again after a courier change, occurred_at is the same as in the first event,
so tell such events apart by event_id.
Verifying the signature
Genel Kurye signs every request with the webhook secret. To check a request,
compute the value below and compare it with X-GK-Signature:
signature = "sha256=" + hex(HMAC_SHA256(webhook_secret, X-GK-Timestamp + "." + raw_body))- Compute it over the bytes you received, before you parse the body.
- Compare in constant time.
- Reject the request if
X-GK-Timestampis more than 5 minutes away from your clock.
Sending and retries
- Respond with a
2xxwithin 20 seconds. Only the status code counts. If you pass the event on before answering, answer first: a late answer is a failure and the event comes again 30 seconds later. Redirects (3xx) are not followed and count as a failure. - Events are sent in rounds, about every 20 seconds. If a delivery changes
status more than once between two rounds, only the last status is sent. For
example, if a delivery becomes
picked_upand thenin_transitright away, onlydelivery.in_transitarrives. The times of the skipped steps are in the delivery object. - A response other than
2xx, a timeout and a connection error are all failures. A failed event is sent again after 30 seconds, then after a wait that doubles each time, up to 30 minutes. There are 14 attempts in total (about 4 hours). After the last one the event is dropped. - After a
5xx, a408, a429, a timeout, a redirect or a connection error, no other event is sent to your URL in that round. The rest are sent in the next round. - Every attempt is signed again with a new
X-GK-Timestamp. Theevent_iddoes not change. - The same event can arrive more than once. Ignore an
event_idyou have already processed. - Events are sent only for deliveries opened in the last 24 hours, and not after the restaurant removes the connection.
- You can miss an event: when all 14 attempts fail, when a newer status
replaces it in the same round, and when the delivery is older than 24 hours.
To get the current status, read
GET /deliveries/{id}while the connection is active.
Reference
Personal data
How a customer's and a courier's data is stored, and who can see it.
- Until the cooperative has signed the customer data agreement with a
restaurant (KVKK is Turkey's personal data protection law) and approved it
for order transfer, the restaurant's orders are rejected with
403 not_approved. Nothing about a rejected order's customer is stored. - A platform order needs no customer data: do not send
customer, the address line ornote. If you send them, they are ignored and not stored. Genel Kurye keeps only the district and city of the address. - While a delivery is in the pool, couriers see only the customer's drop-off address. The pool card also shows where the order came from, its code, the payment method, the preparation time, the package count and the item names.
- After a courier accepts the delivery, that courier also sees the customer's name as "Ayşe Y.".
- After that courier picks up the package, they also see the phone number, the customer's note, the address directions and the amount to collect at the door. No other courier sees them.
- Put anything personal about the customer, such as an allergy or a door code,
in
note, not in an item name. Item names are visible in the pool. - Every delivery object and every webhook event carries the courier's name and phone number. The number is not masked. It is there so the restaurant can reach the courier about that delivery.
- Show the courier's name and number only to the restaurant staff who handle that delivery. Do not use them for anything else. Do not keep them after the delivery is over.
- For a test store, Genel Kurye keeps a copy of up to 10 requests a minute,
and of every webhook event for the store's deliveries, for 7 days to help
with debugging. A request's copy has the path and query, the first 16 KB of
the request body, the first 8 KB of the response body, the IP address and
the
User-Agentheader. An event's copy has the event body and the first 1 KB of your response body. In these copies, the courier's phone number, the pickup code and any key are masked. The cooperative can look these copies up for you. - For a request to a real store, an order sent to a test store without
"test": true, and an order rejected before its content is checked (401, any403,429 rate_limited, or400 invalid_requeston thetestfield), Genel Kurye keeps the names and types of the fields you sent. Of their values it keeps only those that say nothing about a person, and only when the store's keys are valid:external_order_id,platform,platform_order_code,ordered_at,prep_minutes,package_count,delivery_by,test,payment.method,payment.amount_to_collect, the items' names and quantities, and thestatusandoccurred_atof a platform status report. A value that is not valid for its field is kept as a marker, not as sent. A top-level field that is not in this reference is kept only when its value istrueorfalse, a small whole number or a short lowercase code. Nothing undercustomeroraddress, and nevernote,reasonorbrand. At most 2 KB per request, for 7 days. A test order rejected for its content, for example with400 invalid_requestor422 address_too_far, is kept in full.
Reference
Versioning
This document describes v1.
New fields can be added to responses and webhook events without notice, so
ignore fields you do not recognize. A change that removes a field or changes
its meaning will come with a new path, /api/pos/v2.
Questions?
For a test store, a partner key or a webhook URL, or with any question about the API, write to the cooperative.