Merchant API contract
merchant-api owns the authenticated business operations used by the retail
portal and POS clients: organizations, stores, catalog, inventory, staff,
customers, payments, reporting, and related merchant workflows. Its generated
OpenAPI document is the route and schema contract.
Sources of truth
- Runtime controllers and security:
apps/microservices/merchant-api - Generated contract: Merchant API OpenAPI
- Authorization behavior: IAM contract
- Service selection and credentials: API reference
The generated contract includes non-internal controller routes. It does not
grant access, and a generated localhost server URL is not a production
address.
Public routing
The API is reached through the public API load balancer. /merchant/* routes
are forwarded to merchant-api with the prefix removed. The default API host
also sends the merchant-owned direct routes to this service, so clients must
use the deployment URL configured for their environment rather than deriving a
host from the OpenAPI servers field.
Organization and store identifiers are part of many controller paths. They are authorization scope, not merely lookup keys. Callers must use the ID from their authenticated context and must not infer access from knowing another resource ID.
Authentication and authorization
A normal Merchant API request sends both:
Authorization: Bearer <firebase-id-token>
X-Peak-Authorization: Bearer <peak-authorization-token>
Obtain the Peak token from /auth/merchant/token after Firebase sign-in. The
service also accepts explicitly supported support and account token types on
routes intended for those principals. Token acceptance does not bypass the
route's organization, store-membership, ownership, or permission checks.
Authorization is enforced in layers:
- Firebase authenticates the principal.
- The Peak token establishes the tenant and authorization context.
- Controller annotations require the named permission.
- Store-scoped handlers verify store membership or ownership where required.
- Service logic applies resource-specific invariants.
The default is deny. See the IAM contract for policy evaluation and current seeded roles.
Narrow authentication exceptions
The service security configuration explicitly permits a small set of bootstrap or callback paths. Examples include health/error handling, invite validation, gift-card lookup, payment-provider webhooks, selected public wallet routes, and provider callback surfaces with their own verification. Invite acceptance and support authorization require Firebase even when they do not yet require a Peak token.
Treat each exception as route-specific:
- Verify the controller and security matcher before integrating.
- Preserve webhook signature, OIDC, App Check, or token validation implemented for that route.
- Do not describe a controller group as public because one operation is permitted anonymously.
- Never expose
/internal/routes to a browser, mobile user, or external integration.
Reading the generated contract
Use the OpenAPI file for exact methods, paths, query parameters, request bodies, status codes, and schemas. When evaluating a proposed integration:
- Find the operation in the generated document.
- Confirm its organization/store path scope.
- Check the controller for the required permission and non-schema invariants.
- Confirm the caller can obtain the required Firebase and Peak tokens.
- Use an idempotency mechanism only where the operation documents one; do not assume all mutations are safely retryable.
The OpenAPI schema describes the transport contract. It does not replace service-layer rules such as ownership verification, provider state, inventory invariants, or ambiguous payment outcome handling.
Changing the contract
Start from implementation, not from the artifact:
-
Change the controller, request/response DTOs, validation, and tests.
-
Regenerate the artifacts:
bazel run //scripts:sync_openapi_specs -
Review the generated Merchant API path and schema changes.
-
Regenerate affected clients through
tools/openapi-generator. -
Run the repository contract gate:
./scripts/test-local.sh contracts
Do not maintain copied endpoint inventories or DTO tables on this page. They drift from controller annotations and generated clients.