---
title: Delivery Confirmation Flow
tags: [flow, delivery, escrow-release, code]
related_models: ["[[PurchaseRequest]]", "[[Payment]]"]
related_apis: ["POST /api/marketplace/purchase-requests/:id/delivery-code/generate", "POST /api/marketplace/purchase-requests/:id/delivery-code/verify"]
---

# Delivery Confirmation Flow

> **Last updated:** 2026-05-29 — aligned with code (see [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md))

After the escrow is funded ([[PRD - Request Network In-House Checkout]] / [[Escrow Flow]]) and the seller has prepared the item, the seller **marks shipped**, the buyer **generates and reads out the delivery code**, the seller **verifies the code** to confirm receipt, and the escrow becomes eligible for release ([[Payout Flow]]).

## Actors

- **Buyer** — after the order reaches `delivery` status, explicitly generates the delivery code and reads it out to the seller at hand-off.
- **Seller** — types the code into their dashboard to confirm delivery.
- **Backend** — `DeliveryService` (`backend/src/services/delivery/DeliveryService.ts`), exposed through the marketplace routes (`backend/src/services/marketplace/routes.ts`).
- **MongoDB** — `purchaserequests.deliveryInfo` subdocument fields.
- **Socket.IO** — `delivery-code-generated`, `delivery-update`.

## Preconditions

- `PurchaseRequest.status` is `payment`, `processing`, or `delivery`.
- `Payment.escrowState === 'funded'`.

## Step-by-step narrative

1. **Seller marks shipped** — from the seller step `frontend/src/sections/request/components/seller-steps/step-3-ship-goods.tsx`, clicks "Mark as shipped". The frontend action `updateDelivery` calls `PUT /api/marketplace/purchase-requests/:id/delivery`. The controller's `updateDeliveryInfo` sets `shippedAt` and advances status to `delivery`. No code is generated at this point.
2. **Buyer generates the delivery code** — once status is `delivery`, the buyer explicitly triggers `POST /api/marketplace/purchase-requests/:id/delivery-code/generate` (buyerId is enforced server-side). `DeliveryService.generateDeliveryCode(requestId)`:
   - Generates a 6-digit code (`Math.floor(100000 + Math.random()*900000)`).
   - Sets `deliveryInfo.deliveryCode`, `deliveryCodeGeneratedAt = now`, `deliveryCodeExpiresAt = now + 7d`, `deliveryCodeUsed = false`.
   - Emits `delivery-code-generated` and `delivery-update` to `request-{requestId}`.
   - The code is displayed to the buyer in `frontend/src/sections/request/components/buyer-steps/step-5-receive-goods.tsx`.
3. **Buyer reads code to seller** — at hand-off the buyer reads the 6-digit code out loud (or shows it) to the seller.
4. **Seller enters code** — seller types the code into `frontend/src/sections/request/components/seller-steps/delivery-code-verification.tsx`.
5. **Verification** — `POST /api/marketplace/purchase-requests/:id/delivery-code/verify` with `{ code }` (selectedOffer.sellerId is enforced server-side). Handled by `DeliveryService.verifyDeliveryCode` (lines 180-212):
   - Matches `code` against `deliveryInfo.deliveryCode`.
   - Checks `deliveryCodeExpiresAt > now` and `deliveryCodeUsed === false`.
   - On success: `deliveryInfo.deliveryCodeUsed = true; deliveryCodeUsedAt = now`. Status flips `delivery → delivered`.
   - Emits `purchase-request-update` `status-changed`.
   - Sends delivery-confirmed notifications to both buyer and seller directly within `DeliveryService.verifyDeliveryCode`.
6. **Alternative path — buyer fast-track** — the buyer can also call `PATCH .../confirm-delivery` to set status to `delivered` without any code (used when the code path fails, e.g. code expired or lost). This endpoint emits only `purchase-request-update` with `status-changed` — it does **not** send delivery-specific notifications to either party. **⚠️ Authorization gap:** this endpoint currently has no authorization check; any authenticated user can call it.
7. **Optional auto-release timer** — once `status === 'delivered'`, a scheduled job can flip the request to `confirming` and then to `seller_paid` after a grace period (e.g. 48h). The auto-release worker is not yet implemented; today an admin completes the chain via [[Payout Flow]].

## Sequence diagram

```mermaid
sequenceDiagram
    autonumber
    actor S as Seller
    actor B as Buyer
    participant FE as Frontend
    participant BE as Backend
    participant DB as MongoDB
    participant IO as Socket.IO

    S->>FE: Click "Mark as shipped"
    FE->>BE: PUT /api/marketplace/purchase-requests/{id}/delivery
    BE->>DB: PurchaseRequest.shippedAt=now, status="delivery"
    Note over BE,DB: No code generated here

    B->>FE: View delivery code in step-5-receive-goods
    FE->>BE: POST /api/marketplace/purchase-requests/{id}/delivery-code/generate
    BE->>DB: deliveryInfo.deliveryCode=XXXXXX\nexpires=+7d
    BE->>IO: emit request-{id} 'delivery-code-generated' {code, expiresAt}
    FE->>B: Display 6-digit code

    B->>S: At hand-off, read the 6-digit code aloud
    S->>FE: Enter code in delivery-code-verification
    FE->>BE: POST /api/marketplace/purchase-requests/{id}/delivery-code/verify {code}
    BE->>DB: match code, expires>now, !used
    BE->>DB: set deliveryCodeUsed = true
    BE->>DB: set status = "delivered"
    BE->>IO: emit request-{id} 'purchase-request-update' status-changed
    BE->>B: notifyDeliveryConfirmed (DeliveryService.verifyDeliveryCode)
    BE->>S: notifyDeliveryConfirmed (DeliveryService.verifyDeliveryCode)
    Note over BE: Auto-release timer (planned) → seller_paid → payout
```

