Source document · API/docs/CATALOG_AND_PROVIDERS.md
Service catalog, providers and listings
This guide explains the route family that turns provider-entered business data into a public listing customers can discover and book. The catalog module owns provider profiles, listings, categories, and menu items. PostgreSQL constraints and owner-qualified update statements prevent one provider from changing another provider’s records.
The resources and their relationship
The provider owner is the authenticated user. A provider profile is not itself the public business listing: the profile represents the provider account and approval state, while each listing has its own category, description, status, menu, and subscription. An order stores an authoritative listing/menu price snapshot so future menu edits do not silently rewrite past bookings.
Public discovery
| Endpoint | Access | Behavior |
|---|---|---|
GET /api/v1/catalog/services | Public | Returns the platform service catalog. It is distinct from a provider’s listing menu. |
GET /api/v1/catalog/categories | Public | Returns active listing categories for filters and listing creation. |
GET /api/v1/listings?q=&category_id=&limit=&offset= | Public | Searches only public, eligible listings. Search text matches listing name, description, or provider display name. Results prioritize currently featured listings, then most recently updated listings. |
GET /api/v1/listings/{id} | Public | Returns one listing only while it meets the same public eligibility rules as search. |
GET /api/v1/listings/{id}/contact | Signed-in member | Returns the provider account ID and display name for an eligible listing. This endpoint is protected because public search does not expose account IDs for chat initiation. |
The listing search accepts limit (default 20, maximum 100) and offset (default 0). Negative/out-of-range values and an overlong query/category identifier are rejected with validation errors. Unknown/inactive categories cannot be used to create a listing. Public listing visibility requires all of these conditions:
- The listing status is
active. - Its subscription status is
activeand the subscription is not expired. - The owning provider’s approval status is
approved.
The returned menu includes only active menu items. An eligible featured listing sorts ahead of other eligible results; featured is derived from featured_until. Listing subscription eligibility and featuring are different concepts: subscription allows publishing; feature time only changes search ordering.
Provider onboarding and owner-bound mutations
| Endpoint | Access | Behavior |
|---|---|---|
POST /api/v1/providers | Signed-in user | Creates one provider profile for the authenticated user. The server supplies the owner ID, pending approval state, offline availability, generated ID, and creation time. A client cannot choose another owner or self-approve. |
PATCH /api/v1/providers/me | Signed-in provider owner | Updates the authenticated user’s provider profile. The owner ID is taken from the token, not trusted from JSON. |
POST /api/v1/admin/providers/{id}/approve | Administrator | Approves the provider. Approval is an administrative action; it also promotes the provider owner’s role in the PostgreSQL transaction. |
GET /api/v1/providers/me/dashboard | Signed-in provider | Returns dashboard summary data scoped to the provider owner. It is a read model, not a new source of order/payment records. |
POST /api/v1/providers/me/sales | Signed-in provider | Records a provider-owned manual sale through the sales service. The server derives the actor from the bearer token. |
Provider registration requires a non-empty display name. The profile starts pending and offline. The business UI should show the review state to the provider; hiding the approval control is not the security boundary—the API checks the actor role.
Listings and menu management
| Endpoint | Access | Behavior |
|---|---|---|
POST /api/v1/listings | Signed-in provider owner | Creates a listing for the caller’s provider profile. The server assigns its ID, creation time, and initial draft state. A valid category ID and name are required. |
PATCH /api/v1/listings/{id} | Signed-in provider owner | Updates the listing addressed by the path only if it belongs to the authenticated owner. The handler overwrites the submitted listing/provider IDs with path/actor values. |
DELETE /api/v1/listings/{id} | Signed-in provider owner | Deactivates the caller’s listing; this is a state change, not a hard-delete endpoint. |
POST /api/v1/listings/{id}/menu-items | Signed-in provider owner | Creates an active menu item under an owned listing. The path supplies listing ID and the server assigns item ID and active state. |
PATCH /api/v1/menu-items/{id} | Signed-in provider owner | Updates an item only when it is linked to the caller’s listing. |
DELETE /api/v1/menu-items/{id}?listing_id= | Signed-in provider owner | Deletes a menu item only when the supplied listing belongs to the caller. |
Menu item creation/update validates a name, non-negative price_amount, and a three-letter currency code; currencies are uppercased by the service. The route contract supports JSON request bodies, but some fields are not fully described in OpenAPI yet. For exact fields, consult the endpoint handler DTO and catalog domain types until the OpenAPI request schemas are expanded.
How a provider becomes bookable
Provider approval and listing subscription payment are separate checks. A provider can be approved while a listing is still a draft or unpaid; a subscription payment can be confirmed before approval without making the listing public. Once both conditions hold, confirming an eligible subscription activates the subscription and promotes the listing from draft to active. The public query also checks the subscription expiry at read time.
The diagram describes the main gates, not every database status field. Listing/payment state changes happen in the repository transaction. A subscription payment’s amount/currency/duration come from server configuration; the client chooses a supported payment method but cannot set the plan price.
Client sequence
- Load categories and services for search/filter UI.
- Register the signed-in account as a provider.
- Create a draft listing with a valid category.
- Add menu items with prices and currency.
- Create a listing subscription payment and show the returned server-owned amount and payment instructions.
- Wait for the payment review and provider approval workflows to complete.
- Refresh public search; only the fully eligible listing should appear.
- A customer can then open the listing, select an active menu item, and place an order. The order service rechecks price and eligibility at booking time.
Important limits
- The provider dashboard is an aggregate read model, not a full accounting ledger.
- Service catalog records and listing menu items serve different purposes; the current booking price comes from the active listing menu item.
- Public listing lookups intentionally hide ineligible records as not found.
- A provider’s role, approval state, listing state, and subscription state are related but not interchangeable.
- Do not cache eligibility for a long time: approval or subscription expiry can change after the client fetches a listing.