> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/Ishaq74/concordia/llms.txt
> Use this file to discover all available pages before exploring further.

# Services API

> Manage service listings, bookings, availability, and categories

## Overview

The Services API allows you to manage service listings, bookings, availability schedules, categories, and media.

All Services API endpoints require **admin authentication**.

***

## Service Listings

### List Services

From `/src/pages/api/admin/services/services.ts:20-112`:

<CodeGroup>
  ```bash GET Request theme={null}
  curl "https://your-domain.com/api/admin/services/services?status=active&page=1" \
    -H "Authorization: Bearer <token>"
  ```

  ```json Response theme={null}
  {
    "services": [
      {
        "id": "svc_123",
        "slug": "web-development",
        "categoryId": "cat_001",
        "status": "active",
        "basePrice": "150.00",
        "priceType": "hourly",
        "currency": "EUR",
        "durationMinutes": 60,
        "isMobile": false,
        "isFeatured": true,
        "displayInHome": true,
        "translations": [
          {
            "inLanguage": "fr",
            "title": "Développement Web"
          }
        ],
        "category": {
          "id": "cat_001",
          "name": { "fr": "Développement" },
          "slug": "development"
        }
      }
    ],
    "total": 45,
    "page": 1,
    "perPage": 20,
    "totalPages": 3
  }
  ```
</CodeGroup>

#### Query Parameters

<ParamField query="page" type="integer" default="1">
  Page number
</ParamField>

<ParamField query="perPage" type="integer" default="20">
  Items per page (max: 100)
</ParamField>

<ParamField query="q" type="string">
  Search by slug
</ParamField>

<ParamField query="status" type="string">
  Filter by status: `draft`, `active`, `suspended`, `archived`
</ParamField>

<ParamField query="featured" type="boolean">
  Filter featured services
</ParamField>

<ParamField query="home" type="boolean">
  Filter services displayed on homepage
</ParamField>

<ParamField query="category" type="string">
  Filter by category ID
</ParamField>

<ParamField query="orgId" type="string">
  Filter by organization ID
</ParamField>

<ParamField query="id" type="string">
  Fetch single service with full details (translations, media)
</ParamField>

***

### Create Service

From `/src/pages/api/admin/services/services.ts:136-206`:

<CodeGroup>
  ```bash POST Request theme={null}
  curl -X POST "https://your-domain.com/api/admin/services/services" \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -d @service.json
  ```

  ```json service.json theme={null}
  {
    "action": "create",
    "slug": "web-development",
    "categoryId": "cat_001",
    "providerId": "usr_123",
    "organizationId": "org_456",
    "status": "draft",
    "basePrice": "150.00",
    "priceType": "hourly",
    "currency": "EUR",
    "durationMinutes": 60,
    "isMobile": false,
    "maxParticipants": 1,
    "bookingAdvanceHours": 24,
    "cancellationHours": 12,
    "isFeatured": false,
    "displayInHome": true,
    "allowReviews": true,
    "inLanguage": "fr",
    "translations": [
      {
        "inLanguage": "fr",
        "title": "Développement Web",
        "description": "Services de développement web professionnel",
        "shortDescription": "Dev web",
        "seoTitle": "Développement Web Pro",
        "seoDescription": "Services de dev web"
      }
    ],
    "media": [
      {
        "mediaId": "media_789",
        "type": "cover",
        "position": 0
      }
    ]
  }
  ```

  ```json Response theme={null}
  {
    "id": "svc_999",
    "slug": "web-development"
  }
  ```
</CodeGroup>

#### Request Body

<ParamField body="action" type="string" required>
  Must be `"create"`
</ParamField>

<ParamField body="slug" type="string" required>
  URL-friendly service slug
</ParamField>

<ParamField body="categoryId" type="string">
  Category ID (nullable)
</ParamField>

<ParamField body="providerId" type="string" required>
  User ID of service provider
</ParamField>

<ParamField body="organizationId" type="string">
  Organization ID (nullable)
</ParamField>

<ParamField body="status" type="string" default="draft">
  Service status: `draft`, `active`, `suspended`, `archived`
</ParamField>

<ParamField body="basePrice" type="string">
  Service price (decimal string, e.g., "150.00")
</ParamField>

<ParamField body="priceType" type="string">
  Pricing model: `hourly`, `fixed`, `per_session`, `custom`
</ParamField>

<ParamField body="currency" type="string" default="EUR">
  ISO currency code (EUR, USD, etc.)
</ParamField>

<ParamField body="durationMinutes" type="number">
  Service duration in minutes
</ParamField>

<ParamField body="isMobile" type="boolean" default="false">
  Service available at customer location
</ParamField>

<ParamField body="maxParticipants" type="number">
  Maximum participants per booking
</ParamField>

<ParamField body="bookingAdvanceHours" type="number">
  Minimum hours required before booking
