openapi: 3.1.0
info:
  title: Sewapedia Agent API
  version: 1.0.0
  description: Public agent-facing subset — excludes admin/owner paths. See /llms.txt and /mcp-agent-guide.md for agent
    flows (REST + MCP).
servers:
  - url: /api/v1
tags:
  - name: platform
    description: Health & public config
  - name: auth
    description: JWT login, refresh, API keys
  - name: catalog
    description: Assets, categories, vendors
  - name: booking
    description: Rental requests & lifecycle
  - name: payment
    description: Midtrans, credit, webhooks
  - name: trust
    description: Verification & risk
  - name: owner
    description: Vendor/owner operations
  - name: admin
    description: Admin-only (RBAC ADMIN)
  - name: pricing
    description: Price quotes
  - name: ucp
    description: Universal Commerce Protocol facade
  - name: mcp
    description: Hosted Model Context Protocol (Streamable HTTP) — see /mcp-agent-guide.md
  - name: notifications
    description: In-app & push
  - name: reviews
    description: Booking reviews
  - name: chat
    description: Booking chat (prefer polling over SSE for agents)
  - name: user
    description: User profile
  - name: internal
    description: Cron — not for public agents
paths:
  /mcp:
    get:
      tags:
        - mcp
      summary: MCP Streamable HTTP (GET — SSE stream)
      description: >-
        Hosted Model Context Protocol for external AI agents (Hermes, Claude Desktop remote).

        Discovery manifest at `/.well-known/mcp`. Canonical guide: `/mcp-agent-guide.md` (25 tools).

        Transport `streamable-http`. Public tools (no auth): search_assets, check_availability, catalog_feed,
        get_asset_detail, list_categories, get_vendor, pricing_quote, ucp_discover, login.

        Auth tools (Bearer JWT or X-Api-Key + scopes): get_profile, list_my_bookings, get_booking_detail,
        booking_readiness, create_booking, promote_booking, cancel_booking, request_extension, apply_credit,
        initiate_payment, get_pay_status, get_verification_status, upload_document, list_notifications,
        mark_notification_read, ucp_checkout.

        Payment is human-in-the-loop (Midtrans) — see MCP guide §7.
      operationId: mcp_streamable_http_get
      responses:
        "200":
          description: MCP SSE stream or JSON-RPC response
        "405":
          description: Method not allowed (stateless mode may reject standalone GET)
    post:
      tags:
        - mcp
      summary: MCP Streamable HTTP (POST — JSON-RPC)
      description: Primary MCP entry — send JSON-RPC 2.0 messages (initialize, tools/list, tools/call). See
        /mcp-agent-guide.md for tool params and examples.
      operationId: mcp_streamable_http_post
      responses:
        "200": &a3
          description: Success envelope
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiSuccess"
          headers:
            X-RateLimit-Limit:
              schema:
                type: integer
              description: Rate limit ceiling
            X-RateLimit-Remaining:
              schema:
                type: integer
              description: Remaining requests in window
        "401": &a1
          description: Error envelope
          content: &a2
            application/json:
              schema:
                $ref: "#/components/schemas/ApiError"
        "403": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
    delete:
      tags:
        - mcp
      summary: MCP session close (Streamable HTTP)
      operationId: mcp_streamable_http_delete
      responses:
        "200":
          description: Session closed
  /health:
    get:
      tags:
        - platform
      summary: Health check
      operationId: platform_list_health
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /assets:
    get:
      tags:
        - catalog
      summary: List aset (q, category, vendorSlug, city, minPrice, maxPrice, page, limit)
      operationId: catalog_list_assets
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /assets/{slug}:
    get:
      tags:
        - catalog
      summary: Detail aset
      operationId: catalog_get_assets
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /assets/{slug}/availability:
    get:
      tags:
        - catalog
      summary: Ketersediaan kalender (from, to ISO date)
      operationId: catalog_get_assets_by_id_availability
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /catalog/feed:
    get:
      tags:
        - platform
      summary: Feed katalog paginated untuk agent discovery
      operationId: platform_get_catalog_feed
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /ucp/products:
    get:
      tags:
        - ucp
      summary: UCP product discovery (search/feed)
      operationId: ucp_get_ucp_products
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /ucp/products/{slug}:
    get:
      tags:
        - ucp
      summary: UCP product lookup by slug
      operationId: ucp_get_ucp_products
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /ucp/products/{slug}/availability:
    get:
      tags:
        - ucp
      summary: UCP product availability (from, to)
      operationId: ucp_get_ucp_products_by_id_availability
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /ucp/quotes:
    post:
      tags:
        - ucp
      summary: UCP price quote
      operationId: ucp_create_ucp_quotes
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /ucp/checkout/sessions:
    post:
      tags:
        - ucp
      summary: UCP checkout session create (booking)
      operationId: ucp_create_ucp_checkout_sessions
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /ucp/checkout/sessions/{id}:
    get:
      tags:
        - ucp
      summary: UCP checkout session status
      operationId: ucp_get_ucp_checkout_sessions
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /ucp/checkout/sessions/{id}/complete:
    post:
      tags:
        - ucp
      summary: UCP complete checkout (promote or pay)
      operationId: ucp_create_ucp_checkout_sessions_by_id_complete
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /categories:
    get:
      tags:
        - catalog
      summary: Daftar kategori
      operationId: catalog_list_categories
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /auth/login:
    post:
      tags:
        - auth
      summary: Login JWT (email + password)
      operationId: auth_login
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /auth/refresh:
    post:
      tags:
        - auth
      summary: Rotasi refresh token
      operationId: auth_create_auth_refresh
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /verification/status:
    get:
      tags:
        - trust
      summary: Status verifikasi
      operationId: trust_get_verification_status
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /upload:
    post:
      tags:
        - trust
      summary: Upload dokumen/foto (multipart)
      operationId: trust_create_upload
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /bookings/{id}/apply-credit:
    post:
      tags:
        - booking
      summary: Terapkan saldo kredit ke booking PENDING_PAYMENT
      operationId: booking_create_bookings_by_id_apply-credit
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /vendors/{slug}:
    get:
      tags:
        - catalog
      summary: Vendor storefront publik
      operationId: catalog_get_vendors
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /category-requirements:
    get:
      tags:
        - catalog
      summary: Persyaratan verifikasi per kategori
      operationId: catalog_list_category-requirements
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /pricing/quote:
    post:
      tags:
        - pricing
      summary: Quote harga (auth opsional untuk deposit dinamis)
      operationId: pricing_create_pricing_quote
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /bookings:
    get:
      tags:
        - booking
      summary: List booking penyewa
      operationId: booking_list_bookings
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
    post:
      tags:
        - booking
      summary: Buat rental request
      operationId: booking_create
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /bookings/{id}:
    get:
      tags:
        - booking
      summary: Detail booking
      operationId: booking_get_bookings
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /bookings/{id}/pay:
    post:
      tags:
        - booking
      summary: Inisiasi pembayaran Midtrans
      operationId: payment_initiate
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /bookings/{id}/messages:
    get:
      tags:
        - booking
      summary: Chat log
      operationId: booking_get_bookings_by_id_messages
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /bookings/{id}/pay/status:
    get:
      tags:
        - booking
      summary: Sinkron status pembayaran Midtrans
      operationId: booking_get_bookings_by_id_pay_status
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /bookings/{id}/counter-offer:
    patch:
      tags:
        - booking
      summary: Terima/tolak counter-offer (penyewa)
      operationId: booking_update_bookings_by_id_counter-offer
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /bookings/{id}/promote:
    post:
      tags:
        - booking
      summary: Promosikan booking PENDING_VERIFICATION ke inbox pemilik
      operationId: booking_promote
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /bookings/{id}/cancel:
    post:
      tags:
        - booking
      summary: Batalkan booking oleh penyewa
      operationId: booking_create_bookings_by_id_cancel
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /bookings/{id}/readiness:
    get:
      tags:
        - booking
      summary: Status booking + nextActions untuk AI agent
      operationId: booking_get_bookings_by_id_readiness
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /auth/api-keys:
    get:
      tags:
        - auth
      summary: Daftar API key scoped user
      operationId: auth_get_auth_api-keys
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
    delete:
      tags:
        - auth
      summary: Revoke API key (query keyId)
      operationId: auth_delete_auth_api-keys
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /auth/api-keys/{id}/rotate:
    post:
      tags:
        - auth
      summary: Rotate API key — revoke lama, issue secret baru
      operationId: auth_create_auth_api-keys_by_id_rotate
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
  /webhooks/agent:
    get:
      tags:
        - payment
      summary: List webhook agent terdaftar
      operationId: payment_get_webhooks_agent
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
    post:
      tags:
        - payment
      summary: Daftar webhook agent partner (HMAC)
      operationId: payment_create_webhooks_agent
      security:
        - BearerAuth: []
      responses:
        "200": *a3
        "201": *a3
        "400": *a1
        "401": *a1
        "403": *a1
        "404": *a1
        "429":
          description: Rate limited — check Retry-After and X-RateLimit-* headers
          content: *a2
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: "JWT from POST /auth/login, or scoped API key (sp_test_… / sp_live_…) via Authorization: Bearer or
        X-Api-Key header"
  schemas:
    ApiSuccess:
      type: object
      required:
        - success
        - data
      properties:
        success:
          type: boolean
          enum:
            - true
        data: {}
    ApiError:
      type: object
      required:
        - success
        - error
      properties:
        success:
          type: boolean
          enum:
            - false
        error:
          type: string
    PaginationMeta:
      type: object
      properties:
        page:
          type: integer
        limit:
          type: integer
        total:
          type: integer
        totalPages:
          type: integer
    LoginRequest:
      $ref: ../schemas/LoginRequest.json
    LoginResponse:
      $ref: ../schemas/LoginResponse.json
    CreateBookingRequest:
      $ref: ../schemas/CreateBookingRequest.json
    PricingQuoteRequest:
      $ref: ../schemas/PricingQuoteRequest.json
    BookingReadiness:
      $ref: ../schemas/BookingReadiness.json
    AssetAvailability:
      $ref: ../schemas/AssetAvailability.json
    CatalogFeedItem:
      $ref: ../schemas/CatalogFeedItem.json
