# Sanad Platform — Backend API Requirements

**Document Version:** 1.0
**Date:** March 17, 2026
**Prepared for:** Backend Development Team
**Purpose:** Complete API specification for the Dashboard and Marketplace modules

---

## Table of Contents

1. [Overview](#overview)
2. [General Conventions](#general-conventions)
3. [Dashboard APIs](#1-dashboard-apis)
4. [Marketplace — Influencer Offers](#2-marketplace--influencer-offers)
5. [Marketplace — Orders](#3-marketplace--orders)
6. [Marketplace — Transactions](#4-marketplace--transactions)
7. [Marketplace — Influencer Payouts](#5-marketplace--influencer-payouts)
8. [Endpoint Summary Table](#endpoint-summary-table)

---

## Overview

The Sanad admin panel requires 14 API endpoints to power two modules:

- **Main Dashboard** — Aggregated platform KPIs and marketplace activity feed
- **Marketplace** — Full lifecycle management of influencer offers, merchant orders, wallet payments, escrow, and influencer payouts

### Business Flow

```
1. Influencer creates an offer (campaign/product)
2. Admin reviews and approves the offer
3. Merchant browses offers and places an order
4. Merchant pays from wallet → funds held in Platform Escrow
5. Influencer delivers the service
6. Merchant confirms delivery
7. Admin releases escrow → funds transferred to influencer account
8. Influencer requests payout (withdrawal to bank)
9. Admin approves and processes the bank transfer
```

---

## General Conventions

### Authentication
All endpoints require authentication via XSRF-TOKEN cookie sent as `Authorization: Bearer {token}` header.

### Base URL
```
https://sanad.work/production/api
```

### Pagination (all list endpoints)
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page` | integer | 1 | Page number |
| `per_page` | integer | 20 | Items per page |

### Standard Paginated Response Wrapper
```json
{
  "data": [...],
  "total": 156,
  "page": 1,
  "per_page": 20,
  "totalPages": 8
}
```

### Currency
All monetary values are in **SAR** (Saudi Riyal) as integers (no decimals).

### Dates
All dates in **ISO 8601** format: `"2026-03-17T09:15:00Z"`

### Error Response Format
```json
{
  "success": false,
  "message": "Error description",
  "errors": {
    "field_name": ["Validation message"]
  }
}
```

---

## 1. Dashboard APIs

### 1.1 GET `/api/dashboard/stats`

Returns aggregated KPI numbers for the main dashboard cards.

**Parameters:** None

**Response:**
```json
{
  "totalUsers": 1250,
  "totalSubscriptions": 834,
  "totalPackages": 45,
  "totalRevenue": 125000,
  "currency": "SAR",
  "trends": {
    "users": {
      "percentage": 12.5,
      "direction": "up"
    },
    "subscriptions": {
      "percentage": 8.2,
      "direction": "up"
    },
    "packages": {
      "percentage": 3.1,
      "direction": "up"
    },
    "revenue": {
      "percentage": 15.3,
      "direction": "up"
    }
  }
}
```

**Notes:**
- Trends compare current month vs previous month
- `direction` is either `"up"` or `"down"`

---

### 1.2 GET `/api/dashboard/marketplace-summary`

Returns marketplace overview metrics and recent activity feed shown in the dashboard widgets.

**Parameters:** None

**Response:**
```json
{
  "metrics": {
    "activeOffers": 32,
    "openOrders": 24,
    "escrowBalance": 24225,
    "pendingPayouts": 5,
    "currency": "SAR"
  },
  "recentActivity": [
    {
      "id": 1,
      "type": "order",
      "title": "New order placed",
      "description": "TechStore SA ordered Instagram Story Campaign from Sarah Ahmed",
      "amount": 2500,
      "currency": "SAR",
      "status": "paid",
      "createdAt": "2026-03-17T09:15:00Z"
    },
    {
      "id": 2,
      "type": "offer",
      "title": "New offer submitted",
      "description": "Mohammed Ali listed YouTube Product Review",
      "amount": 8000,
      "currency": "SAR",
      "status": "pending",
      "createdAt": "2026-03-17T08:45:00Z"
    }
  ]
}
```

**Activity `type` values:**

| Type | Description |
|------|-------------|
| `order` | Merchant placed a new order |
| `offer` | Influencer submitted a new offer |
| `confirmation` | Merchant confirmed service delivery |
| `payout` | Influencer requested a withdrawal |
| `dispute` | A dispute was opened on an order |

**Activity `status` values:**

| Status | Description |
|--------|-------------|
| `paid` | Payment completed |
| `pending` | Awaiting review |
| `completed` | Fully completed |
| `processing` | Currently being processed |
| `disputed` | Under dispute |

---

## 2. Marketplace — Influencer Offers

### 2.1 GET `/api/marketplace/offers`

List all influencer campaign offers with filtering and pagination.

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number (default: 1) |
| `per_page` | integer | No | Items per page (default: 20) |
| `search` | string | No | Search by offer title or influencer name |
| `status` | string | No | Filter: `active`, `pending_review`, `paused`, `rejected` |
| `platform` | string | No | Filter: `Instagram`, `YouTube`, `Twitter` |
| `category` | string | No | Filter by category name |

**Response:**
```json
{
  "data": [
    {
      "id": "OFF-001",
      "influencer": {
        "id": 10,
        "name": "Sarah Ahmed",
        "avatar": "https://storage.example.com/avatars/sarah.jpg",
        "followers": "125K",
        "platform": "Instagram"
      },
      "title": "Instagram Story Campaign",
      "description": "Professional Instagram story promotion with swipe-up link, reaching 125K followers in Fashion & Beauty niche.",
      "platform": "Instagram",
      "category": "Fashion & Beauty",
      "price": 2500,
      "currency": "SAR",
      "rating": 4.8,
      "totalOrders": 12,
      "status": "active",
      "createdAt": "2026-03-10T00:00:00Z",
      "updatedAt": "2026-03-10T00:00:00Z"
    }
  ],
  "total": 48,
  "page": 1,
  "per_page": 20,
  "totalPages": 3,
  "summary": {
    "totalOffers": 48,
    "active": 32,
    "pendingReview": 8,
    "paused": 5,
    "rejected": 3,
    "totalRevenue": 245000,
    "currency": "SAR"
  }
}
```

**Offer `status` values:**

| Status | Description |
|--------|-------------|
| `active` | Live and visible to merchants |
| `pending_review` | Submitted by influencer, awaiting admin approval |
| `paused` | Temporarily hidden by admin or influencer |
| `rejected` | Rejected by admin |

---

### 2.2 PUT `/api/marketplace/offers/{id}/status`

Admin action to approve, pause, or reject an offer.

**URL Parameters:**
- `id` — Offer ID (e.g., `OFF-001`)

**Request Body:**
```json
{
  "status": "active",
  "reason": "Optional reason for rejection or pause"
}
```

**Valid status transitions:**
- `pending_review` → `active` (approve)
- `pending_review` → `rejected` (reject)
- `active` → `paused` (pause)
- `paused` → `active` (resume)

**Response:**
```json
{
  "success": true,
  "message": "Offer status updated successfully",
  "data": {
    "id": "OFF-001",
    "status": "active",
    "updatedAt": "2026-03-17T14:30:00Z"
  }
}
```

---

## 3. Marketplace — Orders

### 3.1 GET `/api/marketplace/orders`

List all merchant orders with filtering and pagination.

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number (default: 1) |
| `per_page` | integer | No | Items per page (default: 20) |
| `search` | string | No | Search by order ID, merchant name, or influencer name |
| `status` | string | No | Filter: `paid`, `in_progress`, `awaiting_confirmation`, `completed`, `cancelled`, `disputed` |
| `payout_status` | string | No | Filter: `held`, `released`, `pending`, `refunded`, `frozen` |

**Response:**
```json
{
  "data": [
    {
      "id": "ORD-1001",
      "merchant": {
        "id": 20,
        "name": "TechStore SA",
        "avatar": "https://storage.example.com/avatars/techstore.jpg"
      },
      "influencer": {
        "id": 10,
        "name": "Sarah Ahmed",
        "avatar": "https://storage.example.com/avatars/sarah.jpg"
      },
      "offer": {
        "id": "OFF-001",
        "title": "Instagram Story Campaign"
      },
      "amount": 2500,
      "currency": "SAR",
      "walletBalance": 15000,
      "status": "completed",
      "payoutStatus": "released",
      "orderedAt": "2026-03-10T00:00:00Z",
      "completedAt": "2026-03-14T00:00:00Z"
    }
  ],
  "total": 156,
  "page": 1,
  "per_page": 20,
  "totalPages": 8,
  "summary": {
    "totalOrders": 156,
    "inProgress": 24,
    "awaitingConfirmation": 12,
    "completed": 108,
    "disputed": 3,
    "cancelled": 9
  }
}
```

**Order `status` values:**

| Status | Description |
|--------|-------------|
| `paid` | Merchant paid, funds in escrow, work not started |
| `in_progress` | Influencer is working on the campaign |
| `awaiting_confirmation` | Influencer delivered, waiting for merchant to confirm |
| `completed` | Merchant confirmed delivery |
| `cancelled` | Order cancelled |
| `disputed` | Dispute opened by either party |

**Order `payoutStatus` values:**

| Status | Description |
|--------|-------------|
| `pending` | Payout not yet applicable |
| `held` | Funds held in escrow |
| `released` | Funds released to influencer |
| `refunded` | Funds refunded to merchant wallet |
| `frozen` | Funds frozen due to dispute |

---

### 3.2 PUT `/api/marketplace/orders/{id}/confirm-delivery`

Admin confirms the merchant has received the service from the influencer.

**URL Parameters:**
- `id` — Order ID (e.g., `ORD-1001`)

**Request Body:** None

**Response:**
```json
{
  "success": true,
  "message": "Delivery confirmed successfully",
  "data": {
    "id": "ORD-1001",
    "status": "completed",
    "completedAt": "2026-03-17T14:30:00Z"
  }
}
```

---

### 3.3 PUT `/api/marketplace/orders/{id}/release-payout`

Admin releases escrowed funds to the influencer's account balance.

**URL Parameters:**
- `id` — Order ID

**Request Body:** None

**Response:**
```json
{
  "success": true,
  "message": "Payout released to influencer",
  "data": {
    "id": "ORD-1001",
    "payoutStatus": "released",
    "releasedAmount": 2375,
    "platformFee": 125,
    "currency": "SAR",
    "releasedAt": "2026-03-17T14:30:00Z",
    "transactionId": "TXN-5002"
  }
}
```

---

### 3.4 PUT `/api/marketplace/orders/{id}/refund`

Admin refunds the order amount back to the merchant's wallet.

**URL Parameters:**
- `id` — Order ID

**Request Body:**
```json
{
  "reason": "Service not delivered within agreed timeframe"
}
```

**Response:**
```json
{
  "success": true,
  "message": "Order refunded to merchant wallet",
  "data": {
    "id": "ORD-1001",
    "status": "cancelled",
    "payoutStatus": "refunded",
    "refundedAmount": 2500,
    "currency": "SAR",
    "refundedAt": "2026-03-17T14:30:00Z",
    "transactionId": "TXN-5010"
  }
}
```

---

### 3.5 PUT `/api/marketplace/orders/{id}/freeze`

Admin freezes the escrowed funds on a disputed order pending investigation.

**URL Parameters:**
- `id` — Order ID

**Request Body:**
```json
{
  "reason": "Dispute under investigation"
}
```

**Response:**
```json
{
  "success": true,
  "message": "Funds frozen successfully",
  "data": {
    "id": "ORD-1001",
    "status": "disputed",
    "payoutStatus": "frozen",
    "frozenAmount": 2500,
    "currency": "SAR",
    "frozenAt": "2026-03-17T14:30:00Z"
  }
}
```

---

## 4. Marketplace — Transactions

### 4.1 GET `/api/marketplace/transactions`

Complete financial ledger of all money movements in the marketplace.

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number (default: 1) |
| `per_page` | integer | No | Items per page (default: 20) |
| `search` | string | No | Search by transaction ID, sender name, or receiver name |
| `type` | string | No | Filter: `payment`, `escrow_release`, `payout`, `refund`, `wallet_topup` |
| `status` | string | No | Filter: `completed`, `processing`, `frozen` |
| `date_from` | string | No | ISO date, start of date range filter |
| `date_to` | string | No | ISO date, end of date range filter |

**Response:**
```json
{
  "data": [
    {
      "id": "TXN-5001",
      "type": "payment",
      "from": {
        "id": 20,
        "name": "TechStore SA",
        "type": "Merchant"
      },
      "to": {
        "id": null,
        "name": "Platform Escrow",
        "type": "System"
      },
      "amount": 2500,
      "fee": 125,
      "net": 2375,
      "currency": "SAR",
      "orderId": "ORD-1001",
      "status": "completed",
      "description": "Payment for Instagram Story Campaign",
      "createdAt": "2026-03-10T09:15:00Z"
    }
  ],
  "total": 340,
  "page": 1,
  "per_page": 20,
  "totalPages": 17,
  "summary": {
    "totalVolume": 56375,
    "platformFees": 1415,
    "inEscrow": 24225,
    "frozenFunds": 2500,
    "currency": "SAR"
  }
}
```

**Transaction `type` values:**

| Type | Description | From → To |
|------|-------------|-----------|
| `payment` | Merchant pays for an order | Merchant → Platform Escrow |
| `escrow_release` | Escrow released after confirmation | Platform Escrow → Influencer |
| `payout` | Influencer withdraws to bank | Influencer → Bank Account |
| `refund` | Order refund to merchant | Platform Escrow → Merchant |
| `wallet_topup` | Merchant tops up wallet | Bank/Card → Merchant Wallet |

**Transaction `status` values:**

| Status | Description |
|--------|-------------|
| `completed` | Transaction fully processed |
| `processing` | Transaction in progress (e.g., bank transfer pending) |
| `frozen` | Transaction frozen due to dispute |

**Party `type` values:**

| Type | Description |
|------|-------------|
| `Merchant` | Business that orders campaigns |
| `Influencer` | Content creator providing the service |
| `System` | Platform escrow or internal system account |
| `External` | External bank account or payment method |

---

## 5. Marketplace — Influencer Payouts

### 5.1 GET `/api/marketplace/payouts`

List all influencer payout (withdrawal) requests.

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `page` | integer | No | Page number (default: 1) |
| `per_page` | integer | No | Items per page (default: 20) |
| `search` | string | No | Search by payout ID or influencer name |
| `status` | string | No | Filter: `pending`, `approved`, `processing`, `completed`, `rejected` |

**Response:**
```json
{
  "data": [
    {
      "id": "PAY-2001",
      "influencer": {
        "id": 10,
        "name": "Sarah Ahmed",
        "email": "sarah@example.com",
        "avatar": "https://storage.example.com/avatars/sarah.jpg"
      },
      "bankDetails": {
        "bankName": "Al Rajhi Bank",
        "accountEnding": "4521",
        "iban": "SA***************4521"
      },
      "availableBalance": 12500,
      "requestedAmount": 5000,
      "fee": 25,
      "netAmount": 4975,
      "currency": "SAR",
      "ordersCompleted": 8,
      "status": "approved",
      "requestedAt": "2026-03-15T08:30:00Z",
      "processedAt": "2026-03-15T14:00:00Z"
    }
  ],
  "total": 28,
  "page": 1,
  "per_page": 20,
  "totalPages": 2,
  "summary": {
    "pendingReview": 5,
    "processing": 3,
    "totalPaidOut": 185000,
    "platformFees": 9200,
    "currency": "SAR"
  }
}
```

**Payout `status` values:**

| Status | Description |
|--------|-------------|
| `pending` | Submitted by influencer, awaiting admin review |
| `approved` | Approved by admin, queued for processing |
| `processing` | Bank transfer initiated, awaiting completion |
| `completed` | Bank transfer completed, funds received by influencer |
| `rejected` | Rejected by admin |

---

### 5.2 PUT `/api/marketplace/payouts/{id}/approve`

Admin approves a payout request.

**URL Parameters:**
- `id` — Payout ID (e.g., `PAY-2001`)

**Request Body:** None

**Response:**
```json
{
  "success": true,
  "message": "Payout approved successfully",
  "data": {
    "id": "PAY-2001",
    "status": "approved",
    "approvedAt": "2026-03-17T14:30:00Z"
  }
}
```

---

### 5.3 PUT `/api/marketplace/payouts/{id}/process`

Admin triggers the bank transfer for an approved payout.

**URL Parameters:**
- `id` — Payout ID

**Request Body:** None

**Response:**
```json
{
  "success": true,
  "message": "Payout processing initiated",
  "data": {
    "id": "PAY-2001",
    "status": "processing",
    "transactionId": "TXN-5020",
    "estimatedCompletion": "2026-03-18T14:30:00Z"
  }
}
```

---

### 5.4 PUT `/api/marketplace/payouts/{id}/reject`

Admin rejects a payout request with a reason.

**URL Parameters:**
- `id` — Payout ID

**Request Body:**
```json
{
  "reason": "Insufficient completed orders to meet minimum payout threshold"
}
```

**Response:**
```json
{
  "success": true,
  "message": "Payout rejected",
  "data": {
    "id": "PAY-2001",
    "status": "rejected",
    "reason": "Insufficient completed orders to meet minimum payout threshold",
    "rejectedAt": "2026-03-17T14:30:00Z"
  }
}
```

---

## Endpoint Summary Table

| # | Method | Endpoint | Purpose |
|---|--------|----------|---------|
| 1 | GET | `/api/dashboard/stats` | Dashboard KPI cards (users, subscriptions, packages, revenue) |
| 2 | GET | `/api/dashboard/marketplace-summary` | Dashboard marketplace widgets (metrics + activity feed) |
| 3 | GET | `/api/marketplace/offers` | List all influencer offers with search and filters |
| 4 | PUT | `/api/marketplace/offers/{id}/status` | Admin: approve, pause, or reject an offer |
| 5 | GET | `/api/marketplace/orders` | List all merchant orders with search and filters |
| 6 | PUT | `/api/marketplace/orders/{id}/confirm-delivery` | Admin: confirm merchant received the service |
| 7 | PUT | `/api/marketplace/orders/{id}/release-payout` | Admin: release escrow funds to influencer |
| 8 | PUT | `/api/marketplace/orders/{id}/refund` | Admin: refund order to merchant wallet |
| 9 | PUT | `/api/marketplace/orders/{id}/freeze` | Admin: freeze funds on disputed order |
| 10 | GET | `/api/marketplace/transactions` | Full financial transaction ledger |
| 11 | GET | `/api/marketplace/payouts` | List influencer payout requests |
| 12 | PUT | `/api/marketplace/payouts/{id}/approve` | Admin: approve payout request |
| 13 | PUT | `/api/marketplace/payouts/{id}/process` | Admin: trigger bank transfer |
| 14 | PUT | `/api/marketplace/payouts/{id}/reject` | Admin: reject payout request |

---

## Database Entities (Suggested)

For the backend team's reference, the following database tables/models are implied by these APIs:

| Entity | Key Fields |
|--------|------------|
| **Offer** | id, influencer_id, title, description, platform, category, price, rating, total_orders, status, created_at |
| **Order** | id, merchant_id, influencer_id, offer_id, amount, status, payout_status, ordered_at, completed_at |
| **Transaction** | id, type, from_id, from_type, to_id, to_type, amount, fee, net, order_id, status, description, created_at |
| **Payout** | id, influencer_id, bank_name, account_ending, available_balance, requested_amount, fee, net_amount, status, requested_at, processed_at |
| **Wallet** | id, user_id, balance, currency |
| **Escrow** | id, order_id, amount, status, created_at, released_at |

---

*End of Document*
