---
feature: Provider requests and appointments
slug: orders
surface: provider
consumer: flutter
version: 2.7.0
status: ready
brd_version: "1.3"
brd_ids: [PROV-014, PROV-015, PROV-019, PROV-020, PROV-022]
business_rules: [BR-03, BR-12, BR-13, BR-14, BR-16, BR-19, BR-24, BR-28]
decisions: [D3, DEC-04, DEC-05, DEC-10, TD-19, TD-92, TD-93, TD-94, TD-96, TD-101, TD-102, TD-104, TD-144, TD-145]
schema_tables: [orders, order_items, order_item_options, order_status_history, payments, refunds, wallets, wallet_transactions, notifications]
screens: [provider--requests--01-incoming-request-20s, provider--schedule--01-*, provider--schedule--02-*, provider--schedule--03-*]
related_docs: [availability.md, order-execution.md, cancellation.md, chat.md, ../client/booking.md, ../client/orders.md, ../client/cancellation.md]
last_updated: 2026-11-08
---

# Provider requests and appointments — Flutter Integration Guide (provider)

> **Base URL:** `{HOST}/api/v1/provider` · [Shared API conventions](../API_CONVENTIONS.md).

## Version history

| Version | Date | Type | Summary | App action required |
|---------|------|------|---------|---------------------|
| 2.7.0 | 2026-11-09 | MINOR | Group orders (D2, TD-180): `is_group` on every order row; the details carry `people[]` and `services[].person_id`. | Show who each service is for when `is_group` is true. |
| 2.6.2 | 2026-11-08 | PATCH | Rate action: show it from `can_rate` (PROV-024 is built). | Show the Rate button from `can_rate`; ignore the earlier "keep Rate hidden" note. |
| 2.6.1 | 2026-11-02 | PATCH | The Rate action is now live: `can_rate` drives the button and the rating endpoint is [ratings.md](ratings.md) (TD-162). No contract change. | Show the Rate button when `can_rate` is `true`; ignore the earlier "do not show Rate yet" notes. |
| 2.6.0 | 2026-11-01 | MINOR | Order tile and details add the always-present `can_cancel` flag; `POST /orders/{order}/cancel` is documented in [cancellation](cancellation.md) (PROV-016, TD-153). | Show Cancel from `can_cancel`. |
| 2.5.0 | 2026-10-29 | MINOR | Adds `can_chat` to order tiles/details and a persisted order chat. | Show chat only while allowed; recover history and unread after reconnect. |
| 2.4.0 | 2026-10-28 | MINOR | Order detail includes transport choice and a nullable trip summary (TD-134–139). | Parse the transport block and refresh on trip events. |
| 2.3.0 | 2026-10-27 | MINOR | Package purchases are excluded; sessions use the usual order flags and acceptance flow. | Handle package session orders using the existing flags. |
| 2.2.0 | 2026-10-26 | Additive | Cancelled appointments tab and `order.updated` cancellation event; active schedule excludes cancelled orders. | Add a cancelled tab, refetch on `reason: cancelled`, and hide execution buttons. |
| 2.1.0 | 2026-10-26 | Additive | Six always-present action flags (`can_prepare`, `can_start_travel`, `can_mark_arrived`, `can_start_service`, `can_complete`, `can_rate`) on every tile and on the details; the completed tab is filled; schedule tabs keep orders under way. | Read the flags; build the order execution buttons per [order-execution](order-execution.md); do not show Rate yet. |
| 2.0.0 | 2026-10-25 | **Breaking** | `status`/`booking_type` become `{value,label}`; new `payment_status`, `can_respond`; lean list vs full details; address hidden after rejection/expiry; realtime events. | Update the Dart model, read `.value`, and connect the socket (API conventions §9). |
| 1.0.0 | 2026-10-25 | Initial | Incoming requests, decisions and Phase 2 schedule tabs. | Refresh pending requests and decide by `respond_before`. |

## Changelog

### 2.7.0 — 2026-11-09

- **Added:** `is_group`; on the details `people[]` (`id`, `label`, `is_customer`, `sort_order`, `services_count`, `duration_min`, `services_amount`) and `services[].person_id`. The estimated duration is the sum of every service. Companions are names only.
- **Breaking:** No. **App action required:** Group the services by person on the order screen.