## API calls

| Method | Endpoint | Purpose |
|---|---|---|
| `PUT` | `/api/marketplace/purchase-requests/:id/delivery` | Seller marks shipped (sets shippedAt, advances to `delivery`) |
| `GET` | `/api/marketplace/purchase-requests/:id/delivery-code` | Retrieve current code (buyer + seller) |
| `POST` | `/api/marketplace/purchase-requests/:id/delivery-code/generate` | Buyer generates delivery code (buyer only) |
| `POST` | `/api/marketplace/purchase-requests/:id/delivery-code/verify` | Seller verifies code (seller only) |
| `GET` | `/api/marketplace/purchase-requests/:id/delivery-code/status` | Check code status (buyer + seller) |
| `PATCH` | `/api/marketplace/purchase-requests/:id/confirm-delivery` | Buyer fast-track confirm (no code) — ⚠️ no auth check, no delivery notifications |

### Phantom frontend actions (routes do NOT exist on backend)

These Redux/API actions exist in the frontend but call endpoints that return 404:

| Frontend action | Called path | Behaviour |
|---|---|---|
| `regenerateDeliveryCode` | `/delivery-code/regenerate` | 404s; frontend falls back to `/delivery-code/generate` |
| `getDeliveryAttempts` | `/delivery-code/attempts` | 404s — feature not implemented |
| `getDeliveryStats` | `/delivery/stats` | 404s — feature not implemented |

## Two paths to `delivered` status

1. **Code path** — seller calls `POST .../delivery-code/verify` with the correct, unexpired code → status becomes `delivered`. Both buyer and seller receive delivery-confirmed notifications (sent by `DeliveryService.verifyDeliveryCode`).
2. **Fast-track path** — buyer calls `PATCH .../confirm-delivery` (no code required) → also becomes `delivered`. ⚠️ Currently no authorization check on this endpoint, and no delivery-specific notifications are sent to either party.

## Database writes

- **`purchaserequests.deliveryInfo`** — `deliveryCode`, `deliveryCodeGeneratedAt`, `deliveryCodeExpiresAt`, `deliveryCodeUsed`, `deliveryCodeUsedAt`.
- **`purchaserequests.shippedAt`** — set when seller calls `PUT .../delivery`.
- **`purchaserequests.status`** — `delivery` → `delivered` → (eventually `seller_paid` → `completed`).
- **`notifications`** — generated for both parties (code path only).

## Socket events emitted

- **`delivery-code-generated`** → `request-{id}` room (payload: `{ requestId, code, expiresAt, timestamp }`). **⚠️ Security note:** the full 6-digit code is included in the payload and broadcast to all subscribers in the room, including the seller. The buyer dashboard displays the code; the seller receives it via socket as well.
- **`delivery-update`** → `request-{id}` (`type: 'code-generated'`).
- **`purchase-request-update`** `status-changed` on `delivery → delivered`.

## Side effects

- The code is displayed to the **buyer** in their dashboard. The buyer verbally shares it with the seller at hand-off. Note that the `delivery-code-generated` socket event also broadcasts the raw code to the entire request room (including the seller — see socket events section above).
- Triggers the path that eventually frees up the escrow (manual today via [[Payout Flow]], auto in the future).

## Error / edge cases

- **Wrong code** → `400 Invalid delivery code`.
- **Expired code** (>7 days) → `400 Code expired`. Buyer can generate a new code via `POST .../delivery-code/generate` (the `regenerateDeliveryCode` frontend action also falls through to this endpoint).
- **Already used code** → `400 Code already used`.
- **Buyer never generates / confirms** → status remains `delivery`. Auto-release timer (not yet built) should trigger `delivered` after N days. Until then, admin intervention.
- **Seller delivers but never marks shipped** → buyer can dispute via [[Dispute Flow]]; the dispute resolution will release the escrow regardless.
- **Lost / expired code** → buyer re-triggers `POST .../delivery-code/generate` to get a fresh code, invalidating the old one.

> [!tip] The buyer holds the code, not the seller
> The seller should ask the buyer for the code at hand-off. If the buyer disputes "never received", an unused code is strong circumstantial evidence that delivery has not been confirmed; a used code = seller confirmed receipt.

## Linked flows

- [[PRD - Request Network In-House Checkout]] / [[Escrow Flow]] — funding precondition.
- [[Escrow Flow]] — state transitions triggered by confirmation.
- [[Payout Flow]] — fires after confirmation (manual today).
- [[Dispute Flow]] — escape hatch.
- [[Notification Flow]] — channel for the code.

## Source files

- Backend: `backend/src/services/delivery/DeliveryService.ts` (generateDeliveryCode, verifyDeliveryCode lines 180-212)
- Backend: `backend/src/services/marketplace/routes.ts` (delivery endpoints)
- Frontend: `frontend/src/sections/request/components/seller-steps/step-3-ship-goods.tsx`
- Frontend: `frontend/src/sections/request/components/seller-steps/delivery-code-verification.tsx`
- Frontend: `frontend/src/sections/request/components/buyer-steps/step-5-receive-goods.tsx`
