تخطَّ إلى المحتوى
Developers

Shipping API for delivery companies

Your company receives the orders of متاجرة stores automatically and reports each parcel back to the merchant and the customer.

CountryPalestine (PS)
CurrencyILS
Phone formatE.164, e.g. +970568823256
Delivery zoneswb · jer · in48 · gaza

Companies connected in Palestine: سندر Snder

1) How it works

  1. The متاجرة team gives you two keys: a test key (mtj_test_…) and a live key (mtj_…). Each is shown once.
  2. When a merchant sets an order to “out for delivery” or “shipped” and the customer chose your company, you get it: a webhook, or by polling GET /orders.
  3. You report its status with POST /orders/{id}/status; the merchant and the customer see it at once. “delivered” completes the order in the store.
  4. No developer? Everything here is also available by hand in the partners’ portal → “Deliveries”.

2) Base URL and authentication

Base URL:  https://motajrh.com/api/carrier/v1
Header:    Authorization: Bearer YOUR_KEY
           Accept: application/json

Every answer is JSON. A wrong key answers 401. Limit: 240 calls a minute.

3) Test mode

With the test key you see and change test orders only; no store is touched. Create a test order (it is sent to your webhook too), then try every step on it:

POST https://motajrh.com/api/carrier/v1/test/orders          → 201 { "order": { "id": 7, "order_number": "TEST-AB12CD", "test": true, … } }
GET  https://motajrh.com/api/carrier/v1/me                   → { "company": "…", "mode": "test", "open_orders": 1, "zones": { … } }

With the live key this endpoint answers 403. Rates and settlements sent with the test key are checked, not saved.

4) List orders

GET https://motajrh.com/api/carrier/v1/orders?status=new&limit=50&after_id=0
ParameterMeaning
statusnew, accepted, picked, out, delivered, returned, failed, cancelled
sinceorders changed since this time (ISO 8601)
after_idnext page: the next_after_id of the previous answer
limit1 to 100 (default 50)
{
  "orders": [{
    "id": 1024,
    "order_number": "MA-10501",
    "status": "new",
    "tracking_number": null,
    "cod_amount": 122,          // to collect at the door (0 = already paid)
    "currency": "ILS",
    "test": false, "settled": false, "settlement_ref": null,
    "created_at": "2026-10-08T12:10:00+03:00",
    "store":     { "id": 551, "name": "Magic Veil", "phone": "+970568823256", "url": "https://magicveil.motajrh.com",
                   "lat": 32.2211, "lng": 35.2544 },
    "pickup":    { "city": "نابلس", "district": "", "address": "…",
                   "lat": 32.2211, "lng": 35.2544 },
    "recipient": { "name": "…", "phone": "+970568823256", "country": "فلسطين",
                   "zone": "wb", "zone_name": "الضفة الغربية", "region": "", "city": "نابلس",
                   "district": "", "address": "رفيديا، شارع الجامعة", "note": "",
                   "lat": 32.227, "lng": 35.242 },
    "items":     [{ "name": "…", "sku": "PRF1", "quantity": 1 }],
    "order_total": 122, "delivery_fee": 20, "paid": false,
    "quote_id": null, "distance_km": null,     // if you price live: the quote the customer saw
    "account": "store"                        // who pays you the fee: store = the store's account with you, platform = the platform's one account
  }],
  "has_more": false,
  "next_after_id": null
}

Order fields: where each piece of data is

DataFieldNote
Our order numberid, order_numberid is the stable number used in URLs; order_number is what the merchant and customer see
Your own order numbertracking_numberyou send it with your first status update; we keep it and show it to the merchant and the customer
Store idstore.ida stable number for each store: keep the store's place under it once
Store namestore.name
Store phonestore.phonethe pickup branch's phone
Store coordinatesstore.lat, store.lng = pickup.lat, pickup.lngthe pickup branch as the merchant pinned it: decimal numbers (WGS84), latitude then longitude. null if not pinned yet
Store addresspickup.city, pickup.district, pickup.address
Customer namerecipient.name
Customer phonerecipient.phoneinternational format: +970 أو +972 ثم 9 أرقام تبدأ بـ 5
Customer coordinatesrecipient.lat, recipient.lngthe place the customer pinned at checkout. If you ask us for «customer location required» no order reaches you without it; otherwise it may be null
Customer addressrecipient.city, district, address, notenote: the customer's note to the courier
Cash to collectcod_amount, currency0 = already paid, collect nothing
Delivery feedelivery_fee, quote_id, distance_kmwhat the customer paid for delivery. With live pricing: delivery_fee = your quote's price, and quote_id is its number
Contentsitems[].name, sku, quantity

Zones of Palestine in recipient.zone: wb الضفة الغربية، jer القدس، in48 الداخل (مناطق 48)، gaza قطاع غزة

5) One order

GET https://motajrh.com/api/carrier/v1/orders/1024      → { "order": { …, "history": [ { "status": "new", "at": "…", "by": "store" } ] } }

6) Update the status

POST https://motajrh.com/api/carrier/v1/orders/1024/status
Content-Type: application/json

