> ## Documentation Index
> Fetch the complete documentation index at: https://system.muzemus.online/llms.txt
> Use this file to discover all available pages before exploring further.

# API design

> REST conventions, request/response patterns, and endpoint structure

## Base URL and prefix

All API endpoints are prefixed with `/api/v1`. better-auth endpoints live outside this prefix at `/api/auth`.

```
https://<backend-url>/api/v1/employees
https://<backend-url>/api/v1/orders
https://<backend-url>/api/auth/sign-in
```

## REST conventions

| HTTP method | Purpose                                                         | Example                           |
| ----------- | --------------------------------------------------------------- | --------------------------------- |
| `GET`       | Read one or many                                                | `GET /api/v1/employees/:id`       |
| `POST`      | Create, and non-CRUD operations (approve, reject, bulk actions) | `POST /api/v1/orders/:id/approve` |
| `PATCH`     | Partial update                                                  | `PATCH /api/v1/employees/:id`     |
| `DELETE`    | Delete                                                          | `DELETE /api/v1/stores/:id`       |

MUZE does not use `PUT`. Bulk operations use dedicated `POST /<resource>/bulk-deactivate` and `POST /<resource>/bulk-permanent` endpoints that accept `{ ids: string[] }` in a single request.

## Response patterns

Single objects are returned directly. Lists are returned as arrays or wrapped in a `{ data: [], total: number }` envelope depending on the endpoint.

Errors follow a consistent shape (see [Validation](/security/validation) for how errors are produced):

```json theme={null}
{
  "statusCode": 400,
  "message": "These details are already in use. Please review them and try again.",
  "error": "Conflict"
}
```

Validation errors return the messages as an array:

```json theme={null}
{
  "statusCode": 400,
  "message": ["name should not be empty", "email must be an email"],
  "error": "Bad Request"
}
```

## Authentication

All endpoints except `/api/auth/*` require a valid session. The session lives in an HTTP-only cookie managed by better-auth and sent automatically by the browser. No `Authorization: Bearer` header and no JWT tokens are involved. See [Authentication](/security/authentication).

## Authorization decorators

Controllers declare required access with NestJS decorators:

```typescript theme={null}
@Permissions(Permission.EMPLOYEES_MANAGE)
@Post()
async create(@Body() dto: CreateEmployeeDto) { ... }
```

```typescript theme={null}
@RequireScope({ storeIdParam: 'storeId' })
@Get('stores/:storeId/employees')
async getStoreEmployees(@Param('storeId') storeId: string) { ... }
```

`@Permissions(...)` uses AND semantics; the legacy `@Roles(...)` decorator uses OR semantics. See [Authorization](/security/authorization) for the full model.

## File uploads

File uploads use `multipart/form-data` or JSON-wrapped CSV payloads and are validated at the service level. Import endpoints use dedicated routes such as `POST /api/v1/employees/import`.

## Client scoping parameters

Client-scoped list endpoints accept an optional `clientId` query parameter. The rule is **narrow, never widen**: admins may filter to any client, HR can only narrow within their assigned client, and Store Manager requests ignore the parameter entirely. See [Client isolation](/security/client-isolation).

## Rate limiting

All endpoints are rate-limited to 100 requests per 60-second window. There are no per-route overrides and rate-limit headers are not exposed to the client.
