> Documentation index: https://stilldeeppos.com/llms.txt

# StillDeep gRPC API reference

**Generated:** 2026-10-17T09:21:47-04:00
**Source:** `src/public/StillDeep.Intelligence.Contracts/protos/` (20 files)
**Endpoint:** `localhost:50051` (HTTP/2, package `stilldeep.v1`)

Generated by `scripts/generate-grpc-docs.ps1` from the .proto files: edit the .proto files, then run the script again.

## Call metadata

- `x-shift-id`: id of the open shift, from `ShiftService.OpenShift` or `ShiftService.GetCurrentShift`. Send it on every call after sign-in. Required by refunds, discounts, payment refusals, reservation cancellation, pause / resume / close shift, inventory writes and receipts: missing, `UNAUTHENTICATED`; not a UUID, `INVALID_ARGUMENT`; not the open or paused shift of this register, or permission missing, `PERMISSION_DENIED`. `SalesService.CreateCart` needs an open shift on the register (`no_open_shift`, `shift_paused`).
- Refusals: `google.rpc.Status` with a stable code; show a message from the code, never retry blindly.

## Contents

- [ageverification](#ageverification)
- [appointment](#appointment)
- [card](#card)
- [catalog](#catalog)
- [common](#common)
- [config](#config)
- [device](#device)
- [events](#events)
- [extensions](#extensions)
- [inventory](#inventory)
- [language](#language)
- [member](#member)
- [receipt](#receipt)
- [rental](#rental)
- [report](#report)
- [sales](#sales)
- [scheduling](#scheduling)
- [shift](#shift)
- [sync](#sync)
- [user](#user)

## ageverification

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/ageverification.proto`

> AgeVerificationService: legal age verification for restricted products
> (alcohol, tobacco, cannabis in applicable jurisdictions).
> 
> The service relies on:
>   * Member.custom_fields (e.g. "date_of_birth") for an identified customer.
>   * Manual DOB entry by the cashier for an anonymous customer.
>   * External plugin (driver's license ID scanner) that injects a DOB
>     via SourceKind.PLUGIN_SCANNER.
> 
> The service does NOT store the DOB for anonymous customers, only the
> result (passed/failed) with an anonymous hash for audit.
> 

### Services

#### `AgeVerificationService`

AgeVerificationService - legal age verification. Error codes: INVALID_ARGUMENT (no DOB source set or invalid format), FAILED_PRECONDITION (verification passed=false), NOT_FOUND (product_id), PERMISSION_DENIED (invalid manager_auth_token). The server logs every verification in system_audit_log with an anonymized hash for legal compliance.

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `VerifyAge` | `VerifyAgeRequest` | `VerifyAgeResponse` | - |
| `GetMinAgeForProduct` | `GetMinAgeForProductRequest` | `GetMinAgeForProductResponse` | - |

### Messages

- `AgeVerificationResult`
- `GetMinAgeForProductRequest`
- `GetMinAgeForProductResponse`
- `VerifyAgeRequest`
- `VerifyAgeResponse`

### Enums

- `VerificationSourceKind`

---

## appointment

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/appointment.proto`

> AppointmentService: service appointments (grooming, nail trimming, care).
> 
> An appointment occupies a qualified employee for the service duration plus its buffer.
> Availability crosses scheduled shifts (user_schedule), skill groups and the
> appointments already booked. Payment goes through SalesService.AddItem(service_appointment_id).
> 

### Services

#### `AppointmentService`

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `ListAppointments` | `ListAppointmentsRequest` | `ListAppointmentsResponse` | - |
| `GetAvailability` | `GetAvailabilityRequest` | `GetAvailabilityResponse` | - |
| `CreateAppointment` | `CreateAppointmentRequest` | `AppointmentResponse` | - |
| `UpdateAppointment` | `UpdateAppointmentRequest` | `AppointmentResponse` | - |

### Messages

- `Appointment`
- `AppointmentResponse`
- `AvailableSlot`
- `CreateAppointmentRequest`
- `GetAvailabilityRequest`
- `GetAvailabilityResponse`
- `ListAppointmentsRequest`
- `ListAppointmentsResponse`
- `StaffDay`
- `TimeRange`
- `UpdateAppointmentRequest`

### Enums

- `AppointmentStatus`

---

## card

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/card.proto`

> CardService - physical / virtual cards: gift card, loyalty card,
> discount card, employee card, membership card.
> 
> A card can be attached to 0 or 1 member. Balance / operations /
> passes are handled by the Cards plugin. This service only exposes
> read / lookup for POSClient and SalesService.AttachCard.
> 

### Services

#### `CardService`

**Not served yet:** defined in the contract, the register answers `UNIMPLEMENTED`.

CardService - read access to physical / virtual cards. Called by: POSClient (card scan / entry), SalesService.AttachCard internally, MemberService (list a member's cards). No mutation here - issuance, top-up, blocking go through ServerAdmin + the Cards plugin. Error codes: NOT_FOUND, INVALID_ARGUMENT, FAILED_PRECONDITION (card LOST/REVOKED/EXPIRED depending on usage context).

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `GetCard` | `GetCardRequest` | `Card` | - |
| `ListCards` | `ListCardsRequest` | `ListCardsResponse` | - |

### Messages

- `Card`
- `GetCardRequest`
- `ListCardsRequest`
- `ListCardsResponse`

### Enums

- `CardKind`
- `CardStatus`

---

## catalog

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/catalog.proto`

> CatalogService: products, categories, taxes.
> 
> Read-only from POSClient. Catalog changes go through
> ServerAdmin, not this service. Data is synchronized
> from ServerDB to the post's local SQLite (bootstrap data + delta sync).
> 

### Services

#### `CatalogService`

----- Service ----- CatalogService - read-only catalog of products / categories / taxes. Called by: POSClient (product search, autocomplete, barcode scan), and indirectly by SalesService.AddItem (internal resolution). Never writes - changes go through ServerAdmin which writes to ServerDB (Postgres) and sync propagates to the posts via Communication.Lib (bootstrap + delta). Typical errors: NOT_FOUND if product / category / tax does not exist; INVALID_ARGUMENT if the request is empty or parameters are inconsistent.

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `GetProductBySku` | `GetProductRequest` | `Product` | - |
| `GetProductByBarcode` | `GetProductByBarcodeRequest` | `Product` | - |
| `GetItem` | `GetItemRequest` | `Product` | - |
| `GetItems` | `GetItemsRequest` | `GetItemsResponse` | - |
| `SearchProducts` | `SearchProductsRequest` | `SearchProductsResponse` | - |
| `Autocomplete` | `AutocompleteRequest` | `AutocompleteResponse` | - |
| `ListCategories` | `ListCategoriesRequest` | `ListCategoriesResponse` | - |
| `GetActiveTaxes` | `GetTaxesRequest` | `GetTaxesResponse` | - |
| `ListPriceTierLists` | `ListPriceTierListsRequest` | `ListPriceTierListsResponse` | - |
| `ListPriceTierEntries` | `ListPriceTierEntriesRequest` | `ListPriceTierEntriesResponse` | - |
| `GetMedia` | `GetMediaRequest` | `MediaResponse` | - |

### Messages

- `AutocompleteHit`
- `AutocompleteRequest`
- `AutocompleteResponse`
- `Category`
- `GetItemRequest`
- `GetItemsRequest`
- `GetItemsResponse`
- `GetMediaRequest`
- `GetProductByBarcodeRequest`
- `GetProductRequest`
- `GetTaxesRequest`
- `GetTaxesResponse`
- `ListCategoriesRequest`
- `ListCategoriesResponse`
- `ListPriceTierEntriesRequest`
- `ListPriceTierEntriesResponse`
- `ListPriceTierListsRequest`
- `ListPriceTierListsResponse`
- `MediaResponse`
- `PriceOption`
- `PriceTierEntry`
- `PriceTierEntryWindow`
- `PriceTierList`
- `Product`
- `ProductCustomFieldValue`
- `SearchProductsRequest`
- `SearchProductsResponse`
- `Tax`

### Enums

- `PricingKind`

---

## common

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/common.proto`

> Fundamental types shared by all StillDeep services.
> 
> These messages are imported by all the other .proto files. They define the
> numeric conventions (R14: no double / float, decimal everywhere) and the
> envelope building blocks: Money, Identifier, Pagination, ErrorDetail,
> PluginExtensions, etc.
> 
> R14: amounts in int64 cents (never double/float).
> Versioning: namespace stilldeep.v1.* - v2 will be a separate namespace.
> Convention: snake_case for fields, PascalCase for messages.
> 

### Messages

- `ActionResponse`
- `CustomFieldValue`
- `ErrorDetail`
- `Identifier`
- `Money`
- `PageInfo`
- `Pagination`
- `Percentage`
- `PluginExtensions`
- `Quantity`

---

## config

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/config.proto`

> ConfigService: runtime configuration of the post + diagnostics.
> 
> Lets the cashier / manager:
>   * read the current config (assigned printer, idle_timeout, branding).
>   * change user preferences (persisted language, theme).
>   * fetch recent logs for debugging (limited to 1h).
>   * trigger a clean restart of the POSClient (after a plugin update).
> 
> Changing the business-wide config (e.g. tax_rates, business_hours)
> goes through ServerAdmin, not this service.
> 

### Services

#### `ConfigService`

ConfigService - runtime configuration + diagnostics. Error codes: NOT_FOUND, PERMISSION_DENIED, FAILED_PRECONDITION (restart with open carts).

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `GetPostConfig` | `GetPostConfigRequest` | `PostConfig` | - |
| `GetUserPreferences` | `GetUserPreferencesRequest` | `UserPreferences` | - |
| `UpdateUserPreferences` | `UpdateUserPreferencesRequest` | `UserPreferences` | - |
| `GetClientLogs` | `GetClientLogsRequest` | `GetClientLogsResponse` | - |
| `RestartClient` | `RestartClientRequest` | `RestartClientResponse` | - |

### Messages

- `GetClientLogsRequest`
- `GetClientLogsResponse`
- `GetPostConfigRequest`
- `GetUserPreferencesRequest`
- `LogEntry`
- `PaymentMethodInfo`
- `PostConfig`
- `RestartClientRequest`
- `RestartClientResponse`
- `StoreInfo`
- `UpdateUserPreferencesRequest`
- `UserHotkey`
- `UserPreferences`

### Enums

- `LogLevel`

---

## device

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/device.proto`

> DeviceService: state / control of POS peripherals.
> 
> Concrete implementations via driver plugins - Epson ESC/POS,
> Moneris P2PE, etc. This service exposes a uniform control on the UI side.
> 

### Services

#### `DeviceService`

**Not served yet:** defined in the contract, the register answers `UNIMPLEMENTED`.

----- Service ----- DeviceService - control / reading of POS peripherals. Called by: POSClient (status for UI iconography, manual drawer opening, peripheral tests during configuration), SalesService (auto print at Finalize via ReceiptService -> DeviceService). Implementations: routed to the driver plugins via the IDeviceChain chain in Intelligence.Lib. Each DeviceType has its own driver. Error codes: NOT_FOUND (device_id), FAILED_PRECONDITION (device not READY), UNAVAILABLE (driver plugin missing), PERMISSION_DENIED. Dependencies: EventService (publishes DeviceStatusChangedEvent, DrawerOpenedEvent, ReceiptPrintedEvent).

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `ListDevices` | `ListDevicesRequest` | `ListDevicesResponse` | - |
| `GetDeviceStatus` | `GetDeviceStatusRequest` | `GetDeviceStatusResponse` | - |
| `OpenCashDrawer` | `OpenCashDrawerRequest` | `ActionResponse` | - |
| `PrintReceipt` | `PrintReceiptRequest` | `ActionResponse` | - |
| `TestDevice` | `TestDeviceRequest` | `TestResponse` | - |

### Messages

- `Device`
- `GetDeviceStatusRequest`
- `GetDeviceStatusResponse`
- `ListDevicesRequest`
- `ListDevicesResponse`
- `OpenCashDrawerRequest`
- `PrintReceiptRequest`
- `TestDeviceRequest`
- `TestResponse`

### Enums

- `DeviceStatus`
- `DeviceType`

---

## events

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/events.proto`

> EventService: real-time streaming for POSClient.
> 
> Lets several simultaneous UIs (register, customer display, kitchen
> display, second screen) see the same cart live. The UI subscribes
> via Subscribe -> Event stream; the server pushes every relevant mutation.
> 
> Note: the plugins' internal bus (in-process AssemblyLoadContext) is a
> separate C# abstraction (IPluginEventBus) - see
> StillDeep.Intelligence.Plugins.Abstractions. This gRPC service only serves
> remote or multi-post UI CLIENTS.
> 

### Services

#### `EventService`

**Not served yet:** defined in the contract, the register answers `UNIMPLEMENTED`.

----- Service ----- EventService - real-time stream for multi-UI synchronization. Called by: main POSClient (register), customer display, kitchen display, admin second screen, out-of-process plugins. Usage patterns:   * Customer display: Subscribe(kinds=[CART, PAYMENT], cart_id_filter=<active>)     -> shows items + total live.   * Kitchen display: Subscribe(kinds=[CART], post_id_filter=<restaurant>) ->     filters client-side the Carts with product_kind="restaurant_item".   * External scanner: Publish(ScanReceivedEvent) - the system routes to the     consumer that had emitted ScanRequestedEvent. Error codes: INVALID_ARGUMENT (empty subscriber_id), UNAUTHENTICATED.

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `Subscribe` | `SubscribeRequest` | `Event` | server |
| `Publish` | `PublishEventRequest` | `ActionResponse` | - |
| `Heartbeat` | `HeartbeatRequest` | `HeartbeatResponse` | bidi |

### Messages

- `BalanceUpdatedEvent`
- `CartCardAttachedEvent`
- `CartItemAddedEvent`
- `CartItemRemovedEvent`
- `CartLocationAttachedEvent`
- `CartMemberAttachedEvent`
- `CartUpdatedEvent`
- `DeviceStatusChangedEvent`
- `DrawerOpenedEvent`
- `Event`
- `HeartbeatRequest`
- `HeartbeatResponse`
- `PaymentCompletedEvent`
- `PaymentInitiatedEvent`
- `PaymentMethodChangedEvent`
- `PublishEventRequest`
- `ReceiptPrintedEvent`
- `RefundIssuedEvent`
- `ScanReceivedEvent`
- `ScanRequestedEvent`
- `ShiftClosedEvent`
- `ShiftOpenedEvent`
- `ShiftPausedEvent`
- `ShiftResumedEvent`
- `SubscribeRequest`
- `TransactionFinalizedEvent`

### Enums

- `EventKindFilter`

---

## extensions

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/extensions.proto`

> Extension documents and sections of the generic screens.
> 
> A document is an extension record without a table (a pet, a breed, a product's
> setting): values as a JSON object validated by the schema of its type. A post reads all
> types; it writes only what the type opens to it (post_access) and, if the type requires it,
> with the permission of the employee of the open shift (post_permission).
> 

### Services

#### `DocumentService`

DocumentService - extension documents at a post. Writes run as the employee of the post's open shift. Errors: NOT_FOUND, PERMISSION_DENIED (type closed to posts, permission missing), FAILED_PRECONDITION (invalid values, no open shift, still referenced), with the reason code in the x-failure-code trailer.

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `GetDocument` | `GetDocumentRequest` | `DocumentResponse` | - |
| `ListDocuments` | `ListDocumentsRequest` | `ListDocumentsResponse` | - |
| `SaveDocument` | `SaveDocumentRequest` | `DocumentResponse` | - |
| `ChangeDocument` | `ChangeDocumentRequest` | `DocumentResponse` | - |

#### `UiExtensionService`

UiExtensionService - sections declared by the extensions loaded on this post.

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `ListUiExtensions` | `ListUiExtensionsRequest` | `ListUiExtensionsResponse` | - |

### Messages

- `ChangeDocumentRequest`
- `Document`
- `DocumentResponse`
- `GetDocumentRequest`
- `ListDocumentsRequest`
- `ListDocumentsResponse`
- `ListUiExtensionsRequest`
- `ListUiExtensionsResponse`
- `SaveDocumentRequest`
- `UiSection`

### Enums

- `DocumentAction`
- `DocumentPostAccess`
- `UiSectionKind`

---

## inventory

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/inventory.proto`

> InventoryService: receiving, transfers, adjustments, counts,
> movement history. Available stock is computed by summing
> product_inventory_movement (+/-). No denormalized counter - StillDeep
> convention.
> 
> Reading current stock: Product.stock_quantity (CatalogService), computed
> by the server on demand. For movement detail, GetStockHistory.
> 
> Called by: ServerAdmin (receiving, transfer, adjustment), POSClient
> (in-store count, manual restock), plugins (automatic consumption via
> product_component recipes).
> 

### Services

#### `InventoryService`

InventoryService - inventory management (append-only ledger). Error codes: INVALID_ARGUMENT (negative quantity for Receive, invalid kind), NOT_FOUND, FAILED_PRECONDITION (insufficient stock for transfer if oversell_policy=block), PERMISSION_DENIED. Dependencies: EventService publishes StockMovementRecorded (to add in events.proto V2 if live needed).

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `ReceiveStock` | `ReceiveStockRequest` | `ReceiveStockResponse` | - |
| `TransferStock` | `TransferStockRequest` | `TransferStockResponse` | - |
| `AdjustStock` | `AdjustStockRequest` | `AdjustStockResponse` | - |
| `CountInventory` | `CountInventoryRequest` | `CountInventoryResponse` | - |
| `GetStockLevel` | `GetStockLevelRequest` | `StockLevel` | - |
| `ListStockLevels` | `ListStockLevelsRequest` | `ListStockLevelsResponse` | - |
| `GetStockHistory` | `GetStockHistoryRequest` | `GetStockHistoryResponse` | - |

### Messages

- `AdjustStockRequest`
- `AdjustStockResponse`
- `CountInventoryRequest`
- `CountInventoryResponse`
- `GetStockHistoryRequest`
- `GetStockHistoryResponse`
- `GetStockLevelRequest`
- `ListStockLevelsRequest`
- `ListStockLevelsResponse`
- `ReceiveStockRequest`
- `ReceiveStockResponse`
- `StockLevel`
- `StockMovement`
- `TransferStockRequest`
- `TransferStockResponse`

### Enums

- `InventoryMovementKind`

---

## language

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/language.proto`

> LanguageService: display language selection and resolution.
> 
> Lets the cashier / kiosk / customer:
>   * List active languages (system_language is_active=true).
>   * Resolve the effective language for a context (post, employee, customer).
>   * Force a language on a cart_id (volatile override).
>   * Clear the volatile override.
> 
> The override is held in memory in Intelligence.Service (lost on restart),
> by design. To change the language of a post or employee durably, use
> Server.Admin.
> 

### Services

#### `LanguageService`

----- Service ----- Error codes:   * NOT_FOUND: requested locale missing or inactive in system_language.   * INVALID_ARGUMENT: malformed cart_id.

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `ListLanguages` | `ListLanguagesRequest` | `ListLanguagesResponse` | - |
| `GetEffectiveLocale` | `GetEffectiveLocaleRequest` | `GetEffectiveLocaleResponse` | - |
| `SetCartLocaleOverride` | `SetCartLocaleOverrideRequest` | `SetCartLocaleOverrideResponse` | - |
| `ClearCartLocaleOverride` | `ClearCartLocaleOverrideRequest` | `ClearCartLocaleOverrideResponse` | - |

### Messages

- `ClearCartLocaleOverrideRequest`
- `ClearCartLocaleOverrideResponse`
- `GetEffectiveLocaleRequest`
- `GetEffectiveLocaleResponse`
- `Language`
- `ListLanguagesRequest`
- `ListLanguagesResponse`
- `SetCartLocaleOverrideRequest`
- `SetCartLocaleOverrideResponse`

---

## member

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/member.proto`

> MemberService: search / read / create / update
> of members (customers enrolled in the program).
> 
> Note: wallets / passes / loyalty programs live in the Cards plugin.
> GetWalletBalance returns plugin_loaded=false when it is absent. To
> scan / attach a card to the cart, use SalesService.AttachCard.
> 

### Services

#### `MemberService`

----- Service ----- MemberService - management of members (enrolled customers). Called by: POSClient (search / quick creation / cart association), ServerAdmin (full CRUD). Error codes: NOT_FOUND, INVALID_ARGUMENT, ALREADY_EXISTS, PERMISSION_DENIED (status BARRED without manager role), FAILED_PRECONDITION. Dependencies: Cards plugin for GetWalletBalance.

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `GetMemberByNumber` | `GetMemberRequest` | `Member` | - |
| `SearchMembers` | `SearchMembersRequest` | `SearchMembersResponse` | - |
| `CreateMember` | `CreateMemberRequest` | `Member` | - |
| `UpdateMember` | `UpdateMemberRequest` | `Member` | - |
| `ValidateMembership` | `ValidateMembershipRequest` | `ValidateMembershipResponse` | - |
| `GetWalletBalance` | `GetWalletBalanceRequest` | `GetWalletBalanceResponse` | - |
| `AssignCard` | `AssignCardRequest` | `MemberCard` | - |

### Messages

- `AssignCardRequest`
- `CommunicationPreferences`
- `CreateMemberRequest`
- `GetMemberRequest`
- `GetWalletBalanceRequest`
- `GetWalletBalanceResponse`
- `Member`
- `MemberAddress`
- `MemberCard`
- `Membership`
- `MemberStats`
- `SearchMembersRequest`
- `SearchMembersResponse`
- `UpdateMemberRequest`
- `ValidateMembershipRequest`
- `ValidateMembershipResponse`
- `WalletBalance`

### Enums

- `CommunicationChannel`
- `MemberStatus`

---

## receipt

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/receipt.proto`

> ReceiptService: deliver / reprint / resend a receipt.
> 
> All rendering (PDF / ESC-POS / HTML email) is done on the POSIntelligenceSvc side.
> POSClient only asks: for transaction X, format Y, channel Z.
> Intelligence takes care of:
>   - rendering the content (i18n, detailed taxes, branding)
>   - sending to the peripheral (printer via DeviceService)
>   - posting to an email/SMS queue for deferred channels
> 
> This separation lets open-source POSClients (Web, Mobile, Kiosk
> fork) avoid reimplementing templating + printer drivers.
> 

### Services

#### `ReceiptService`

**Not served yet:** defined in the contract, the register answers `UNIMPLEMENTED`.

ReceiptService - receipt emission and search. Called by: POSClient (after Finalize if not auto-emitted, reprint screen, after-the-fact email), Kiosk (gift receipt). Implementation: routed to the internal render pipeline (templates) + DeviceService (printer) + SMTP/SMS plugin for deferred channels. Error codes: NOT_FOUND (transaction), INVALID_ARGUMENT (missing destination for EMAIL/SMS), FAILED_PRECONDITION (printer not READY), UNAVAILABLE (SMTP/SMS plugin missing). Dependencies: Transaction (SalesService), Member (CommunicationPreferences), Device (printing), EventService (publishes ReceiptPrintedEvent).

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `EmitReceipt` | `EmitReceiptRequest` | `ReceiptRenderResult` | - |
| `FindReceipts` | `FindReceiptsRequest` | `FindReceiptsResponse` | - |

### Messages

- `EmitReceiptRequest`
- `FindReceiptsRequest`
- `FindReceiptsResponse`
- `ReceiptRenderResult`
- `ReceiptSummary`

### Enums

- `ReceiptChannel`
- `ReceiptFormat`

---

## rental

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/rental.proto`

> RentalService: item rental contracts (checkout, due date, return, deposit).
> 
> A rental sold at the counter opens at finalization (SalesService.AddItem with
> rental_due_at). CreateRental opens a contract outside a sale (future booking, loan).
> 

### Services

#### `RentalService`

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `ListRentals` | `ListRentalsRequest` | `ListRentalsResponse` | - |
| `CreateRental` | `CreateRentalRequest` | `RentalResponse` | - |
| `ReturnRental` | `ReturnRentalRequest` | `RentalResponse` | - |

### Messages

- `CreateRentalRequest`
- `ListRentalsRequest`
- `ListRentalsResponse`
- `Rental`
- `RentalResponse`
- `ReturnRentalRequest`

### Enums

- `RentalScope`

---

## report

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/report.proto`

> ReportService: operational in-POS reports.
> 
> For advanced analytical reports (BI, complex dashboards), use
> ServerReport, which reads the database directly. This service only exposes
> the real-time data the cashier / manager needs during a shift.
> 
> No mutation - purely read-only. Permissions: `report.shift_summary` /
> `report.top_products` / `report.cashier_productivity` (manager).
> 

### Services

#### `ReportService`

ReportService - real-time reports for the cashier / manager. Error codes: NOT_FOUND, PERMISSION_DENIED.

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `GetShiftSummary` | `GetShiftSummaryRequest` | `ShiftSummary` | - |
| `GetTopProducts` | `GetTopProductsRequest` | `GetTopProductsResponse` | - |
| `GetSalesByMethod` | `GetSalesByMethodRequest` | `GetSalesByMethodResponse` | - |
| `GetTipSummary` | `GetTipSummaryRequest` | `TipSummary` | - |
| `GetCashierProductivity` | `GetCashierProductivityRequest` | `GetCashierProductivityResponse` | - |

### Messages

- `CashierProductivityEntry`
- `CashierTipEntry`
- `GetCashierProductivityRequest`
- `GetCashierProductivityResponse`
- `GetSalesByMethodRequest`
- `GetSalesByMethodResponse`
- `GetShiftSummaryRequest`
- `GetTipSummaryRequest`
- `GetTopProductsRequest`
- `GetTopProductsResponse`
- `PaymentMethodSummary`
- `ProductSalesEntry`
- `ShiftSummary`
- `TipSummary`

---

## sales

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/sales.proto`

> SalesService: cart, payment, finalization.
> 
> Main service exposed by POSIntelligenceSvc, consumed by all
> POSClients (Windows Reference, web, kiosk, mobile). It is the core of the
> sales flow: cart creation -> add / remove lines -> discounts ->
> payment -> finalization -> receipt.
> 
> Idempotency: every mutation exposes an idempotency_key. Re-sending with
> the same key => same response, no side effect. The server keeps keys
> for 24h in the local SQLite.
> 

### Services

#### `SalesService`

----- Service ----- SalesService - main POS service. All cart, payment and finalization operations go through here. Called by: POSClient (Windows Reference, web, kiosk, mobile) over gRPC on localhost (HTTP/2 pipe). No call outside the customer's LAN. Common preconditions: an OPEN shift must exist on the post for the cashier_user_id. Otherwise PERMISSION_DENIED. Per-RPC permissions are checked via interceptor (see ShiftService.CheckPermission for UI pre-check). Main error codes:   INVALID_ARGUMENT     - inconsistent parameters (e.g. product_id and sku both empty).   NOT_FOUND            - cart / product / member / transaction does not exist.   FAILED_PRECONDITION  - incompatible cart status (e.g. Finalize on a VOIDED cart).   PERMISSION_DENIED    - no open shift, or insufficient role for discount/refund.   UNAUTHENTICATED      - manager_auth_token invalid or expired.   ALREADY_EXISTS       - re-send detected by idempotency_key, idempotent return. Usage patterns (excerpts):   * Standard cash sale: CreateCart -> AddItem* -> ProcessPayment(cash) ->     FinalizeTransaction.   * Split payment: CreateCart -> AddItem* -> ProcessPayment(card_p2pe partial)     -> ProcessPayment(cash for rest) -> FinalizeTransaction.   * Card refusal with fallback: ProcessPayment(card) -> refusal -> ResolvePaymentRefusal     (FALLBACK_TO_CASH) -> ProcessPayment(cash) -> FinalizeTransaction.   * Pause / resume: SaveCart -> [customer busy with another cart] -> GetSavedCart -> ...   * Reservation : AddItem* -> AssociateMember (or a rented location) -> ProcessPayment (deposit, optional)     -> ReserveCart ; later ResumeReservation -> AddItem (fees) -> ProcessPayment -> ReserveCart or     FinalizeTransaction ; or PreviewReservationCancellation -> CancelReservation(expected_refund). Dependencies: CatalogService (product resolution in AddItem), MemberService (AssociateMember), CardService (AttachCard), DeviceService (printing via FinalizeTransaction), EventService (cart event publication).

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `CreateCart` | `CreateCartRequest` | `CartResponse` | - |
| `GetCart` | `GetCartRequest` | `CartResponse` | - |
| `AddItem` | `AddItemRequest` | `CartResponse` | - |
| `RemoveLine` | `RemoveLineRequest` | `CartResponse` | - |
| `UpdateQuantity` | `UpdateQuantityRequest` | `CartResponse` | - |
| `UpdateLineNote` | `UpdateLineNoteRequest` | `CartResponse` | - |
| `ListDiscounts` | `ListDiscountsRequest` | `ListDiscountsResponse` | - |
| `ApplyDiscount` | `ApplyDiscountRequest` | `CartResponse` | - |
| `RemoveDiscount` | `RemoveDiscountRequest` | `CartResponse` | - |
| `AssociateMember` | `AssociateMemberRequest` | `CartResponse` | - |
| `AttachCard` | `AttachCardRequest` | `CartResponse` | - |
| `DetachCard` | `DetachCardRequest` | `CartResponse` | - |
| `AttachLocation` | `AttachLocationRequest` | `CartResponse` | - |
| `DetachLocation` | `DetachLocationRequest` | `CartResponse` | - |
| `BeginPayment` | `BeginPaymentRequest` | `CartResponse` | - |
| `ResolveInteraction` | `ResolveInteractionRequest` | `CartResponse` | - |
| `SetLineMember` | `SetLineMemberRequest` | `CartResponse` | - |
| `SetSubject` | `SetSubjectRequest` | `CartResponse` | - |
| `SetExtensions` | `SetExtensionsRequest` | `CartResponse` | - |
| `SelectPaymentMethod` | `SelectPaymentMethodRequest` | `CartResponse` | - |
| `CancelPayment` | `GetCartRequest` | `CartResponse` | - |
| `ProcessPayment` | `ProcessPaymentRequest` | `PaymentResponse` | - |
| `ResolvePaymentRefusal` | `ResolvePaymentRefusalRequest` | `PaymentResponse` | - |
| `ClearPayments` | `GetCartRequest` | `CartResponse` | - |
| `FinalizeTransaction` | `FinalizeRequest` | `TransactionResponse` | - |
| `CancelCart` | `GetCartRequest` | `ActionResponse` | - |
| `SendToKitchen` | `SendToKitchenRequest` | `ActionResponse` | - |
| `SaveCart` | `SaveCartRequest` | `SaveCartResponse` | - |
| `ListSavedCarts` | `ListSavedCartsRequest` | `ListSavedCartsResponse` | - |
| `GetSavedCart` | `GetSavedCartRequest` | `CartResponse` | - |
| `DeleteSavedCart` | `DeleteSavedCartRequest` | `ActionResponse` | - |
| `SearchTransactions` | `SearchTransactionsRequest` | `SearchTransactionsResponse` | - |
| `GetTransaction` | `GetTransactionRequest` | `TransactionResponse` | - |
| `RefundTransaction` | `RefundTransactionRequest` | `TransactionResponse` | - |
| `ReserveCart` | `ReserveCartRequest` | `ReserveCartResponse` | - |
| `ListReservations` | `ListReservationsRequest` | `ListReservationsResponse` | - |
| `ResumeReservation` | `ResumeReservationRequest` | `CartResponse` | - |
| `ReleaseReservation` | `ReleaseReservationRequest` | `ActionResponse` | - |
| `PreviewReservationCancellation` | `CancelReservationRequest` | `ReservationCancellationResponse` | - |
| `CancelReservation` | `CancelReservationRequest` | `ReservationCancellationResponse` | - |

### Messages

- `AddItemRequest`
- `AppliedDiscount`
- `AppliedTax`
- `ApplyDiscountRequest`
- `AssociateMemberRequest`
- `AttachCardRequest`
- `AttachLocationRequest`
- `BeginPaymentRequest`
- `CancelReservationRequest`
- `Cart`
- `CartLine`
- `CartReservation`
- `CartResponse`
- `CreateCartRequest`
- `DeleteSavedCartRequest`
- `DetachCardRequest`
- `DetachLocationRequest`
- `DiscountCatalogEntry`
- `FinalizeRequest`
- `GetCartRequest`
- `GetSavedCartRequest`
- `GetTransactionRequest`
- `Interaction`
- `InteractionChoice`
- `ListDiscountsRequest`
- `ListDiscountsResponse`
- `ListReservationsRequest`
- `ListReservationsResponse`
- `ListSavedCartsRequest`
- `ListSavedCartsResponse`
- `Notice`
- `Payment`
- `PaymentRefusalGuide`
- `PaymentResponse`
- `ProcessPaymentRequest`
- `RefundLineSelection`
- `RefundSeat`
- `RefundTransactionRequest`
- `ReleaseReservationRequest`
- `RemoveDiscountRequest`
- `RemoveLineRequest`
- `Reservation`
- `ReservationCancellationResponse`
- `ReservationKeptLine`
- `ReservationRefund`
- `ReserveCartRequest`
- `ReserveCartResponse`
- `ResolveInteractionRequest`
- `ResolvePaymentRefusalRequest`
- `ResumeReservationRequest`
- `SaveCartRequest`
- `SaveCartResponse`
- `SavedCart`
- `SearchTransactionsRequest`
- `SearchTransactionsResponse`
- `SelectPaymentMethodRequest`
- `SendToKitchenRequest`
- `SetExtensionsRequest`
- `SetLineMemberRequest`
- `SetSubjectRequest`
- `Transaction`
- `TransactionLine`
- `TransactionResponse`
- `TransactionSummary`
- `UpdateLineNoteRequest`
- `UpdateQuantityRequest`

### Enums

- `CartStatus`
- `DiscountKind`
- `DiscountSource`
- `DiscountTiming`
- `InteractionKind`
- `InteractionTargetKind`
- `NoticeLevel`
- `PaymentStatus`
- `RefusalAction`

---

## scheduling

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/scheduling.proto`

> SchedulingService: scheduled ticketing (cinema, theatre, conference).
> 
> ScheduledEvent ("Dune Part 3") -> EventSchedule (showing "Friday 7 pm, hall 4")
>                                -> EventTicketType ("Adult", "Student", "VIP").
> A seat is a seat location (product_location.location_kind = 'seat'); seat_id is its product id.
> The client draws the hall itself.
> 

### Services

#### `SchedulingService`

----- Service ----- SchedulingService - scheduled ticketing: events, showings, ticket types, seats. Seats and capacities are the server's. HoldSeat holds a seat on the server for an open SalesService cart until expires_at (renewed while the cart lives), ReleaseSeat or a cancelled cart gives it back. Other posts' changes are not pushed: read GetSeatMap / GetSchedules again. SalesService.AddItem carries no showing nor seats yet: a held seat is not sold here. Errors: NOT_FOUND (cart_not_found, hold_not_found), FAILED_PRECONDITION (seat_taken, resource_invalid), INVALID_ARGUMENT.

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `ListEvents` | `ListEventsRequest` | `ListEventsResponse` | - |
| `GetSchedules` | `GetSchedulesRequest` | `GetSchedulesResponse` | - |
| `GetTicketTypes` | `GetTicketTypesRequest` | `GetTicketTypesResponse` | - |
| `GetSeatMap` | `GetSeatMapRequest` | `GetSeatMapResponse` | - |
| `HoldSeat` | `HoldSeatRequest` | `HoldSeatResponse` | - |
| `ReleaseSeat` | `ReleaseSeatRequest` | `ActionResponse` | - |

### Messages

- `EventSchedule`
- `EventTicketType`
- `GetSchedulesRequest`
- `GetSchedulesResponse`
- `GetSeatMapRequest`
- `GetSeatMapResponse`
- `GetTicketTypesRequest`
- `GetTicketTypesResponse`
- `HoldSeatRequest`
- `HoldSeatResponse`
- `ListEventsRequest`
- `ListEventsResponse`
- `ReleaseSeatRequest`
- `ScheduledEvent`
- `Seat`

### Enums

- `SeatStatus`

---

## shift

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/shift.proto`

> ShiftService - shift lifecycle.
> 
> States: open -> paused -> open -> ... -> closed -> reconciled.
> Invariants:
>   * 1 user = 1 open shift max (cross-post). Double-open attempt = FAILED_PRECONDITION.
>   * 1 post = 1 open shift max.
>   * Cashier-less posts (default_user_id + is_always_open or post_type.has_shift=false): the server opens
>     the shift on demand as default_user_id and rolls it at business_day_cutoff. The UI never calls Open/Close.
> 
> Closing: a deposited amount is declared for each payment_method with
> deposit_at_close=true (typically cash). With no sale -> direct close without
> dialog (no-op deposit).
> 

### Services

#### `ShiftService`

ShiftService - shift management. Called by: POSClient (open at login, close at logout), all services SalesService/Device/etc. via interceptor for permission validation, and UI for button pre-validation. Usage patterns:   * Login: UserService.Authenticate -> OpenShift.   * Meal break: PauseShift -> ResumeShift.   * End of day: CloseShift with declared_deposits per method. Error codes: NOT_FOUND, FAILED_PRECONDITION (already open / not open), PERMISSION_DENIED, ALREADY_EXISTS (idempotency). Dependencies: UserService (auth), publication of ShiftOpenedEvent / ShiftPausedEvent / ShiftResumedEvent / ShiftClosedEvent via EventService.

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `OpenShift` | `OpenShiftRequest` | `OpenShiftResponse` | - |
| `PauseShift` | `PauseShiftRequest` | `Shift` | - |
| `ResumeShift` | `ResumeShiftRequest` | `Shift` | - |
| `CloseShift` | `CloseShiftRequest` | `CloseShiftResponse` | - |
| `GetCurrentShift` | `GetCurrentShiftRequest` | `Shift` | - |
| `ListShifts` | `ListShiftsRequest` | `ListShiftsResponse` | - |
| `CheckPermission` | `CheckPermissionRequest` | `CheckPermissionResponse` | - |

### Messages

- `CheckPermissionRequest`
- `CheckPermissionResponse`
- `CloseShiftRequest`
- `CloseShiftResponse`
- `DeclaredDeposit`
- `GetCurrentShiftRequest`
- `ListShiftsRequest`
- `ListShiftsResponse`
- `OpenShiftRequest`
- `OpenShiftResponse`
- `PauseShiftRequest`
- `ResumeShiftRequest`
- `Shift`
- `ShiftDeposit`
- `ShiftPause`

### Enums

- `ShiftStatus`

---

## sync

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/sync.proto`

> SyncService: outbox synchronization and post network health.
> 
> The post keeps a local SQLite and an outbox of mutations. When the
> connection to ServerDB Postgres is restored, the worker drains the
> outbox. This service lets the UI:
>   * see the sync state (online/degraded/offline + outbox count)
>   * force a manual drain (e.g. cashier sees 200 pending messages)
>   * monitor network health (latency to ServerDB)
> 

### Services

#### `SyncService`

SyncService - read / control the sync. Called by: POSClient (connection indicator + "Force resync" button), ServerAdmin (online posts dashboard). Error codes: PERMISSION_DENIED (force_drain), UNAVAILABLE (sync worker stopped).

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `GetSyncStatus` | `GetSyncStatusRequest` | `SyncStatus` | - |
| `GetOutboxCount` | `GetOutboxCountRequest` | `GetOutboxCountResponse` | - |
| `ForceDrain` | `ForceDrainRequest` | `ForceDrainResponse` | - |

### Messages

- `ForceDrainRequest`
- `ForceDrainResponse`
- `GetOutboxCountRequest`
- `GetOutboxCountResponse`
- `GetSyncStatusRequest`
- `SyncStatus`

### Enums

- `SyncState`

---

## user

Source file: `src/public/StillDeep.Intelligence.Contracts/protos/user.proto`

> UserService - authentication (password or id scan mode) + read of the
> post's accepted auth modes + idle timeout.
> 
> Plugins (RFID, magstripe, exotic scanner) do NOT plug into this
> API: they locally inject a "barcode" or "credential string" via
> the POSClient event bus (ScanReceivedEvent), which then calls
> Authenticate with the "barcode" mode + the credential. The service does not know
> where the code comes from - it only knows it received an identifier to
> resolve.
> 

### Services

#### `UserService`

UserService - authentication and user lookup. Called by: POSClient (login screen, user selection dropdown), all RPCs that accept a manager_auth_token (override discount, refund, etc.). Error codes: INVALID_ARGUMENT (mode not accepted by the post, empty login), UNAUTHENTICATED (invalid credentials), PERMISSION_DENIED (user suspended/locked/deactivated), NOT_FOUND (unknown post_id). Dependencies: ServerDB for user storage (Postgres), publication of ShiftOpenedEvent indirectly via OpenShift.

| RPC | Request | Response | Streaming |
|---|---|---|---|
| `ListUsersForPost` | `ListUsersForPostRequest` | `ListUsersForPostResponse` | - |
| `GetPostAuthConfig` | `GetPostAuthConfigRequest` | `PostAuthConfig` | - |
| `Authenticate` | `AuthenticateRequest` | `AuthenticateResponse` | - |
| `ResetPasswordEmail` | `ResetPasswordEmailRequest` | `ResetPasswordEmailResponse` | - |
| `ResetPasswordAutoGenerate` | `ResetPasswordAutoGenerateRequest` | `ResetPasswordAutoGenerateResponse` | - |
| `ChangePassword` | `ChangePasswordRequest` | `ChangePasswordResponse` | - |

### Messages

- `AuthenticateRequest`
- `AuthenticateResponse`
- `ChangePasswordRequest`
- `ChangePasswordResponse`
- `GetPostAuthConfigRequest`
- `ListUsersForPostRequest`
- `ListUsersForPostResponse`
- `PostAuthConfig`
- `ResetPasswordAutoGenerateRequest`
- `ResetPasswordAutoGenerateResponse`
- `ResetPasswordEmailRequest`
- `ResetPasswordEmailResponse`
- `UserSummary`

### Enums

- `AuthMode`

---

## How to call

```bash
# List the services (server reflection)
grpcurl -plaintext localhost:50051 list

# List the RPCs of a service
grpcurl -plaintext localhost:50051 list stilldeep.v1.CatalogService

# Describe a message
grpcurl -plaintext localhost:50051 describe stilldeep.v1.Cart

# Call an RPC
grpcurl -plaintext -d '{"query":"kind:boutique"}' localhost:50051 stilldeep.v1.CatalogService/SearchProducts
```

## Related HTTP endpoints

| URL | Description |
|---|---|
| `http://localhost:5050/healthz` | Health check (HTTP/1.1) |
| `http://localhost:5051/grpc-info` | JSON manifest of the services and RPCs |
| `http://localhost:5051/swagger` | Swagger UI (description only, no live transcoding) |
| `http://localhost:5051/swagger/v1/swagger.json` | Minimal OpenAPI JSON |

Source: https://stilldeeppos.com/docs/reference/grpc