</ParamField>

<ParamField body="cancellationHours" type="number">
  Hours before booking when cancellation is allowed
</ParamField>

<ParamField body="isFeatured" type="boolean" default="false">
  Mark as featured
</ParamField>

<ParamField body="displayInHome" type="boolean" default="false">
  Show on homepage
</ParamField>

<ParamField body="allowReviews" type="boolean" default="true">
  Enable customer reviews
</ParamField>

<ParamField body="translations" type="array">
  Array of translation objects with `inLanguage`, `title`, `description`, etc.
</ParamField>

<ParamField body="media" type="array">
  Array of media objects with `mediaId`, `type` (cover/gallery), `position`
</ParamField>

***

### Update Service

From `/src/pages/api/admin/services/services.ts:208-286`:

```json theme={null}
{
  "action": "update",
  "id": "svc_999",
  "slug": "professional-web-dev",
  "status": "active",
  "basePrice": "175.00",
  "isFeatured": true,
  "translations": [...],
  "media": [...]
}
```

Updates replace translations and media (full replacement strategy).

***

### Other Service Actions

From `/src/pages/api/admin/services/services.ts:288-371`:

<CodeGroup>
  ```json Delete theme={null}
  {
    "action": "delete",
    "id": "svc_999"
  }
  ```

  ```json Activate theme={null}
  {
    "action": "activate",
    "id": "svc_999"
  }
  ```

  ```json Deactivate theme={null}
  {
    "action": "deactivate",
    "id": "svc_999"
  }
  ```

  ```json Duplicate theme={null}
  {
    "action": "duplicate",
    "id": "svc_999"
  }
  ```
</CodeGroup>

Duplicating creates a copy with `status: "draft"` and unique slug.

***

## Service Categories

### List Categories

From `/src/pages/api/admin/services/categories.ts:19-99`:

```bash theme={null}
GET /api/admin/services/categories?all=true
```

#### Query Parameters

<ParamField query="id" type="string">
  Fetch single category by ID (includes service count)
</ParamField>

<ParamField query="all" type="boolean">
  Return all categories for selectors
</ParamField>

<ParamField query="parent" type="string">
  Filter by parent ID (use `"root"` for top-level)
</ParamField>

<ParamField query="featured" type="boolean">
  Filter featured categories
</ParamField>

<ParamField query="home" type="boolean">
  Filter categories displayed on homepage
</ParamField>

<ParamField query="menu" type="boolean">
  Filter categories shown in menu
</ParamField>

***

### Create Category

From `/src/pages/api/admin/services/categories.ts:124-155`:

```json theme={null}
{
  "action": "create",
  "slug": "development",
  "name": { "fr": "Développement", "en": "Development" },
  "description": { "fr": "Services de développement" },
  "icon": "code",
  "parentId": null,
  "sortOrder": 0,
  "displayInHome": true,
  "displayInMenu": true,
  "isActive": true,
  "isFeatured": false
}
```

***

### Update/Delete Category

From `/src/pages/api/admin/services/categories.ts:157-216`:

<CodeGroup>
  ```json Update theme={null}
  {
    "action": "update",
    "id": "cat_001",
    "name": { "fr": "Nouveau nom" },
    "sortOrder": 10
  }
  ```

  ```json Delete theme={null}
  {
    "action": "delete",
    "id": "cat_001"
  }
  ```
</CodeGroup>

Deleting a category unlinks all services (sets `categoryId` to `null`).

***

## Availability

### List Availability Slots

From `/src/pages/api/admin/services/availability.ts:13-30`:

```bash theme={null}
GET /api/admin/services/availability?serviceId=svc_123
```

#### Response

```json theme={null}
{
  "slots": [
    {
      "id": "slot_001",
      "serviceId": "svc_123",
      "dayOfWeek": 1,
      "startTime": "09:00",
      "endTime": "17:00",
      "isAvailable": true
    },
    {
      "dayOfWeek": 2,
      "startTime": "09:00",
      "endTime": "17:00",
      "isAvailable": true
    }
  ]
}
```

**Day of Week**: 0 = Sunday, 1 = Monday, ..., 6 = Saturday

***

### Manage Availability

From `/src/pages/api/admin/services/availability.ts:36-127`:

<CodeGroup>
  ```json Create Slot theme={null}
  {
    "action": "create",
    "serviceId": "svc_123",
    "dayOfWeek": 1,
    "startTime": "09:00",
    "endTime": "17:00",
    "isAvailable": true
  }
  ```

  ```json Update Slot theme={null}
  {
    "action": "update",
    "id": "slot_001",
    "startTime": "10:00",
    "endTime": "18:00"
  }
  ```

  ```json Delete Slot theme={null}
  {
    "action": "delete",
    "id": "slot_001"
  }
  ```

  ```json Bulk Set (Replace All) theme={null}
  {
    "action": "bulkSet",
    "serviceId": "svc_123",
    "slots": [
      { "dayOfWeek": 1, "startTime": "09:00", "endTime": "17:00" },
      { "dayOfWeek": 2, "startTime": "09:00", "endTime": "17:00" },
      { "dayOfWeek": 3, "startTime": "09:00", "endTime": "17:00" }
    ]
  }
  ```
