# API Conventions

## API Versions

API mixes v1 (`/api/{resource}`) and v2 (`/api/v2/{resource}`) — always check endpoint in api_howto.

## Multi-tenancy

**Network Owner IS a customer** — same table, `subreseller` object present indicates Network Owner status. Enables cross-reference WBQL queries between customers and subresellers.

| Role | Without customerId | With customerId |
|------|-------------------|-----------------|
| ADMIN | ⚠️ GLOBAL (all tenants) | Scoped to that tenant |
| Network Owner | All member tenants | Scoped to that tenant (if owned) |
| Regular | Own tenant only | Own tenant only |

**⚠️ ADMIN without customerId = data corruption risk:**
- Queries return mixed cross-tenant results
- Using those IDs can link resources across tenants
- **Always:** Ask user which customer → search by name → use `customerId` in requests

**Network Owner:** Without customerId operates on all member tenants. Provide customerId to scope to specific tenant.

**Batch operations** (e.g., `/api/v2/device/{command}?search=...`): Require customerId, cannot run cross-tenant.

**Explicit global:** `customerId=-1` when cross-tenant query is intended.

No batch create endpoint — create one resource per API call. (MCP tools orchestrate multiple calls.)

## Response Behavior

- **Null values omitted** — don't expect all schema fields
- **PUT is usually a partial update** — check the endpoint; webhook action updates also overwrite omitted configuration fields
- **Exception: `data` field** — full replacement (use `manage_playlist`/`manage_datasource`)
- **Counting**: `size=1&select=id` → `totalElements` in response

## IDs

Format: UUID (32-char, no dashes) or integer. Server-generated on create.

## Search vs Select

- `search` (WBQL): ANY field including internal (e.g., `teamAssignments`)
- `select`: Only DTO fields in OpenAPI schema

## Flat API

Use flat collections with relationship filters in `search`. Use nested action URLs only where documented.

## Folders

Three types: `deviceGroup`, `fileFolder`, `contentGroup`. Root folders: auto-created per customer, no `name` field — query first to get ID.

**Summary view** (v2, folders+items paginated):
```
/api/v2/file/view/summary?folderSearch=parentId={folderId}
/api/v2/device/view/summary?deviceGroupSearch=parentId={folderId}
/api/v2/deviceContent/view/summary?contentGroupSearch=parentId={folderId}
```
Params: `{entity}Search`, `{folder}Search` for WBQL; `select`, `{folder}Select` for projection.

## Teams

Control resource visibility.

**Create with team**: `POST /api/{resource}?teamIds={id}:{readOnly},{id2}:{readOnly2},...`

**Modify** (OWNER role required): `POST /api/{resource}/updateTeamAssignments?{idParam}={id}`
Body: `{"assignToTeams": [{"teamId": "x", "readOnly": false}], "removeFromTeamIds": ["y"]}`

## Tags

Resources: Device, DeviceGroup, Campaign, Message, DeviceContent, File.

**Bulk ops**: `POST /api/v2/{resource}/tags/{add|set|remove}?search=...` body: `["tag1"]`

Note: Devices use bulk endpoint only. Other entities support `{"tags":[...]}` in PUT.

## Protected Operations

User CRUD, password change, customer delete, config changes may require CAPTCHA (`g-recaptcha-response`) and/or 2FA (`x-totp`) headers — depends on server and user settings.

## Scheduling (affectedDateRanges)

Used in playlists, datasources, campaigns/messages.

```
{"intervals": [{
  "affectedDays": {"monday":true,...,"sunday":false},  // 7 booleans, omit=all true
  "affectedHours": {
    "allDay": false,        // true=24h, omit=true
    "from": "09:00",        // daily start (HH:mm), omit=00:00
    "end": "17:00"          // daily end (HH:mm), omit=23:59
  },
  "from": "2024-01-01",     // start date (yyyy-MM-dd), omit=no limit
  "fromTime": "10:00",      // time on start date (HH:mm), omit=start of day
  "to": "2024-12-31",       // end date, omit=no limit
  "toTime": "18:00",        // time on end date, omit=end of day
  "isExcluded": false       // false=active during, true=blackout (required)
}]}
```

**Multiple intervals:** Evaluated with OR logic. Exclude (`isExcluded: true`) takes precedence over include.

**Overnight schedules:** When `from` > `end` (e.g., 22:00→06:00), the interval spans midnight. Additional flags (`allowPartialStartingSegment`, `allowPartialEndingSegment`, `allowDayOfWeekOverflow`) control edge cases — rarely needed.