### 2.6.2 — 2026-11-08

- **Changed:** Rate action: show it from `can_rate` (PROV-024 is built).
- **Breaking:** No. **App action required:** Show the Rate button from `can_rate`; ignore the earlier "keep Rate hidden" note.

### 2.6.0 — 2026-11-01

- **Added:** `can_cancel` (bool, always present) on every order tile and on the details; provider-side cancellation is in [cancellation](cancellation.md).
- **Breaking:** No. **App action required:** Show Cancel only while `can_cancel` is true.
- **BRD / decisions:** PROV-016, BR-19, BR-20; TD-153.

### 2.5.0 — 2026-10-29

- Added always-present `can_chat` from the paid-confirmed order gate. See [provider chat](chat.md) for the HTTP and event contract. App action required: parse the flag, deduplicate messages by UUID and explicitly mark displayed messages read.

### 2.4.0 — 2026-10-28

- **Changed:** Order detail includes transport choice and a nullable trip summary (TD-134–139).
- **App action required:** Parse the transport block and refresh on trip events.
- **BRD / decisions:** Cycle F, TD-132–140; DEC-08 remains open.

### 2.3.0 — 2026-10-27

- **Added / changed:** Package purchases are excluded; sessions use the usual order flags and acceptance flow.
- **Fixed / removed:** None.
- **Breaking:** No.
- **Endpoints affected:** Existing order or package reads and actions described below.
- **App action required:** Handle package session orders using the existing flags.
- **BRD / decisions:** D1, TD-123–128.


### 2.2.0 — 2026-10-26
- **Added:** `GET /appointments?tab=cancelled` lists her cancelled orders newest first. Customer cancellation sends a provider inbox row and `order.updated` (`reason: cancelled`); a cancelled tile has all execution flags false.
- **Changed:** Cancelled orders leave `today` and `upcoming`; `GET /orders/{order}` reports `cancelled` and hides address/notes as before.
- **Fixed / Removed:** None. **Breaking:** No.
- **Endpoints affected:** `GET /appointments`, `GET /orders/{order}`.
- **App action required:** add the cancelled tab and refetch on cancellation. Provider-side cancellation remains PROV-016.
- **BRD / decisions:** CUST-020, BR-19/20/22, DEC-24, TD-105/106.

### 2.1.0 — 2026-10-26
- **Added:** `can_prepare`, `can_start_travel`, `can_mark_arrived`, `can_start_service`, `can_complete` and `can_rate` (all booleans, never omitted) on `GET /orders`, `GET /appointments`, `GET /orders/{order}` and the accept / reject / stage responses. The stage endpoints are documented in [order-execution](order-execution.md). `can_rate` is `true` for a `completed` order for which she has not yet rated the customer (BR-28, TD-104).
- **Changed:** `GET /appointments?tab=completed` returns her `completed` orders, newest `completed_at` first (it was `[]`). `today` and `upcoming` also list scheduled orders in `preparing`, `on_the_way`, `arrived` and `in_progress`, so an order under way stays visible. New `status.value`s may appear in the provider payload: `preparing`, `on_the_way`, `arrived`, `in_progress`, `completed`.
- **Fixed / Removed:** None. **Breaking:** No.
- **Endpoints affected:** `GET /orders`, `GET /orders/{order}`, `GET /appointments`.
- **App action required:** add the six booleans to the Dart model; use them for the buttons (the first five, see [order-execution](order-execution.md)); **do not show the Rate action yet** even when `can_rate` is `true` — the rating endpoints (PROV-024) are a separate Phase 4 feature; render the completed tab; treat unknown `status.value`s by their `label`.
- **BRD / decisions:** PROV-019/020/022, BR-19/28, TD-101/102/104.

