# Frontend API Quickstart Status: active Normative contracts: `contracts/openapi/*`, `contracts/asyncapi/realtime.yaml` Runtime coverage: `docs/architecture/mvp-backend-operation-coverage.yaml` Deployed docs: `https://api.byme.my/docs/` ## Before integration 1. Find the operation ID in the audience contract: Core, Buyer, Creator, Seller, Admin, or Partner. 2. Find it under `canonical_operations` in the matching runtime-coverage slice and confirm both adapter and runtime status. A normative operation may still be unavailable. 3. Obtain one base URL per audience from deployment configuration. Core and Buyer are different roots; never resolve `/cart` against a Core URL. Production docs: - `https://api.byme.my/docs/` - Scalar API Reference hub with Buyer, Core, Seller, Admin, Creator and Partner specs. - `https://docs.byme.my/` - dedicated docs host for direct browsing and external sharing. Prefer `https://api.byme.my/docs/` for integration tooling when same-origin API/docs access is useful. - `https://docs.byme.my/site/seller-integrator-guide.html` - step-by-step external seller integration: machine access, catalog, stock and fulfillment. Production audience base URLs: - Core: `https://api.byme.my/core/v1` - Buyer: `https://api.byme.my/buyer/v1` - Seller: `https://api.byme.my/seller/v1` - Admin: `https://api.byme.my/admin/v1` - Creator: `https://api.byme.my/creator/v1` - Partner: `https://api.byme.my/partner/v1` Raw OpenAPI YAML: - `https://api.byme.my/docs/openapi/core.openapi.yaml` - `https://api.byme.my/docs/openapi/buyer.openapi.yaml` - `https://api.byme.my/docs/openapi/seller.openapi.yaml` - `https://api.byme.my/docs/openapi/admin.openapi.yaml` - `https://api.byme.my/docs/openapi/creator.openapi.yaml` - `https://api.byme.my/docs/openapi/partner.openapi.yaml` ## Every request - send `X-Request-Id` and W3C `traceparent`; - use exactly one contract-declared auth alternative: Bearer, or web-session cookies plus `X-CSRF-Token` when the operation declares the CSRF scheme; - send a 16–128 character `Idempotency-Key` whenever the operation declares it; - when an operation uses `x-byme-idempotency` instead of the common `Idempotency-Key` parameter, follow that extension exactly: `client-message-id` means persist and replay the client message ID, `event-id-deduplicated` means dedupe by the event ID, and `inherently-idempotent` means retries are allowed only with the same resource identity and request body; - send the strong `ETag` from the declared source GET as `If-Match`; never fabricate an ETag or send `W/`; - parse RFC 9457 `application/problem+json` by stable type/code and retain `request_id`; - treat `202`, partial outcomes, cursor expiry, and retry as explicit UI states; - ignore additive response fields, but map unknown enum/status values to an explicit `unknown` state, retain the raw value for safe telemetry, and stop automated transitions until refreshed or upgraded. The frontend owns confirmation dialogs, permissions, geolocation choice, loading, offline, responsive layout, and accessibility interaction. The backend owns authorization, ownership, money, eligibility, concurrency, idempotency, and terminal workflow truth. ## Creator payout Production PSP and payout provider status: the commerce backend path is runtime-verified through signed provider webhooks, but real external PSP and payout rails are still provider-integration gates. Frontend must render payout and payment provider `503`/degraded states from Problem Details and must not assume provider settlement outside the returned authoritative state. 1. Call `createCreatorPayoutQuote` with amount, beneficiary, payout method and tax profile. 2. Display `gross_amount`, `withholding`, `fees`, `net_amount`, rail and `expires_at` exactly as returned. 3. Call `createCreatorPayout` with the same gross amount and `quote_id`. 4. On expiry or `409`, request a new quote; never recalculate deductions or reuse a quote in the client. ## Working fixture [`examples/frontend-api.ts`](examples/frontend-api.ts) is a bearer-client fixture and contains: - authenticated read; - replay-safe POST; - ETag PATCH; - cursor iteration with typed restart; - cancellation submission and authoritative recovery read; - long-running operation polling; - unknown enum and degraded-response classification; - RFC 9457 parsing without exposing `detail`; - WebSocket ticket negotiation, deduplication, checkpoint resume and snapshot recovery. Run its dependency-free self-check on Node 24: ```bash node --experimental-strip-types docs/examples/frontend-api.ts ``` Use `BYME_CORE_API_BASE_URL`, `BYME_BUYER_API_BASE_URL` and `BYME_ACCESS_TOKEN` only in the frontend runtime environment. Keep the audience prefix in each base URL, pass relative resource paths without a leading `/`, and never commit tokens or cookies. For browser cookie sessions, build a separate client with `credentials: "include"` and the contract-declared CSRF header. Do not send an empty Bearer header and do not copy the bearer fixture unchanged. ## Unknown outcomes 1. Keep the idempotency key outside the retry loop. 2. Retry only the identical method, path and body with the same key. 3. Treat an idempotency fingerprint conflict as terminal. 4. For money and cancellation commands, read authoritative state before a new command. 5. Keep the key until the command reaches authoritative terminal state; a UI reload is not a new intent. Branch on Problem `type`/`code`, never `title` or `detail`. Use `retryable`, `retry_after`/`Retry-After`, validation `errors`, `conflict` and `required_action` as typed UI states. Show only an approved `user_message`; retain `request_id` for support. `503` is a degraded dependency state, not empty business data. Preserve the last explicitly labelled snapshot when policy allows it, disable affected mutations, and provide retry. `retryable: true` permits a retry but does not itself mean dependency degradation. Honor `retry_after`/`Retry-After`; never convert an error into an empty successful list. ## Pagination - Treat cursors as opaque, query-bound and expiring. - On a typed cursor-expired/invalid `409`, restart without `cursor`. - Reconcile restarted items by opaque resource ID; do not append duplicates. - Never reuse a cursor after filters, ordering, principal or page size changes. ## Order cancellation 1. Show the action only when `BuyerOrder.available_actions` advertises it as allowed. 2. Call `createOrderCancellation` once and keep its idempotency key. 3. Treat `202` as pending, not completed. 4. Follow `operation.result_uri`/`poll_after_seconds` when present; otherwise poll `getBuyerOrderCancellation`. 5. Render accepted, rejected and pending line outcomes; use stable reason codes for copy selection. 6. Refresh `getBuyerOrder`; realtime is a refresh hint, never order authority. ## Creator disputes - Show dispute creation for `deal` and `deliverable` only after the corresponding creator-owned resource has been loaded. - Treat missing, unsupported or incomplete subject evidence as unavailable; never invent a client-side evidence package. - `getCreatorDisputeEvidencePackage` is the immutable server-sealed decision input. Do not rebuild its digests in the frontend. ## Creator onboarding 1. Read `getCreatorApplication` and keep its strong `ETag`. 2. Render `required_actions` and `available_actions`; never infer readiness from locally completed fields. 3. Call `submitCreatorApplication` with the same `Idempotency-Key` on retries and the latest `If-Match`. 4. On `creator_application_not_ready` or `409`, reread the application; do not force an `approved`/`active` UI state. 5. Treat `review` as pending server reconciliation. Enable creator-only capabilities only after the public status becomes `approved`; `rejected` is final for the submitted revision. There is no frontend-facing staff review route in the current contract. Admin clients must not call `/internal/**`. Approval remains server-owned. Earned creator milestones are available through `GET /creator/v1/achievements`; locked achievements and progress remain unavailable until a policy owner contract exists. ## Return and dispute evidence 1. Create an evidence upload intent and upload the exact declared bytes. 2. Wait until core reports the media as `ready`; do not submit `awaiting_upload`, `scanning` or rejected IDs. 3. Send the ready ID in `evidence_media_ids`. 4. Parse `X-ByMe-Attachment-Verification-Evidence` as JSON. Every requested ID must appear under `bindings[]` with a ready, MIME-matched and malware-clean gate. An empty attachment list returns `{"bindings":[]}`. A non-ready or wrong-owner ID fails the mutation; never retry it as attachment-free. ## Realtime recovery LiveKit interactive rooms are enabled internally through `live-media-gateway` room grants, but public WebRTC release requires DNS and certificates for `livekit.byme.my` and `turn.byme.my`. Treat LiveKit join failures as a typed transport-degraded state and keep HLS playback on the MediaMTX playback URL when the API advertises it. 1. Mint a new single-use ticket through `createRealtimeConnectionTicket`. Keep one idempotency key for an uncertain mint response; use a new key for a genuinely new single-use ticket. 2. Connect to returned `websocket_url` with subprotocols `byme.realtime.v1` and `byme.ticket.`. Query tickets are forbidden. ## Admin refund maker-checker 1. Maker sends `approve_return` to `POST /return-cases/{return_case_id}/decisions` and keeps the returned `decision_id`. 2. A different authenticated staff account sends `approve_refund` for the same case with that ID in `maker_action_id`. Keep the policy version and all maker evidence IDs. 3. Reuse the exact idempotency key and body after timeout or `503`; do not create a new command. Missing maker evidence is rejected; self-approval, another case, policy drift and removed maker evidence return a conflict. The public request shape is unchanged. 3. Deduplicate by event `id` and checkpoint `sequence` per `partitionkey`. 4. On a gap, stop applying events, refresh every authoritative recovery snapshot, then mint a fresh ticket. 5. Reconnect with the latest rotated resume token and per-partition checkpoints; never reuse a connection ticket. 6. Do not persist expired tickets/tokens or send them to logs or analytics. Unknown event types are not commands. Ignore their payload, retain only allowlisted telemetry, and refresh `recoveryurl` when that event may affect the visible resource. ## Long-running operations Poll `getOperation` no faster than `poll_after_seconds`. Treat `pending` and `running` as non-terminal; `partially_succeeded` is terminal but requires rendering partial results. On an unknown status, stop polling automation and surface a safe refresh/upgrade state. Follow `result_uri` only through the configured audience router; do not assume it belongs to the Core base URL. ## Contract changes Use `.agents/skills/contract-safe-change/SKILL.md`. Frontend code must consume `contracts/`; backend-local OpenAPI files are generator projections only.