Govza Partner API API reference OpenAPI spec

Post deliveries to Govza from your system

Price a trip, create a delivery, follow it to the door — over plain HTTPS and JSON, authenticated with a key the customer issues themselves.

1. Get a key

Keys are created by the Govza customer whose deliveries you will be placing, in their dashboard at run.govza.app/api-keysAPI-ключиСоздать ключ. Every request you make with that key acts as that customer: the orders are theirs, billed to them, and visible in their order list.

The key is shown once. Govza stores only a hash of it, so it cannot be recovered — if it is lost, the customer creates a new one and revokes the old.

Check it works before writing anything else:

curl https://api.govza.app/v1/partner/ping \
  -H "Authorization: Bearer gvz_<key id>_<secret>"

The response names the account the key acts as. A 401 means the key is wrong, revoked, or its account can no longer create orders.

2. Understand addresses first

This is the part integrations get wrong, so it is worth reading before you write any code. Govza does not geocode. There is no address lookup, no spelling correction and no fuzzy matching behind this API. Every address you send must already be structured and must already carry coordinates.

Field Required Notes
latitude Yes Decides routing and price. −90…90.
longitude Yes −180…180.
area Yes City or locality.
street Yes Street name, without the house number.
building Yes House or building number, as text — "12к3" is fine.
apartment, entrance, floor No Whole numbers only. "12A" is rejected rather than silently dropped — put it in instructions.
intercom, instructions No Free text shown to the courier.

The coordinates are authoritative for routing and pricing; the text is what the courier reads on the way. Keep them consistent — nothing on our side reconciles the two.

If a coordinate falls outside an area Govza serves, the order is rejected at creation rather than accepted and left undeliverable.

3. Price the trip

curl -X POST https://api.govza.app/v1/partner/quotes \
  -H "Authorization: Bearer $GOVZA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "pickup": {
      "address": {
        "latitude": 43.3169, "longitude": 45.6981,
        "area": "Грозный", "street": "проспект Путина", "building": "1"
      },
      "contact": { "value": "+79280000001", "name": "Склад" }
    },
    "dropoffs": [{
      "address": {
        "latitude": 43.3200, "longitude": 45.7000,
        "area": "Грозный", "street": "улица Мира", "building": "12",
        "apartment": 4, "entrance": 2, "floor": 3
      },
      "contact": { "value": "+79280000002", "name": "Получатель" }
    }]
  }'

You get back a price, a quote_id and an expires_at. Quotes last five minutes.

4. Create the delivery

Same body, plus your own external_order_id and — if you want the price you were just quoted — the quote_id.

curl -X POST https://api.govza.app/v1/partner/orders \
  -H "Authorization: Bearer $GOVZA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_order_id": "SHOP-10432",
    "quote_id": "<quote_id from step 3>",
    "payment_type": "cash",
    "payment_amount": 1500,
    "comment": "Позвонить за 10 минут",
    "pickup": { "...": "as above" },
    "dropoffs": [{ "...": "as above" }]
  }'

The response carries the Govza order_code, the final price, and a tracking_url you can forward to the recipient.

Retries are safe

external_order_id is the idempotency key. If you retry a create after a timeout, you get 200 with the original order instead of 201 with a second courier on the road. Use an id from your own system and never reuse one for a different delivery.

If the quote expired

A quote_id is honoured only while every leg of it is still valid. Otherwise the trip is re-priced automatically and the response carries the new price — always read the price back from the create response rather than assuming the quote held.

5. Follow the delivery

Poll:

curl https://api.govza.app/v1/partner/orders/SHOP-10432 \
  -H "Authorization: Bearer $GOVZA_KEY"

Statuses are newassignedin_progressdelivered, or cancelled.

Webhooks

If the key has a webhook URL configured, Govza POSTs each status change to it. Respond 2xx to acknowledge; anything else is retried with exponential backoff for up to eight attempts.

Every request carries a signature header:

X-Govza-Signature: t=1723377600,v1=9f86d081884c7d65...

v1 is HMAC-SHA256(webhook_secret, "<t>.<raw body>"), hex-encoded. Verify against the raw body, before any JSON parsing, and reject requests whose t is more than a few minutes old — that timestamp is what stops a captured callback being replayed later.

import crypto from 'node:crypto';

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(
    header.split(',').map((kv) => kv.split('='))
  );
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`, 'utf8')
    .digest('hex');

  const age = Math.abs(Date.now() / 1000 - Number(parts.t));
  if (age > 300) return false;

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(parts.v1)
  );
}

Cancelling

curl -X POST https://api.govza.app/v1/partner/orders/SHOP-10432/cancel \
  -H "Authorization: Bearer $GOVZA_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "customer_changed_mind" }'

There is no edit. To change a delivery, cancel it and create a new one.

Errors

Validation failures return 400 and list every offending field at once, so one response is enough to fix the request:

{
  "success": false,
  "error": {
    "id": "validation.partner_payload_invalid",
    "message": "dropoffs.0.address.latitude: latitude is required",
    "details": "[{\"field\":\"dropoffs.0.address.latitude\",\"message\":\"latitude is required\"}]"
  }
}
Status Meaning
400 Payload rejected. See error.details.
401 Key missing, malformed, revoked, or its account cannot create orders.
404 No order for that external_order_id.
409 A concurrent request is creating this order. Retry shortly.
429 Rate limit for this key exceeded. Back off and retry.

Full endpoint and schema detail is in the API reference.