</CodeGroup>

***

## Bookings

### List Bookings

From `/src/pages/api/admin/services/bookings.ts:14-96`:

```bash theme={null}
GET /api/admin/services/bookings?status=confirmed&page=1
```

#### Query Parameters

<ParamField query="status" type="string">
  Filter by status: `pending`, `confirmed`, `cancelled_by_customer`, `cancelled_by_provider`, `completed`, `no_show`
</ParamField>

<ParamField query="serviceId" type="string">
  Filter by service ID
</ParamField>

<ParamField query="q" type="string">
  Search in customer messages
</ParamField>

<ParamField query="from" type="string">
  Filter bookings from date (ISO 8601)
</ParamField>

<ParamField query="to" type="string">
  Filter bookings to date (ISO 8601)
</ParamField>

#### Response

```json theme={null}
{
  "bookings": [
    {
      "id": "book_001",
      "serviceId": "svc_123",
      "customerId": "usr_456",
      "status": "confirmed",
      "bookingDate": "2024-03-15",
      "bookingTime": "14:00",
      "customerMessage": "Looking forward to it!",
      "providerResponse": "Confirmed. See you then!",
      "serviceSlug": "web-development",
      "serviceTitle": "Développement Web",
      "customerName": "Jane Smith",
      "customerEmail": "jane@example.com",
      "customerImage": "https://example.com/avatar.jpg"
    }
  ],
  "total": 25,
  "page": 1,
  "perPage": 20,
  "totalPages": 2
}
```

***

### Update Booking Status

From `/src/pages/api/admin/services/bookings.ts:102-154`:

<CodeGroup>
  ```json Confirm theme={null}
  {
    "id": "book_001",
    "action": "confirm"
  }
  ```

  ```json Cancel theme={null}
  {
    "id": "book_001",
    "action": "cancel"
  }
  ```

  ```json Complete theme={null}
  {
    "id": "book_001",
    "action": "complete"
  }
  ```

  ```json Mark No-Show theme={null}
  {
    "id": "book_001",
    "action": "no_show"
  }
  ```

  ```json Respond to Customer theme={null}
  {
    "id": "book_001",
    "action": "respond",
    "providerResponse": "Thanks for booking! See you at 2pm."
  }
  ```
</CodeGroup>

**Method**: `PATCH /api/admin/services/bookings`

***

## Service Media

### List Service Media

From `/src/pages/api/admin/services/media.ts:20-51`:

```bash theme={null}
GET /api/admin/services/media?type=image&page=1
```

***

### Upload Service Media

From `/src/pages/api/admin/services/media.ts:57-208`:

<CodeGroup>
  ```bash Upload theme={null}
  curl -X POST "https://your-domain.com/api/admin/services/media" \
    -H "Authorization: Bearer <token>" \
    -F "file=@service-image.jpg" \
    -F "alt=Service image" \
    -F "customName=web-dev-service"
  ```

  ```json Response theme={null}
  {
    "ok": true,
    "id": "media_333",
    "url": "/uploads/services/web-dev-service.jpg"
  }
  ```
</CodeGroup>

#### Configuration

* **Upload directory**: `public/uploads/services`
* **Max file size**: 10 MB
* **Allowed types**: JPEG, PNG, WebP, AVIF, GIF, SVG

***

### Update/Delete Service Media

<CodeGroup>
  ```json Update theme={null}
  {
    "action": "update",
    "id": "media_333",
    "caption": { "fr": "Image du service" },
    "alt": { "fr": "Photo de service web" }
  }
  ```

  ```json Delete theme={null}
  {
    "action": "delete",
    "id": "media_333"
  }
  ```
</CodeGroup>

***

## Common Response Codes

| Code | Description           |
| ---- | --------------------- |
| 200  | Success               |
| 201  | Created               |
| 400  | Invalid request       |
| 403  | Not admin (forbidden) |
| 404  | Resource not found    |
| 500  | Server error          |

***

## Audit Logging

All Services API actions are logged:

* `service.create`
* `service.update`
* `service.delete`
* `service.duplicate`
* `service_category.create`
* `service_category.update`
* `service_category.delete`
* `services.media.upload`
* `services.media.delete`

***

## Related Resources

<CardGroup cols={2}>
  <Card title="Blog API" icon="newspaper" href="/development/api/blog">
    Manage blog content
  </Card>

  <Card title="Organizations API" icon="building" href="/development/api/organizations">
    Organization management
  </Card>
</CardGroup>
