Skip to content
ROAD RUNNER

DELIVERIES

Create a delivery

POST roadrunnerzw.com/api/v1/deliveries

The delivery is accepted immediately and sent to a courier network in the background, so the response is 202 Accepted with a pending delivery rather than 201 Created. Watch for the delivery.dispatched or delivery.dispatch_failed webhook, or poll this delivery, to learn the outcome.

An Idempotency-Key header is required: retrying with the same key returns the original response instead of creating a second delivery.

REQUEST
curl -X POST https://roadrunnerzw.com/api/v1/deliveries \
  -H 'Authorization: Bearer rr_live_your_key_id' \
  -H 'X-RR-Timestamp: $(date +%s)' \
  -H 'X-RR-Signature: $SIGNATURE' \
  -H 'Idempotency-Key: $(uuidgen)' \
  -H 'Content-Type: application/json' \
  -d '{
      "client_reference": "ABC-10432",
      "pickup": {
          "name": "ABC",
          "phone": "+263242000000",
          "address": "10 Example Avenue, Harare, Zimbabwe",
          "instructions": "Collect from the front counter"
      },
      "dropoff": {
          "name": "John Doe",
          "phone": "+263770000000",
          "email": "john@example.com",
          "address": "45 Example Road, Harare, Zimbabwe",
          "country": "Zimbabwe",
          "instructions": "Gate code 4471. Call on arrival."
      },
      "items": [
          {
              "name": "Widget, large",
              "quantity": 2,
              "unit_price": 1250,
              "notes": "Handle with care"
          }
      ],
      "delivery_fee": 500,
      "tax": 0,
      "currency": "USD",
      "payment_type": "prepaid",
      "metadata": {
          "order_type": "retail"
      }
  }'
RESPONSE 202
{
    "id": "dlv_01JAV3K2QX8M4N7P",
    "object": "delivery",
    "client_reference": "ABC-10432",
    "status": "pending",
    "test_mode": false,
    "pickup": {
        "name": "ABC",
        "phone": "+263242000000",
        "address": "10 Example Avenue, Harare, Zimbabwe",
        "instructions": "Collect from the front counter"
    },
    "dropoff": {
        "name": "John Doe",
        "phone": "+263770000000",
        "email": "john@example.com",
        "address": "45 Example Road, Harare, Zimbabwe",
        "instructions": "Gate code 4471. Call on arrival."
    },
    "items": [
        {
            "name": "Widget, large",
            "quantity": 2,
            "unit_price": 1250,
            "notes": "Handle with care"
        }
    ],
    "payment_type": "prepaid",
    "cash_to_collect": null,
    "pricing": {
        "subtotal": {
            "amount": 2500,
            "currency": "USD"
        },
        "delivery_fee": {
            "amount": 500,
            "currency": "USD"
        },
        "tax": {
            "amount": 0,
            "currency": "USD"
        },
        "total": {
            "amount": 3000,
            "currency": "USD"
        }
    },
    "scheduled_for": null,
    "tracking_url": "https://staging.roadrunnerzw.com/t/8f2a9c1e4b",
    "courier": null,
    "eta": null,
    "proof_of_delivery": null,
    "created_at": "2026-09-21T14:30:00+02:00",
    "updated_at": "2026-09-21T14:30:06+02:00",
    "dispatched_at": null,
    "delivered_at": null,
    "cancelled_at": null,
    "cancellation_reason": null
}

Headers

  • Idempotency-Key string REQUIRED

    REQUIRED. A unique string of your choosing, at most 255 characters: a UUID is the usual choice. Retrying with the same key returns the original response instead of creating a second delivery, so reuse it on every retry of the same intent and change it for a new one. Reusing a key with a different body returns 409.

Body

  • quote_id string

    The id from a POST /v1/quotes response. Supply it to hold the price you were quoted; leave it out and the delivery is priced when we receive it.

  • client_reference string REQUIRED

    Your own order number. It must be unique to your account, and it is what you will recognise the delivery by in the portal, on the tracking page and in support conversations.

  • pickup_location string

    A saved pickup location by id (pl_…, from GET /v1/pickup-locations). Send this or pickup, not both; send neither and we use your default location.

  • pickup object

    Where the courier collects. Leave it out and we use the pickup address held on your account.

    • name string

      Who to ask for at collection, if not your business name.

    • phone string

      A number the courier can call if they cannot find the counter.

    • address string

      Street address including the suburb, e.g. "10 Example Avenue, Msasa, Harare". Required once you send a pickup object at all.

    • instructions string

      Anything the courier needs to know to collect: which door, which counter, who to ask for.

  • dropoff object REQUIRED

    Where the parcel is going, and who is waiting for it.

    • name string REQUIRED

      The recipient's name as they gave it to you. One field, not two: the tracking page shows only the first word of it.

    • phone string REQUIRED

      A Zimbabwean mobile number the courier can call on arrival. Accepted as 07…, 2637… or +2637….

    • email string

      Optional. Only used if we ever need to reach the recipient in writing; it never appears on the public tracking page.

    • address string REQUIRED

      Street address including the suburb. The suburb is what the recipient sees on the tracking page and what the price is measured against, so a vague address costs you accuracy.

    • country string

      Defaults to Zimbabwe. We deliver inside Zimbabwe only, so an address anywhere else is refused before a courier is sent.

    • instructions string

      Gate codes, floor numbers, "call on arrival": whatever the courier needs to complete the drop without phoning you.

  • items array of objects REQUIRED

    What is in the parcel. At least one item, at most 100.

    • name string REQUIRED

      What the item is, in words a courier would understand.

    • quantity integer REQUIRED

      How many of this item. Whole numbers, at least 1.

    • unit_price integer REQUIRED

      Price for ONE of this item, in cents. 1250 is $12.50.

    • notes string

      Handling notes for this item: fragile, cold chain, upright.

  • delivery_fee integer

    What you are charging your customer for delivery, in cents. This is your figure, not ours; it does not change what we bill you. Leave it out and we price the delivery ourselves.

  • tax integer

    Tax on the order, in cents, if you itemise it.

  • currency string

    Three-letter code, e.g. "USD". Defaults to USD.

  • payment_type string

    "prepaid" if the customer has already paid you, or "cash_on_delivery" if the courier must collect money. Defaults to prepaid.

    One of prepaid cash_on_delivery

  • service_level string

    How fast you need it: standard, express or scheduled. Express costs more and scheduled can cost less; what your account pays for each is on your profile page. Defaults to standard, which is what every delivery booked before this existed was.

    One of standard express scheduled

  • cash_to_collect integer

    How much the courier must collect, in cents. Required when payment_type is cash_on_delivery, and refused otherwise.

  • scheduled_for string

    Book a collection for later instead of now. ISO 8601, read as UTC, at least a minute ahead and at most seven days out.

  • metadata array of strings

    Up to 20 key/value pairs of your own. We store them and hand them back on every read; we never act on them.

Responses

202 Accepted

Accepted and queued. The delivery exists with status pending; a courier has not been assigned yet.

401 Unauthenticated

The signature, key or timestamp did not check out. Also returned for a revoked key, and for a timestamp more than 300 seconds from ours.

403 Forbidden

The key is valid but was not issued the scope this endpoint requires. Scopes are fixed at issue, so this needs a new key rather than a retry.

409 Conflict

This Idempotency-Key was already used with a different request body. Reuse a key only when retrying the same intent.

422 Validation failed

The request was understood and refused. Same envelope as every other error; details names each field at fault, so retrying unchanged will fail the same way.

429 Too many requests

Too many requests for this client. Back off and retry; the response carries Retry-After and X-RateLimit-* headers.