Skip to Content
Catalog and providers

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

EndpointAccessBehavior
GET /api/v1/catalog/servicesPublicReturns the platform service catalog. It is distinct from a provider’s listing menu.
GET /api/v1/catalog/categoriesPublicReturns active listing categories for filters and listing creation.
GET /api/v1/listings?q=&category_id=&limit=&offset=PublicSearches 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}PublicReturns one listing only while it meets the same public eligibility rules as search.
GET /api/v1/listings/{id}/contactSigned-in memberReturns 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:

  1. The listing status is active.
  2. Its subscription status is active and the subscription is not expired.
  3. 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

EndpointAccessBehavior
POST /api/v1/providersSigned-in userCreates 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/meSigned-in provider ownerUpdates the authenticated user’s provider profile. The owner ID is taken from the token, not trusted from JSON.
POST /api/v1/admin/providers/{id}/approveAdministratorApproves the provider. Approval is an administrative action; it also promotes the provider owner’s role in the PostgreSQL transaction.
GET /api/v1/providers/me/dashboardSigned-in providerReturns 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/salesSigned-in providerRecords 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

EndpointAccessBehavior
POST /api/v1/listingsSigned-in provider ownerCreates 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 ownerUpdates 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 ownerDeactivates the caller’s listing; this is a state change, not a hard-delete endpoint.
POST /api/v1/listings/{id}/menu-itemsSigned-in provider ownerCreates 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 ownerUpdates an item only when it is linked to the caller’s listing.
DELETE /api/v1/menu-items/{id}?listing_id=Signed-in provider ownerDeletes 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

  1. Load categories and services for search/filter UI.
  2. Register the signed-in account as a provider.
  3. Create a draft listing with a valid category.
  4. Add menu items with prices and currency.
  5. Create a listing subscription payment and show the returned server-owned amount and payment instructions.
  6. Wait for the payment review and provider approval workflows to complete.
  7. Refresh public search; only the fully eligible listing should appear.
  8. 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.