### 2.0.0 — 2026-10-25
- **Changed (breaking):** `status` and `booking_type` were plain strings, now `{"value","label"}` objects like every other enum. `GET /orders` and `GET /appointments` return a **lean** tile (no `services`, `address`, `estimated_duration_min`, `services_amount`, `provider_commission_rate`, `rejection_reason`, `created_at`); those stay on `GET /orders/{order}`. Once an order is `rejected`, `expired` or `cancelled`, `address.text/lat/lng` and `notes_to_provider` are `null` (the keys stay).
- **Added:** `payment_status` `{value,label}` (PROV-014), `can_respond` (true only while `pending_provider` and before `respond_before`), `service_name`, `eta_minutes` on the tile; realtime events `order.requested` / `order.updated` ([API conventions §9](../API_CONVENTIONS.md)).
- **Migration (old → new):** `order.status == 'confirmed'` → `order.status.value == 'confirmed'`; same for `booking_type`. Take the address from the details call, not the list.
- **Fixed:** an instant order now blocks the provider from the moment it is placed until arrival plus service, so a scheduled booking can no longer overlap the instant visit (TD-92).
- **App action required:** change the model, show labels from the API, use `can_respond` for the buttons, connect the socket and refetch on each event/reconnect.
- **BRD / decisions:** PROV-014/015/022, D3, TD-96.

### 1.0.0 — 2026-10-25
- **Added:** `GET /orders`, `GET /orders/{order}`, `POST /orders/{order}/accept`, `POST /orders/{order}/reject`, `GET /appointments`.
- **Changed/Fixed/Removed:** None; **Breaking:** No.
- **App action required:** Use order UUIDs, show net earning and seconds left, and refresh after a late response.
- **BRD / decisions:** PROV-014/015/022, D3, TD-92/93/94.

## 1. Business summary

Package purchases are excluded from provider lists; purchased sessions follow the normal request and execution flags. See [my packages](../client/my-packages.md).

An approved provider sees her pending requests and accepts or rejects each before its deadline. Scheduled acceptance invites the customer to pay; instant acceptance confirms only after payment. Paid instant rejection or timeout automatically credits the customer wallet. The order's `net_earning` is service value after the snapshotted provider commission percentage; payout and earnings accounting are later features.

## 2. User flow and screens

| Screen | Endpoint | App behavior |
|--------|----------|--------------|
| Incoming request (20 seconds for instant) | `GET /orders`, `GET /orders/{order}` | Show service/options, address, duration, value, net earning and countdown. |
| Accept / reject | `POST /orders/{order}/accept`, `/reject` | Disable buttons while sending; refresh order after any error. |
| Schedule today/upcoming/completed | `GET /appointments?tab=…` | Show confirmed/pending rows and orders under way; completed lists her finished orders newest first. |
| Order under way | [order-execution](order-execution.md) | Stage buttons from the `can_*` flags. |

## 3. Endpoints

All require an **approved provider bearer token** (BR-03). A foreign, missing or integer order ID returns 404.

| Method and path | Input | Success | Errors / app action |
|-----------------|-------|---------|---------------------|
| `GET /orders` | None | Lean tiles of pending requests, earliest deadline first. Postman: `provider.orders.index` › "200 Incoming provider order". | Guest 401, unapproved 403. |
| `GET /orders/{order}` | Order UUID | Full resource. Postman: `provider.orders.show` › "200 Provider order details". | 404 foreign/unknown/integer ID. Read applies lazy expiry. |
| `POST /orders/{order}/accept` | Optional `reason` text max 500 (ignored on accept). | Scheduled → `awaiting_payment`; instant paid → `confirmed`. Repeat accept returns same decision. Postman: `provider.orders.accept` › "200 Scheduled request accepted". | 422 `PROVIDER_RESPONSE_EXPIRED` after deadline, `INVALID_STATE_TRANSITION` for other state, validation on long reason. |
| `POST /orders/{order}/reject` | Optional `reason` text max 500. | `rejected`; paid instant amount credited once to wallet. Repeat reject returns same decision. Postman: `provider.orders.reject` › "200 Instant request rejected with wallet refund". | Same late/state/validation errors. |
| `GET /appointments?tab=today\|upcoming\|completed\|cancelled` | Required tab. | Scheduled pending / confirmed / under-way rows (`today`, `upcoming`); `completed` lists finished orders; `cancelled` lists cancelled orders newest first. Postman: `provider.appointments.index` › "200 Upcoming confirmed appointment", "200 Completed tab", "200 Cancelled provider appointment". | 422 for unknown tab, "422 Unknown appointment tab". |

