← API documentation

Live: https://api.areye.app/api · Sandbox: https://sandbox.areye.app/api · Sandbox guide

Areye Shop API (app ⇄ Areye backend) — v1

One shopping experience in the Areye app over two commercial sources:

Carts and checkouts are never mixed: a cart belongs to one source, and to one store (or one Dijo mall). Areye's backend is the only thing the app talks to; it calls the source server to server.

All routes are under /api/customer/shop, use the customer's Bearer token, and return {success, data, source, error?}. When a source can't be reached, the route returns 503 channel_unavailable: the app shows "temporarily unavailable" and sells nothing.

Sources and categories

GET /sources → [{ "source": "dijo"|"storefront", "label", "verticals": [{ "key", "label" }] }]. Only sources and categories switched on are listed. The home tile shows "Shopping"; inside it, the categories (Groceries, Food, Flowers, …) and the Storefront shops.

Catalogue

Route Notes
GET /{source}/stores?lat&lng&vertical [{ id, name, logo_url, is_open, opens_at?, eta_minutes?, delivery_fee?, currency, areye_vertical, mall_id? }]
GET /{source}/stores/{store}/categories [{ id, name, parent_id?, areye_vertical }]
GET /{source}/stores/{store}/items?category&cursor&vertical { items: [ItemCard], next_cursor? }
GET /{source}/stores/{store}/items/{item} ItemDetail
GET /{source}/search?q&lat&lng&vertical?&store_id?&mall_id?&cursor { items: [ItemCard + store_id, store_name], stores?: [...], next_cursor? }
GET /{source}/stores/{store}/slots?date=YYYY-MM-DD { schedule_order, date, lead_minutes, slot_duration_minutes, windows: [{from, to}], slots } (order for later)
GET /{source}/stores/{store}/bundles { bundles: [{ id, title, bundle_price, currency, items, age_restricted }] }
GET /{source}/stores/{store}/reviews?cursor / GET /{source}/items/{item}/reviews?cursor { reviews: [{ rating, comment, at }], next_cursor }
GET /{source}/items/{item}/bought-together { items: [ItemCard] } (goes well with)

Basket and checkout

  1. POST /{source}/basket/validate

    { vertical, delivery: { address, lat, lng, instructions? }, substitution_preference: refund|ask|match,
      mall_id?, lines: [{ store_id, item_id, selected_options: [option ids], qty, shown_unit_price }] }
    

    Returns { validation_id, expires_at, lines: [{ item_id, status: ok|price_changed|reduced|unavailable, unit_price, qty_allowed }], fees: [{ code, label, amount }], discounts: [...], total, currency, changes_require_acceptance }. It may also return 422 with line_outside_vertical, basket_scope_invalid or outside_service_area.

  2. POST /{source}/orders

    { validation_id, accepted_changes, client_key: <uuid>, marketing_consent? }
    

    client_key makes a retry safe: the same key returns the same order. The response is an Order, including commerce.payment_url. A 409 validation_stale means: validate again, and have the customer accept the changes.

  3. Payment: the app opens payment_url in the payment webview, then polls GET /orders/{ref} until commerce.payment_status is paid or failed. Closing the page proves nothing.

Checkout options (each by the source's own rule)

Orders (both sources)

Route Notes
GET /orders the customer's orders, newest first
GET /orders/{ref} refreshed from the source, joined with Areye's delivery
POST /orders/{ref}/cancel { reason } the source decides what is refunded
POST /orders/{ref}/decision `{ request_id, decision: accept reject }`
GET /orders/{ref}/refund-reasons the source's reasons for a refund after delivery
POST /orders/{ref}/refund-request { customer_reason, customer_note? } asks the source for a refund; it reviews it

Order:

{ reference, source,
  commerce: { order_id, status, payment_status, total, currency, payment_url?, lines, refunds, receipt_url? },
  decision: null | { request_id, line_id, proposed: { name, unit_price, qty }, expires_at },
  fulfilment: null | { trip_id, trip_reference, status, driver_first_name, eta_minutes, can_tip },
  commerce_status_stale: bool }

A tip uses the existing POST /api/customer/trip/{trip_id}/tip {amount}, which is allowed for the customer of the channel order.

Push

The data payload { type: "shop_decision", reference } opens the order with the substitution question. { type: "shop_order", reference } opens the order.

The earlier /api/customer/groceries/* routes remain as aliases of /api/customer/shop/dijo/* until the app ships.

Sandbox

The same API runs at https://sandbox.areye.app/api/customer/shop, buying from the partners' own sandboxes. Sign in there as a sandbox customer: nothing is ever sent to a phone in the sandbox, so every one-time code is 123456. Orders are fulfilled by sandbox deliveries that you drive with the simulation endpoints, or the partner's sandbox account's simulated driver.