{ "status": "picked", "tracking_number": "SN-778812", "note": "" }
statusMeaningIn the store
acceptedyou accepted itlogged on the order
pickedpicked up from the storelogged; the customer sees the tracking number
outout with the courierlogged
delivereddeliveredthe order becomes “delivered”; the customer is told
failedcould not deliver (may be tried again)the merchant is alerted
returnedreturned to the store (final)the merchant is alerted

A closed order (delivered / returned / cancelled) answers 409. An unknown status answers 422.

7) Your prices and coverage

Send your prices: every merchant sees them when setting your company up and takes them with one click. A zone you leave out means you do not deliver there.

PUT https://motajrh.com/api/carrier/v1/rates
{ "country": "PS", "rates": [  { "zone": "wb", "price": 20, "days": "1-3" },  { "zone": "jer", "price": 30, "days": "1-3" }
] }

GET https://motajrh.com/api/carrier/v1/rates             → { "rates": { "PS": { … } }, "zones": { "PS": { … } } }

8) Cash collected at the door

What you collected and have not paid to the stores yet, then telling us you paid. The merchant is alerted and confirms from the dashboard.

GET  https://motajrh.com/api/carrier/v1/settlements
     → { "pending": [ { "id": 1024, "order_number": "MA-10501", "store": "Magic Veil", "cod_amount": 122, "currency": "ILS" } ],
         "totals": { "ILS": 122 } }

POST https://motajrh.com/api/carrier/v1/settlements
     { "order_ids": [1024, 1031], "reference": "TRX-2026-0091" }   → { "settled": 2 }

For companies that work by the map: location, live price, paid orders

Three options our team switches on for your company on request, each on its own:

OptionWhat it does
Customer location requiredA customer who chooses your company at checkout cannot finish before pinning their place on the map. So no order reaches you without recipient.lat and recipient.lng.
Live priceWe call a quote endpoint of yours once the customer pins their place, and show the price you answer as it is. It can be «required» (no fixed price: no answer, your company is not offered for that order) or «optional» (no answer, the merchant's fixed price is shown).
Paid orders onlyYour company is not offered with cash on delivery, and no order is sent to you before it is paid. So cod_amount is 0 in every order you receive.

Your quote endpoint (you build it, we call it)

POST https://your-domain/…/shipping/quote
Authorization: Bearer <a key you give us>
Content-Type: application/json

{ "store_id": 551,
  "pickup":  { "lat": 32.2211, "lng": 35.2544 },
  "dropoff": { "lat": 32.227, "lng": 35.242 } }

← 200 { "available": true, "quote_id": "q_8f2a1c", "price": 12, "currency": "ILS",
        "distance_km": 3.4, "eta_minutes": 45, "expires_at": "2026-10-08T18:30:00+03:00" }
← 200 { "available": false }        // outside your coverage: you are not offered for this order
  • price is in the store's currency (ILS in Palestine) and shown to the customer as it is. An answer in another currency is ignored.
  • quote_id, distance_km, eta_minutes and expires_at are optional. A quote_id is kept and returned in the order's quote_id, and delivery_fee equals price.
  • We keep a quote for 10 minutes at most, never past expires_at minus two minutes; after that we ask again. So an order is always created with a price still valid on your side.
  • We wait 6 seconds for your answer. Anything but 200, or without «available», counts as no answer.
  • No quote without the store's place: if the merchant has not pinned their branch we do not call you.

Who pays the delivery fee?

Two ways; we agree on one with you (the «account» field in every order and quote request says which):

accountMeaning
platformOne platform account with you: you deduct each order's fee from the platform's balance. We take it from the store's balance with us before sending the order, so no order reaches you unfunded. If the order is cancelled before pickup (order.cancelled) the fee is not charged.
storeEach store has its own account and balance with you and tops it up itself. store.id is what you link the store's account to; the merchant sees it in their dashboard. The platform is out of the money.

9) Webhook — optional

Give the team an https:// link; we POST to it on every new order and when a merchant cancels one:

X-Motajrh-Event: order.created            (or order.cancelled)
X-Motajrh-Delivery: 5521                  (unique per delivery: use it to ignore a repeat)
X-Motajrh-Signature: hex( HMAC-SHA256( raw body, your webhook secret ) )

{ "event": "order.created", "order": { … the same object as in the list … } }

Check the signature and answer 200 within 8 seconds. If your server does not answer we try again after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, then 12 hours. The order always stays in GET /orders?status=new.

10) Quick example

curl -H "Authorization: Bearer YOUR_KEY" -H "Accept: application/json" \
     "https://motajrh.com/api/carrier/v1/orders?status=new"

curl -X POST -H "Authorization: Bearer YOUR_KEY" -H "Content-Type: application/json" \
     -d '{"status":"delivered"}' "https://motajrh.com/api/carrier/v1/orders/1024/status"

11) Errors

401 unauthorizedwrong or revoked key
403 test_key_onlythe endpoint takes the test key only
404 not_foundnot your order, or it does not exist
409 closedthe order is closed
422a wrong value (see errors or message)
429more than 240 calls a minute

A delivery company that wants to join?

انضم كشريك

اربطوا شركة التوصيل مع متاجرة

عبّئوا بيانات الشركة ويتواصل معكم فريقنا ويرسل لكم مفتاح الاختبار.