**Tile** (`GET /orders`, `GET /appointments`): `id`, `code`, `status`, `payment_status`, `booking_type` (all `{value,label}`), `service_name`, `customer {id,name}`, `scheduled_at`, `eta_minutes`, `net_earning`, `respond_before`, `seconds_left`, `can_respond`, `can_prepare`, `can_start_travel`, `can_mark_arrived`, `can_start_service`, `can_complete`, `can_rate`. **Details** (`GET /orders/{order}` and the accept/reject responses) add `services[]` with options, `address {text,lat,lng}`, `estimated_duration_min`, `services_amount`, `provider_commission_rate`, `notes_to_provider`, `rejection_reason`, `created_at`. `respond_before` is ISO 8601 with offset; `seconds_left` is nonnegative and null outside `pending_provider`. The address is `null` once the order is rejected, expired or cancelled. **Real response:** Postman: `provider.orders.show` › "200 Provider order details". Validation examples: `provider.orders.accept` › "422 Decision reason too long" and `provider.orders.reject` › "422 Rejection reason too long". Late response: `provider.orders.accept` › "422 Provider response deadline passed".

## 4. Statuses and enums

| Status | Meaning for provider | Next action |
|--------|----------------------|-------------|
| `pending_provider` | Response still due. | Accept/reject before `respond_before`. |
| `awaiting_payment` | Scheduled provider accepted; customer must pay. | Wait; no second decision. |
| `confirmed` | Paid and accepted. | Start preparing / leave ([order-execution](order-execution.md)). |
| `preparing`, `on_the_way`, `arrived`, `in_progress` | Execution stages she advances herself. | See [order-execution](order-execution.md). |
| `completed` | She confirmed completion. | Final; in the completed tab. `can_rate` is `true` until she rates the customer. |
| `rejected` | Provider declined. | Final; no further decision. |
| `expired` | Response/payment window elapsed. | Final; do not accept late. |
| `cancelled` | Customer cancelled before execution. | Final; in the cancelled tab with all execution flags false. |

## Data model

| Table | Read / write | Notes |
|-------|--------------|-------|
| `orders` | read/write | Row lock protects decisions and deadline checks. |
| `order_status_history` | trigger write | Actor is provider/system/customer. |
| `refunds`, `wallet_transactions`, `wallets` | write for paid instant reject/timeout | One credit via balance trigger. |
| `notifications` | write by lifecycle | Customer and provider events. |

Recorded cycle F example (Postman: `provider.orders.show` › "200 Provider order with driver trip"):

```json
{
    "status": "success",
    "message": "",
    "data": {
        "id": "01a18165-3b79-7267-a4e3-438a1f6e1852",
        "code": "JSH-qdxq41659376",
        "status": {
            "value": "confirmed",
            "label": "مؤكد"
        },
        "payment_status": {
            "value": "unpaid",
            "label": "غير مدفوع"
        },
        "booking_type": {
            "value": "instant",
            "label": "فوري"
        },
        "service_name": null,
        "customer": {
            "id": "01a18165-3b87-73e0-b15c-b980e26fb927",
            "name": "Mary Douglas"
        },
        "scheduled_at": null,
        "eta_minutes": 45,
        "net_earning": "80.00",
        "respond_before": null,
        "seconds_left": null,
        "can_respond": false,
        "can_prepare": true,
        "can_start_travel": false,
        "can_mark_arrived": false,
        "can_start_service": false,
        "can_complete": false,
        "can_rate": false,
        "services": [],
        "address": {
            "text": "Riyadh, Al Nakheel Dist",
            "lat": "24.774300",
            "lng": "46.738600"
        },
        "estimated_duration_min": null,
        "services_amount": "100.00",
        "provider_commission_rate": "20.00",
        "notes_to_provider": null,
        "rejection_reason": null,
        "created_at": "2026-10-25T07:00:00+00:00",
        "transport": {
            "mode": "joshen_driver",
            "chosen_at": "2026-10-25T07:00:00+00:00",
            "can_choose_transport": false,
            "can_request_driver": false,
            "can_retry_driver": false,
            "trip": {
                "id": "01a18165-3ba1-70e1-8eaf-1415088480f5",
                "code": "TRP-typk03773744",
                "status": {
                    "value": "accepted",
                    "label": "تم قبول الطلب من المندوبة"
                },
                "search_expires_at": "2026-10-25T07:05:00+00:00",
                "delivery_fee": "25.00",
                "driver": {
                    "first_name": "Brenda",
                    "vehicle": {
                        "type": "car",
                        "make_model": "Toyota Yaris",
                        "plate_number": "ABC 1234"
                    }
                }
            }
        }
    }
}
```

