# COMPONENT — desktop-sync-api

> **Code location:** `app/Http/Controllers/Api/Desktop/` · routes in `routes/web.php`
> (`Route::prefix('api/desktop')`, ~line 594).
> **Nav:** Master → [../CLAUDE.md](../../../CLAUDE.md) · Rules → [MASTER_RULES.md](../phase-3-shared/MASTER_RULES.md) ·
> Sync contract → [MASTER_INTEGRATION.md §2](../phase-3-shared/MASTER_INTEGRATION.md#2-desktop-sync-protocol-desktop-sync-api--windows-client)

> Component scope: license validation and offline bidirectional sync with the **Windows desktop
> client** (a separate application that consumes this API).

## 1. Identity & purpose
Controllers in this folder expose the `api/desktop/*` surface. The desktop client is **offline-first**:
it works disconnected, queues local changes, then reconnects to **pull** server state and **push**
its own. This component's job is a correct, idempotent, conflict-aware sync handshake.

**Controllers**
- `APIAuthController` — desktop login / token issuance.
- `LicenseAPIController` — `checkLicense`; blocks unlicensed clients.
- `SyncAPIController` — `appLaunchSync` bootstrap payload on launch.
- `PullController` — server → client reads.
- `PushController` — client → server writes.
- `AttachmentSyncController` — file `upload` / `download`.
- `DesktopController` — desktop-specific helpers.
- ⚠️ `PullController copy.php` and `Backup/ShopApiController copy*.php` are **legacy, not routed** —
  ignore them; never add more.

## 2. Routing & auth
Defined in `routes/web.php` under `Route::prefix('api/desktop')` (~line 594).
- **CSRF-exempt** for `api/desktop/*` — configured in `bootstrap/app.php`. This is intentional
  (non-browser client). Do not broaden the exemption elsewhere.
- Flow: `GET check-license` → `POST authenticate` (issues **Sanctum** token) → token-guarded
  pull/push routes. Keep license + token checks on every data route.

## 3. Contract — pull / push / attachments
See the full endpoint list in
[MASTER_INTEGRATION.md §2](../phase-3-shared/MASTER_INTEGRATION.md#2-desktop-sync-protocol-desktop-sync-api--windows-client).
Summary:
- **Pull:** `user-info`, `employee-info`, `product-updates`, `products` (+ `products/mark-synced`),
  `customers`/`contacts`, `customer-account-updates`, `product-stock`, `server-changes`,
  `invoices`, `invoice-items`, `invoice-return-items`.
- **Push:** products (new/updates), customers (new/edits/account-updates), `stock-entries`/`stocks`,
  invoices (+items, returns, return-items), `goods-receipts` (+items), `contacts`,
  `contact-account-transactions`, `return-invoices`, `expense-categories`.
- **Attachments:** `POST attachments/upload`, `GET attachments/download`.

## 4. Rules specific to this component
1. **Idempotency & conflicts.** Assume duplicate/replayed deliveries. Upserts must be keyed on a
   stable identifier; never blindly `create()` on push. Honor the **mark-synced** acknowledgement
   pattern (`markSynced`, `pullServerChanges`) — do not remove the handshake.
2. **Two-sided changes.** A new syncable field must be added to the matching **Push AND Pull**
   payloads (and the client's expectations) in the same PR, or clients diverge.
3. **JSON everywhere.** Structured JSON responses with explicit status codes; never redirect or
   render HTML (there's no browser).
4. **Return only what changed** where the endpoint supports incremental sync (`server-changes`,
   `*-updates`) — keep payloads small; batch large sets.
5. **Wrap multi-row writes in `DB::transaction()`**; shop stock/ledger effects must stay consistent
   with the shared models.
6. **Preserve backward compatibility** — deployed desktop clients may lag the server. Add fields
   additively; don't rename/remove without a migration plan for clients.

## 5. Testing
Exercise round-trips: authenticate → pull baseline → push local changes → pull `server-changes` →
`mark-synced`. Verify no duplicates on replay and that shop stock/ledger reconcile afterward.
Use `php artisan tinker` to mint tokens and `php artisan pail` to watch requests.