## 5. Dart models

Treat all IDs as UUID strings and money as exact decimal strings. Use `net_earning` as display only, not a payable balance. Count down from `respond_before` and always refresh at zero; the server decides whether the response was on time.

## 6. Business rules enforced by the backend

| Rule | Backend | App response |
|------|---------|--------------|
| BR-03 | Approved provider and own order only. | Hide operational UI for 403; 404 for foreign order. |
| D3/BR-12 | Instant provider response follows successful payment; paid rejection/timeout refunds. | Show final state; never attempt refund in the app. |
| BR-13/14 | Scheduled accept opens payment, then customer payment confirms. | Keep accepted request awaiting payment. |
| DEC-05/TD-92 | 20-second instant timer is configured; scheduled 30 minutes is a working value. | Refresh after deadline; show typed late error. |

## 7. Notifications and real-time events

Inbox rows (persisted, `data.order_id` is the UUID): `order_request_received` alerts the provider; `order_confirmed` reaches both apps; the customer receives `order_accepted_awaiting_payment`, `order_rejected` or `order_expired`.

**Realtime (TD-96, [API conventions §9](../API_CONVENTIONS.md)).** Connect the socket with the provider access token. The provider app receives `order.requested` (`reason: requested`) when a request needs an answer and `order.updated` on confirmation or cancellation; refetch the order on either event. At zero on `respond_before`, refetch the order. The payload contains no address or money. Delivery is best effort: refetch `GET /orders` on reconnect and while disconnected. FCM delivery depends on configuration and registered devices (TD-141–143).

## 8. Localization and UI notes

Service/option names are Arabic checkout snapshots (TD-12). Format money as SAR and timestamps in Saudi local time; keep their offsets for comparisons. The home statistics/earnings screen is not supplied by these endpoints.

## 9. Integration checklist

- [ ] List and open only provider-owned UUIDs; handle empty/loading/404/403.
- [ ] Show value, net earning, address and countdown from returned fields; enable the buttons from `can_respond`.
- [ ] Connect the socket (`order.requested` opens the card, `order.updated` refreshes it) and refetch on reconnect.
- [ ] Disable decisions after deadline and refresh on `PROVIDER_RESPONSE_EXPIRED`.
- [ ] Show today/upcoming tabs and the completed tab (newest first).
- [ ] Read the six `can_*` execution flags from every tile and the details; show the Rate action from `can_rate` (PROV-024 is built, see [ratings](ratings.md)).
- [ ] Test Arabic RTL, English and paid-instant refund state, and the socket dropping mid-countdown.

## 10. Acceptance scenarios

| AC ID | Scenario | Expected |
|-------|----------|----------|
| AC-PROV-014 | View pending request | Own details include service, address, value/net, deadline. |
| AC-PROV-015 | Accept scheduled | Opens customer payment; payment confirms. |
| AC-PROV-015 | Reject/timeout paid instant | Final state and one wallet refund. |
| AC-PROV-015 | Double/late response | Repeat same decision is safe; late response typed and expires. |
| AC-PROV-022 | Schedule tabs | Today/upcoming list (orders under way stay); completed lists her finished orders newest first. |
| AC-PROV-019/020 | Execution | See [order-execution](order-execution.md). |

## 11. Open decisions and known limitations

TD-92 values are working assumptions: pending_provider/awaiting_payment/confirmed block the full estimated duration; 15-minute slots, 60-minute lead, 45-minute ETA, 30-minute scheduled response and 15-minute payment. Values proposed by the project owner on 2026-10-25; DEC-05 stays Partly resolved until the client confirms the numbers. DEC-04 capacity/slot holds remain open. Fixed-amount provider commission is not supported at checkout until the earnings feature adds a snapshot column (TD-93). The local/testing simulator is a launch blocker (TD-94); DEC-06 vendor remains open.

Out of scope: group booking (D2), coupons, VAT on orders, wallet/payment/invoice screens, provider-side cancellation, provider home stats/earnings and the rating endpoints (PROV-024). Chat is in [chat](chat.md); transport choice is in [transportation](transportation.md); execution stages are in [order-execution](order-execution.md). FCM delivery depends on configuration and registered devices (TD-141–143).
