openapi: 3.1.0
info:
  title: Meum Public Merchant API
  version: "1.0.0"
  description: |
    Public merchant API for programmatic payment management: invoices, transactions,
    payment links, webhooks, and WooCommerce integration.

    Authenticate with a store-scoped API key (`sk_live_*`) via Bearer token.
    Base URL: https://api.meum.io
  contact:
    name: Meum Support
    email: support@meum.io
    url: https://meum.io

servers:
  - url: https://api.meum.io
    description: Production

tags:
  - name: Health
    description: Service health checks
  - name: Invoices
    description: Invoice lifecycle
  - name: Transactions
    description: On-chain payment transactions
  - name: Payment Links
    description: Reusable payment link management
  - name: Webhooks
    description: Webhook endpoint and delivery management
  - name: Integrations
    description: Third-party platform integrations

security:
  - bearerAuth: []

paths:
  /health:
    get:
      tags: [Health]
      summary: Health check
      operationId: getHealth
      security: []
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"
              example:
                status: ok

  /v1/invoices:
    get:
      tags: [Invoices]
      summary: List invoices
      description: |
        Returns invoices for the authenticated store with cursor pagination.
        Results are ordered by creation time descending (newest first).
        Date filters default to a 90-day window when not specified.
      operationId: listInvoices
      parameters:
        - $ref: "#/components/parameters/CursorLimit"
        - $ref: "#/components/parameters/CursorStartingAfter"
        - $ref: "#/components/parameters/CursorEndingBefore"
        - name: status
          in: query
          description: Show only invoices in this status (e.g. `paid`, `awaiting_payment`). Omit to include all statuses.
          schema:
            $ref: "#/components/schemas/InvoiceStatus"
        - name: external_order_id
          in: query
          description: Show only invoices with this order reference (exact match). Omit to skip this filter.
          schema:
            type: string
            example: order_demo_1048
        - name: customer_id
          in: query
          description: Show only invoices for this customer ID. Omit to include all customers.
          schema:
            type: string
            format: uuid
        - name: payment_link_id
          in: query
          description: Show only invoices created from this Pay Link (`plink_...`). Omit to include all links.
          schema:
            type: string
            format: uuid
        - name: created_after
          in: query
          description: Only invoices created on or after this time (UTC, e.g. `2025-01-01T00:00:00.000Z`). Omit for no lower bound.
          schema:
            type: string
            format: date-time
        - name: created_before
          in: query
          description: Only invoices created before this time (UTC). Omit for no upper bound.
          schema:
            type: string
            format: date-time
        - name: paid_after
          in: query
          description: Only invoices paid on or after this time (UTC). Omit for no lower bound.
          schema:
            type: string
            format: date-time
        - name: paid_before
          in: query
          description: Only invoices paid before this time (UTC). Omit for no upper bound.
          schema:
            type: string
            format: date-time
      responses:
        "200":
          description: Paginated invoice list
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/CursorPage"
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/PublicInvoice"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

    post:
      tags: [Invoices]
      summary: Create invoice
      description: |
        Creates a new checkout invoice for the authenticated store.
        Returns a legacy error shape on some validation failures for backward compatibility.
      operationId: createInvoice
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateInvoiceRequest"
            example:
              external_order_id: order_demo_1048
              amount: "100.00"
              currency: USD
              return_url: https://shop.example.com/thank-you
              metadata:
                line_items: 2
      responses:
        "201":
          description: Invoice created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceCreateResponse"
        "400":
          description: Bad request (legacy error shape)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Forbidden (legacy error shape)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "409":
          description: Conflict (legacy error shape)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limited (legacy error shape)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /v1/invoices/{id}:
    get:
      tags: [Invoices]
      summary: Get invoice
      operationId: getInvoice
      parameters:
        - $ref: "#/components/parameters/InvoiceId"
      responses:
        "200":
          description: Invoice details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicInvoice"
        "404":
          $ref: "#/components/responses/NotFound"

  /v1/invoices/{id}/status:
    get:
      tags: [Invoices]
      summary: Get invoice status
      description: Lightweight status poll including deposit details and swap processing state.
      operationId: getInvoiceStatus
      parameters:
        - $ref: "#/components/parameters/InvoiceId"
      responses:
        "200":
          description: Invoice status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceStatusResponse"
        "404":
          $ref: "#/components/responses/NotFound"

  /v1/invoices/{id}/cancel:
    post:
      tags: [Invoices]
      summary: Cancel invoice
      description: Cancels an open invoice. Not available for WooCommerce dedicated keys.
      operationId: cancelInvoice
      parameters:
        - $ref: "#/components/parameters/InvoiceId"
      responses:
        "200":
          description: Invoice cancelled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicInvoice"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /v1/invoices/{id}/transactions:
    get:
      tags: [Invoices]
      summary: List invoice transactions
      operationId: listInvoiceTransactions
      parameters:
        - $ref: "#/components/parameters/InvoiceId"
      responses:
        "200":
          description: Transactions for the invoice
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/PublicTransaction"
        "404":
          $ref: "#/components/responses/NotFound"

  /v1/transactions:
    get:
      tags: [Transactions]
      summary: List transactions
      description: Returns payment transactions for the authenticated store with cursor pagination.
      operationId: listTransactions
      parameters:
        - $ref: "#/components/parameters/CursorLimit"
        - $ref: "#/components/parameters/CursorStartingAfter"
        - name: invoice_id
          in: query
          description: Show only transactions for this invoice (`inv_...`). Omit to include all invoices.
          schema:
            type: string
            format: uuid
        - name: payment_link_id
          in: query
          description: Show only transactions from invoices created via this Pay Link. Omit to include all links.
          schema:
            type: string
            format: uuid
        - name: customer_id
          in: query
          description: Show only transactions for this customer's invoices. Omit to include all customers.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Paginated transaction list
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/CursorPage"
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/PublicTransaction"
        "403":
          $ref: "#/components/responses/Forbidden"

  /v1/transactions/{id}:
    get:
      tags: [Transactions]
      summary: Get transaction
      operationId: getTransaction
      parameters:
        - $ref: "#/components/parameters/TransactionId"
      responses:
        "200":
          description: Transaction details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicTransaction"
        "404":
          $ref: "#/components/responses/NotFound"

  /v1/transactions/by-hash/{transactionHash}:
    get:
      tags: [Transactions]
      summary: Get transaction by hash
      operationId: getTransactionByHash
      parameters:
        - name: transactionHash
          in: path
          required: true
          description: On-chain transaction hash
          schema:
            type: string
            example: "0xabc123def4567890abcdef1234567890abcdef1234567890abcdef1234567890"
      responses:
        "200":
          description: Transaction details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicTransaction"
        "404":
          $ref: "#/components/responses/NotFound"

  /v1/payment-links:
    post:
      tags: [Payment Links]
      summary: Create payment link
      operationId: createPaymentLink
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePaymentLinkRequest"
            example:
              title: Summer fundraiser
              amount_mode: fixed_amount
              amount: "25.00"
              allow_multiple_payments: true
              public_visible: true
      responses:
        "201":
          description: Payment link created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentLink"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"

    get:
      tags: [Payment Links]
      summary: List payment links
      operationId: listPaymentLinks
      parameters:
        - $ref: "#/components/parameters/CursorLimit"
        - $ref: "#/components/parameters/CursorStartingAfter"
      responses:
        "200":
          description: Paginated payment link list
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/CursorPage"
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/PaymentLink"

  /v1/payment-links/{id}:
    get:
      tags: [Payment Links]
      summary: Get payment link
      operationId: getPaymentLink
      parameters:
        - $ref: "#/components/parameters/PaymentLinkId"
      responses:
        "200":
          description: Payment link details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentLink"
        "404":
          $ref: "#/components/responses/NotFound"

    patch:
      tags: [Payment Links]
      summary: Update payment link
      description: Pricing fields cannot be changed after a checkout has started.
      operationId: updatePaymentLink
      parameters:
        - $ref: "#/components/parameters/PaymentLinkId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdatePaymentLinkRequest"
      responses:
        "200":
          description: Payment link updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentLink"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"

  /v1/payment-links/{id}/activate:
    post:
      tags: [Payment Links]
      summary: Activate payment link
      operationId: activatePaymentLink
      parameters:
        - $ref: "#/components/parameters/PaymentLinkId"
      responses:
        "200":
          description: Payment link activated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentLink"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"

  /v1/payment-links/{id}/deactivate:
    post:
      tags: [Payment Links]
      summary: Deactivate payment link
      operationId: deactivatePaymentLink
      parameters:
        - $ref: "#/components/parameters/PaymentLinkId"
      responses:
        "200":
          description: Payment link deactivated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentLink"
        "404":
          $ref: "#/components/responses/NotFound"

  /v1/payment-links/{id}/invoices:
    get:
      tags: [Payment Links]
      summary: List payment link invoices
      operationId: listPaymentLinkInvoices
      parameters:
        - $ref: "#/components/parameters/PaymentLinkId"
        - $ref: "#/components/parameters/CursorLimit"
        - $ref: "#/components/parameters/CursorStartingAfter"
      responses:
        "200":
          description: Paginated invoices created from this payment link
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/CursorPage"
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/PaymentLinkInvoiceSummary"
        "404":
          $ref: "#/components/responses/NotFound"

  /v1/payment-links/{id}/transactions:
    get:
      tags: [Payment Links]
      summary: List payment link transactions
      operationId: listPaymentLinkTransactions
      parameters:
        - $ref: "#/components/parameters/PaymentLinkId"
        - $ref: "#/components/parameters/CursorLimit"
        - $ref: "#/components/parameters/CursorStartingAfter"
      responses:
        "200":
          description: Paginated transactions for invoices from this payment link
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/CursorPage"
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/PublicTransaction"
        "404":
          $ref: "#/components/responses/NotFound"

  /v1/public/payment-links/{public_id}:
    get:
      tags: [Payment Links]
      summary: Get buyer-facing payment link
      description: |
        Public endpoint for hosted pay-link pages. No API key required.
        Use the `plpub_` token from the merchant payment link `public_id` field.
      operationId: getPublicPaymentLink
      security: []
      parameters:
        - $ref: "#/components/parameters/PaymentLinkBuyerPublicId"
      responses:
        "200":
          description: Buyer-safe payment link details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicPaymentLinkBuyer"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"

  /v1/public/payment-links/{public_id}/checkout:
    post:
      tags: [Payment Links]
      summary: Start payment link checkout
      description: |
        Creates or reuses an invoice for this payment link and returns a hosted checkout URL.
        No API key required. Rate limited per buyer token.

        **Fixed amount:** omit `amount` when the link uses `fixed_amount` mode.

        **Customer-defined amount:** send `amount` as a decimal string within the link min/max.

        **Single-use links:** a second checkout reuses the open invoice when one exists;
        paid single-use links return `403`.
      operationId: startPublicPaymentLinkCheckout
      security: []
      parameters:
        - $ref: "#/components/parameters/PaymentLinkBuyerPublicId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PublicPaymentLinkCheckoutRequest"
            examples:
              fixedAmount:
                summary: Fixed amount link
                value:
                  origin_asset: "usdc.near"
                  refund_to: "alice.near"
              customerAmount:
                summary: Customer-defined amount
                value:
                  amount: "42.50"
                  origin_asset: "usdc.near"
                  refund_to: "alice.near"
      responses:
        "201":
          description: Checkout session created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicPaymentLinkCheckoutResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"

  /v1/integration/woocommerce/connect:
    post:
      tags: [Integrations]
      summary: Connect WooCommerce
      description: |
        Registers or reconnects a WooCommerce store. Returns a dedicated API key and
        webhook secret. Uses legacy error shape on some failures.
      operationId: wooConnect
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WooConnectRequest"
      responses:
        "200":
          description: WooCommerce connected
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WooConnectResponse"
        "400":
          description: Bad request (legacy error shape)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Forbidden (legacy error shape)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "409":
          description: Conflict (legacy error shape)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "422":
          description: Validation error (legacy error shape)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /v1/integration/woocommerce/disconnect:
    post:
      tags: [Integrations]
      summary: Disconnect WooCommerce
      description: Requires a WooCommerce dedicated API key.
      operationId: wooDisconnect
      responses:
        "200":
          description: WooCommerce disconnected
          content:
            application/json:
              schema:
                type: object
                required: [disconnected]
                properties:
                  disconnected:
                    type: boolean
                    example: true
        "403":
          description: Forbidden (legacy error shape)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Not found (legacy error shape)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /v1/integration/woocommerce/status:
    get:
      tags: [Integrations]
      summary: Get WooCommerce integration status
      operationId: wooStatus
      responses:
        "200":
          description: Integration status and health
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WooStatusResponse"
        "403":
          description: Forbidden (legacy error shape)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /v1/webhook-endpoints:
    post:
      tags: [Webhooks]
      summary: Create webhook endpoint
      description: |
        Creates a developer-managed webhook endpoint. The signing secret is returned
        once in the response and cannot be retrieved again.
      operationId: createWebhookEndpoint
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWebhookEndpointRequest"
            example:
              name: Production webhook
              url: https://api.example.com/webhooks/meum
              events:
                - invoice.created
                - invoice.paid
      responses:
        "201":
          description: Webhook endpoint created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointCreateResponse"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/UnprocessableEntity"

    get:
      tags: [Webhooks]
      summary: List webhook endpoints
      operationId: listWebhookEndpoints
      responses:
        "200":
          description: Webhook endpoints for the store
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/WebhookEndpoint"
        "403":
          $ref: "#/components/responses/Forbidden"

  /v1/webhook-endpoints/{id}:
    get:
      tags: [Webhooks]
      summary: Get webhook endpoint
      operationId: getWebhookEndpoint
      parameters:
        - $ref: "#/components/parameters/WebhookEndpointId"
      responses:
        "200":
          description: Webhook endpoint details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpoint"
        "404":
          $ref: "#/components/responses/NotFound"

    patch:
      tags: [Webhooks]
      summary: Update webhook endpoint
      description: Managed (integration-owned) webhooks cannot be modified via this API.
      operationId: updateWebhookEndpoint
      parameters:
        - $ref: "#/components/parameters/WebhookEndpointId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateWebhookEndpointRequest"
      responses:
        "200":
          description: Webhook endpoint updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpoint"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/UnprocessableEntity"

    delete:
      tags: [Webhooks]
      summary: Delete webhook endpoint
      description: Soft-deletes (archives) the endpoint. Managed webhooks cannot be deleted.
      operationId: deleteWebhookEndpoint
      parameters:
        - $ref: "#/components/parameters/WebhookEndpointId"
      responses:
        "200":
          description: Webhook endpoint deleted
          content:
            application/json:
              schema:
                type: object
                required: [deleted, id]
                properties:
                  deleted:
                    type: boolean
                    example: true
                  id:
                    type: string
                    pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /v1/webhook-endpoints/{id}/test:
    post:
      tags: [Webhooks]
      summary: Send test webhook
      description: Dispatches a `webhook.test` event to the endpoint. Rate limited.
      operationId: testWebhookEndpoint
      parameters:
        - $ref: "#/components/parameters/WebhookEndpointId"
      responses:
        "200":
          description: Test event queued
          content:
            application/json:
              schema:
                type: object
                required: [ok, event_id, event_type]
                properties:
                  ok:
                    type: boolean
                    example: true
                  event_id:
                    type: string
                    pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
                  event_type:
                    type: string
                    example: webhook.test
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"

  /v1/webhook-endpoints/{id}/deliveries:
    get:
      tags: [Webhooks]
      summary: List webhook deliveries
      operationId: listWebhookDeliveries
      parameters:
        - $ref: "#/components/parameters/WebhookEndpointId"
        - $ref: "#/components/parameters/CursorLimit"
        - $ref: "#/components/parameters/CursorStartingAfter"
      responses:
        "200":
          description: Recent delivery attempts for the endpoint
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/WebhookDeliverySummary"
        "404":
          $ref: "#/components/responses/NotFound"

  /v1/webhook-deliveries/{id}:
    get:
      tags: [Webhooks]
      summary: Get webhook delivery
      operationId: getWebhookDelivery
      parameters:
        - name: id
          in: path
          required: true
          description: Webhook delivery attempt ID (`whd_...` or UUID depending on API version).
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Delivery attempt details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookDeliveryDetail"
        "404":
          $ref: "#/components/responses/NotFound"

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        Store-scoped API key. Prefix with `sk_live_`.
        Example: `Authorization: Bearer sk_live_EXAMPLE_DO_NOT_USE`

  parameters:
    InvoiceId:
      name: id
      in: path
      required: true
      description: Invoice ID from create or list responses (starts with `inv_`).
      schema:
        type: string
        pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
    TransactionId:
      name: id
      in: path
      required: true
      description: Transaction ID from list responses (starts with `txn_`).
      schema:
        type: string
        pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
    PaymentLinkId:
      name: id
      in: path
      required: true
      description: Pay Link ID from create or list responses (starts with `plink_`).
      schema:
        type: string
        pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
    PaymentLinkBuyerPublicId:
      name: public_id
      in: path
      required: true
      description: |
        Public checkout token from the Pay Link's `public_id` field (starts with `plpub_`).
        Share this in checkout URLs; do not confuse with the merchant `plink_` ID.
      schema:
        type: string
        pattern: "^plpub_[0-9a-z]{20,32}$"
        example: plpub_c9a9bb730611077574c828af5af5fed1
    WebhookEndpointId:
      name: id
      in: path
      required: true
      description: Webhook endpoint public ID (`wh_...`).
      schema:
        type: string
        pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
    CursorLimit:
      name: limit
      in: query
      description: How many items to return per page (1–100). Defaults to `20` if omitted.
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
    CursorStartingAfter:
      name: starting_after
      in: query
      description: |
        Pagination cursor. Pass the ID of the last item from the previous page to get the next page.
        Omit on the first request.
      schema:
        type: string
        pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
    CursorEndingBefore:
      name: ending_before
      in: query
      description: |
        Reverse pagination. Pass the ID of the first item from the current page to get the previous page.
        Omit unless paginating backwards.
      schema:
        type: string
        pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      description: |
        **Optional.** Unique key (1–128 characters) so retrying the same request does not create duplicates.
        If you send the same key again, you get the original response. Omit if you do not need retry safety.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        example: pl_create_demo_001

  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PublicApiError"
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PublicApiError"
    Forbidden:
      description: Insufficient scope or permission
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PublicApiError"
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PublicApiError"
    UnprocessableEntity:
      description: Validation failed
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PublicApiError"
    RateLimited:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PublicApiError"
    ServiceUnavailable:
      description: Store or platform not ready to accept payments
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/PublicApiError"

  schemas:
    PublicApiError:
      type: object
      description: Standard error envelope for Public API v1 endpoints.
      required: [error]
      properties:
        error:
          type: object
          description: Error details for the failed request.
          required: [type, code, message, request_id]
          properties:
            type:
              type: string
              description: High-level error category used for programmatic handling.
              enum:
                - invalid_request
                - authentication_error
                - permission_error
                - not_found
                - conflict
                - rate_limit_error
                - api_error
              example: not_found
            code:
              type: string
              description: Stable machine-readable error code (for example `invoice_not_found`).
              example: invoice_not_found
            message:
              type: string
              description: Human-readable explanation suitable to show in logs or UI.
              example: Invoice not found
            param:
              type: string
              description: Request field associated with the error, when applicable.
              example: url
            request_id:
              type: string
              description: Unique request ID. Include when contacting support.
              example: req_a1b2c3d4e5f6789012345678
      example:
        error:
          type: not_found
          code: invoice_not_found
          message: Invoice not found
          request_id: req_a1b2c3d4e5f6789012345678

    ErrorResponse:
      type: object
      description: |
        Legacy flat error shape retained for backward compatibility on invoice create
        and WooCommerce integration endpoints.
      properties:
        error:
          type: string
          description: Human-readable error message.
          example: Payout wallet not configured
        code:
          type: string
          description: Machine-readable error code for legacy endpoints.
          example: EXTERNAL_ORDER_INTEGRATION_CONFLICT
        details:
          type: object
          additionalProperties:
            type: string
          description: Optional key-value context with field-level validation errors.

    CursorPage:
      type: object
      required: [data, has_more, next_cursor]
      properties:
        data:
          type: array
          description: Page of resources for the current request.
          items: {}
        has_more:
          type: boolean
          description: Whether additional pages exist after this one.
          example: true
        next_cursor:
          type: [string, "null"]
          format: uuid
          description: Pass as `starting_after` on the next request to fetch the following page. Null when `has_more` is false.
          example: "550e8400-e29b-41d4-a716-446655440000"

    HealthResponse:
      type: object
      properties:
        status:
          type: string
          description: Overall API health. Expect `ok` when the service is accepting traffic.
          example: ok
        service:
          type: string
          description: Optional process label. Do not depend on this value in integrations.
          example: meum-api
        near_mode:
          type: string
          enum: [mock, real]
          description: Blockchain integration mode (`real` in production).
        worker_heartbeat:
          type: [object, "null"]
          description: Background worker liveness signal, when available.
          properties:
            status:
              type: string
              description: Worker health status.
            last_at:
              type: string
              format: date-time
              description: Time of the last worker heartbeat (UTC).

    InvoiceStatus:
      type: string
      description: |
        Where the invoice is in the payment flow:
        - `pending`: created, not yet ready for payment
        - `quoted`: price quote ready; customer can proceed to pay
        - `awaiting_payment`: waiting for the customer to send funds
        - `paid`: payment received and confirmed
        - `underpaid`: customer paid less than the requested amount
        - `expired`: not paid before the deadline
        - `failed`: payment or processing failed
        - `refunded`: payment was returned to the customer
        - `cancelled`: invoice was cancelled before completion
      enum:
        - pending
        - quoted
        - awaiting_payment
        - paid
        - underpaid
        - expired
        - failed
        - refunded
        - cancelled

    CreateInvoiceRequest:
      type: object
      required: [external_order_id, amount]
      properties:
        store_id:
          type: string
          format: uuid
          description: |
            Which store this invoice belongs to. **Optional**: uses your API key's default store if omitted.
            Must match the store tied to your API key when provided.
        integration_id:
          type: string
          format: uuid
          description: |
            **Optional.** Link this invoice to a connected platform (e.g. WooCommerce).
            Omit unless you received this ID from a platform integration.
        external_order_id:
          type: string
          description: |
            **Required.** Your own order or reference number (e.g. `"order_1048"`).
            Sent back in webhooks so you can match payments to orders in your system.
          example: order_demo_1048
        amount:
          type: string
          description: |
            **Required.** How much you want to receive, as a decimal string (e.g. `"100.00"`).
            This is the amount after currency conversion, in the currency below.
          example: "100.00"
        currency:
          type: string
          default: USD
          description: |
            **Optional.** Three-letter currency code for `amount` (e.g. `USD`, `EUR`).
            Defaults to `USD` if omitted.
          example: USD
        callback_url:
          type: string
          format: uri
          description: |
            **Optional.** Legacy server callback URL. For new integrations, use Webhooks instead.
            Omit if you rely on webhooks.
        return_url:
          type: string
          format: uri
          description: |
            **Optional.** Where to send the customer after they finish or cancel checkout.
            Omit if you handle completion via webhooks only.
        metadata:
          type: object
          additionalProperties: true
          description: |
            **Optional.** Custom data attached to the invoice (e.g. `{"cart_id": "abc"}`).
            Returned in webhooks. Omit if you don't need extra fields.

    InvoiceCreateResponse:
      type: object
      properties:
        id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: Unique invoice ID starting with `inv_`. Save this to check status or handle webhooks.
        status:
          $ref: "#/components/schemas/InvoiceStatus"
        checkout_url:
          type: string
          format: uri
          description: |
            Send your customer to this URL to pay (e.g. `https://pay.meum.io/...`).
            Open in a browser or redirect from your site.
          example: https://pay.meum.io/550e8400-e29b-41d4-a716-446655440000
        payment_url:
          type: string
          format: uri
          description: Same as `checkout_url`. Kept for older integrations; prefer `checkout_url`.
        amount:
          type: string
          description: Invoice amount as a decimal string (e.g. `"100.00"`).
          example: "100.00"
        currency:
          type: string
          description: Currency code for `amount` (e.g. `USD`).
          example: USD
        output_asset:
          type: string
          description: Stablecoin you receive when paid (e.g. `USDC`).
          example: USDC
        expires_at:
          type: string
          format: date-time
          description: When this invoice stops accepting payment if still unpaid (UTC, e.g. `2025-09-01T14:31:44.171Z`).

    PublicInvoice:
      type: object
      description: Full invoice record returned by list and get endpoints.
      properties:
        id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: Unique invoice ID (`inv_...`).
        external_order_id:
          type: string
          description: Your order reference from when the invoice was created.
          example: order_demo_1048
        status:
          $ref: "#/components/schemas/InvoiceStatus"
        amount:
          type: string
          description: Amount you requested to receive (decimal string, e.g. `"100.00"`).
          example: "100.00"
        currency:
          type: string
          description: Currency of `amount` (e.g. `USD`).
          example: USD
        order_amount:
          type: string
          description: Original order total before conversion, if different from `amount`.
          example: "100.00"
        order_currency:
          type: string
          description: Currency of `order_amount`.
          example: USD
        paid_amount:
          type: [string, "null"]
          description: How much the customer actually paid. `null` until payment starts.
          example: "100.00"
        output_asset:
          type: string
          description: Stablecoin paid out to you (e.g. `USDC`).
          example: USDC
        checkout_url:
          type: [string, "null"]
          format: uri
          description: Payment page URL while the invoice can still be paid. `null` if no longer payable.
        return_url:
          type: [string, "null"]
          format: uri
          description: Where the customer is redirected after checkout, if you set one at creation.
        customer_id:
          type: [string, "null"]
          format: uuid
          description: Internal customer record ID, if linked. Usually not needed for basic integrations.
        payment_link_id:
          type: [string, "null"]
          format: uuid
          description: Pay Link that created this invoice, if applicable.
        transaction_hash:
          type: [string, "null"]
          description: Blockchain transaction ID after payment is confirmed. `null` until paid.
        metadata:
          type: [object, "null"]
          additionalProperties: true
          description: Custom data you attached when creating the invoice.
        created_at:
          type: string
          format: date-time
          description: When the invoice was created (UTC).
        updated_at:
          type: string
          format: date-time
          description: When the invoice was last updated (UTC).
        paid_at:
          type: [string, "null"]
          format: date-time
          description: When payment was confirmed. `null` until the invoice is paid.
        expires_at:
          type: string
          format: date-time
          description: Deadline to pay before the invoice expires (UTC).

    InvoiceStatusResponse:
      type: object
      description: Lightweight status for polling. Use this to check if a customer has paid.
      properties:
        id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: Invoice ID (`inv_...`).
        status:
          $ref: "#/components/schemas/InvoiceStatus"
        swap_status:
          type: [string, "null"]
          enum: [processing]
          description: |
            `processing`: customer paid; funds are being converted and sent to you.
            Only present during this step; omit or null otherwise.
          example: processing
        paid_amount:
          type: [string, "null"]
          description: What the customer sent (e.g. `"100.00"`). `null` if not paid yet.
          example: "100.00"
        received_amount:
          type: [string, "null"]
          description: What lands in your wallet after fees (e.g. `"99.50"`). `null` until settled.
          example: "99.50"
        difference_amount:
          type: [string, "null"]
          description: "Difference between received_amount and paid_amount (e.g. \"-0.50\" for fees). null if not paid."
          example: "-0.50"
        transaction_hash:
          type: [string, "null"]
          description: Blockchain transaction ID once payment is confirmed.
        paid_at:
          type: [string, "null"]
          format: date-time
          description: When payment was confirmed (UTC). `null` if unpaid.
        deposit_address:
          type: [string, "null"]
          description: Address shown to the customer during checkout to send payment.
        deposit_memo:
          type: [string, "null"]
          description: Extra reference the customer must include with payment, if required by the network.
        return_url:
          type: [string, "null"]
          format: uri
          description: Redirect URL after checkout, if configured.

    PublicTransaction:
      type: object
      description: A payment, payout, or refund movement tied to an invoice.
      properties:
        id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: Transaction ID (`txn_...`).
        invoice_id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: Invoice this transaction belongs to (`inv_...`).
        type:
          type: string
          enum: [payment_origin, settlement, refund]
          description: |
            What kind of movement this is:
            - `payment_origin`: money the customer sent
            - `settlement`: money paid out to your wallet
            - `refund`: money returned to the customer
          example: payment_origin
        network:
          type: [string, "null"]
          description: Blockchain used (e.g. `near`, `ethereum`). `null` if not on-chain.
          example: near
        token:
          type: [string, "null"]
          description: Token involved (e.g. `USDC`).
          example: USDC
        amount:
          type: [string, "null"]
          description: Amount moved, as a decimal string (e.g. `"100.00"`).
          example: "100.00"
        transaction_hash:
          type: string
          description: On-chain transaction ID (block explorer lookup).
          example: "0xabc123def4567890abcdef1234567890abcdef1234567890abcdef1234567890"
        from_wallet:
          type: [string, "null"]
          description: Wallet that sent the funds.
        to_wallet:
          type: [string, "null"]
          description: Wallet that received the funds.
        confirmations:
          type: [integer, "null"]
          description: How many blocks have confirmed this transaction. `null` if not tracked.
        status:
          type: string
          description: Current state (for example `pending`, `confirmed`).
          example: confirmed
        verified:
          type: boolean
          description: "true when the transaction has been verified on the blockchain."
        confirmed_at:
          type: [string, "null"]
          format: date-time
          description: When enough confirmations were reached (UTC). `null` if still pending.
        created_at:
          type: string
          format: date-time
          description: When this record was created (UTC).
        updated_at:
          type: string
          format: date-time
          description: When this record was last updated (UTC).

    PaymentLinkAmountMode:
      type: string
      description: |
        How pricing works on this Pay Link:
        - `fixed_amount`: you set the price; the customer pays exactly that amount
        - `customer_defined_amount`: the customer chooses how much to pay (within min/max you set)
      enum: [fixed_amount, customer_defined_amount]

    PaymentLinkStatus:
      type: string
      description: |
        Whether the Pay Link can accept payments right now:
        - `active`: open for payments
        - `inactive`: turned off by you; no new payments
        - `expired`: past its expiration date
        - `completed`: single-use link already paid, or usage limit reached
      enum: [active, inactive, expired, completed]

    PaymentLinkStats:
      type: object
      description: Aggregated payment metrics for a Pay Link.
      properties:
        total_payments:
          type: string
          description: Total checkout attempts or invoices created from this link.
          example: "12"
        paid_payments:
          type: string
          description: Number of successfully paid invoices from this link.
          example: "10"
        total_volume:
          type: string
          description: Sum of paid amounts in the link's currency (decimal string).
          example: "250.00"

    PublicPaymentLinkBuyer:
      type: object
      description: Buyer-safe payment link view (no store UUIDs or webhook config).
      properties:
        public_id:
          type: string
          pattern: "^plpub_[0-9a-z]{20,32}$"
          description: Public token for this Pay Link (`plpub_...`). Used in checkout URLs.
        title:
          type: string
          description: Display name shown to payers on the checkout page.
        description:
          type: [string, "null"]
          description: Optional longer description or instructions for payers.
        image_url:
          type: [string, "null"]
          format: uri
          description: Hero or product image URL shown on checkout.
        amount_mode:
          $ref: "#/components/schemas/PaymentLinkAmountMode"
        amount:
          type: [string, "null"]
          description: Fixed price when `amount_mode` is `fixed_amount` (decimal string).
          example: "25.00"
        currency:
          type: string
          description: Currency for all amounts on this link (e.g. `USD`).
          example: USD
        min_amount:
          type: [string, "null"]
          description: Minimum amount payers can enter when `amount_mode` is `customer_defined_amount`.
        max_amount:
          type: [string, "null"]
          description: Maximum amount payers can enter when `amount_mode` is `customer_defined_amount`.
        suggested_amounts:
          type: array
          items:
            type: string
          description: Preset amount buttons shown on checkout for variable-amount links.
        usage:
          type: string
          enum: [reusable, single_use]
          description: |
            - `reusable`: multiple payers or repeat payments allowed (subject to link settings)
            - `single_use`: one successful payment completes the link
        status:
          $ref: "#/components/schemas/PaymentLinkStatus"
        store_name:
          type: string
          description: Store display name shown to payers.
        merchant_name:
          type: string
          description: Merchant business name shown to payers.
        store_logo_url:
          type: [string, "null"]
          format: uri
          deprecated: true
          description: Omitted on buyer API; use `image_url` when set on the link.

    PublicPaymentLinkCheckoutRequest:
      type: object
      required: [origin_asset, refund_to]
      properties:
        amount:
          type: string
          description: |
            How much to charge the customer, as a decimal string (e.g. `"42.50"`).
            **Required** when the Pay Link uses `customer_defined_amount`.
            **Omit** when the link has a fixed price; the link's amount is used automatically.
          example: "42.50"
        origin_asset:
          type: string
          description: |
            **Required.** Which crypto the customer will pay with.
            Format: `token.network`: e.g. `usdc.near` means USDC on NEAR.
          example: usdc.near
        refund_to:
          type: string
          description: |
            **Required.** Wallet address to send refunds to if payment fails or the customer overpays.
            Must be valid on the same network as `origin_asset` (e.g. `alice.near` for NEAR).
          example: alice.near

    PublicPaymentLinkCheckoutResponse:
      type: object
      required: [invoice_id, checkout_url, reused]
      properties:
        invoice_id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: New or existing invoice for this checkout (`inv_...`). Use to track payment status.
        checkout_url:
          type: string
          format: uri
          description: Open this URL in a browser to show the payment page to the customer.
        reused:
          type: boolean
          description: |
            `true`: an unpaid invoice from a previous attempt was reused (common for single-use links).
            `false`: a brand-new invoice was created.

    PaymentLink:
      type: object
      description: Merchant-facing Pay Link resource with configuration and stats.
      properties:
        id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: Merchant resource ID (`plink_...`). Use in API paths and webhooks.
        public_id:
          type: string
          pattern: "^plpub_[0-9a-z]{20,32}$"
          description: Buyer-facing token (`plpub_...`) embedded in public checkout URLs.
        public_url:
          type: string
          format: uri
          description: Shareable checkout page URL for this link.
          example: https://pay.meum.io/plpub_c9a9bb730611077574c828af5af5fed1
        title:
          type: string
          description: Link title shown on checkout and in the merchant dashboard.
          example: Summer fundraiser
        description:
          type: [string, "null"]
          description: Optional description for payers.
        image_url:
          type: [string, "null"]
          format: uri
          description: Optional image URL displayed on the checkout page.
        amount_mode:
          $ref: "#/components/schemas/PaymentLinkAmountMode"
        amount:
          type: [string, "null"]
          description: Fixed price when `amount_mode` is `fixed_amount`.
          example: "25.00"
        currency:
          type: string
          description: Currency for all amounts on this link.
          example: USD
        min_amount:
          type: [string, "null"]
          description: Minimum payer-entered amount for variable-amount links.
        max_amount:
          type: [string, "null"]
          description: Maximum payer-entered amount for variable-amount links.
        suggested_amounts:
          type: array
          items:
            type: number
          description: Suggested preset amounts shown as quick-select buttons on checkout.
          example: [10, 25, 50]
        allow_multiple_payments:
          type: boolean
          description: When true, the link can generate multiple paid invoices over its lifetime.
        public_visible:
          type: boolean
          description: When true, the link is reachable via its public URL without extra auth.
        status:
          $ref: "#/components/schemas/PaymentLinkStatus"
        effective_status:
          $ref: "#/components/schemas/PaymentLinkStatus"
          description: Computed status accounting for expiry and usage limits.
        expires_at:
          type: [string, "null"]
          format: date-time
          description: After this time (UTC) the link no longer accepts payments. `null` if it never expires.
        created_at:
          type: string
          format: date-time
          description: When the link was created.
        updated_at:
          type: string
          format: date-time
          description: When the link was last modified.
        stats:
          $ref: "#/components/schemas/PaymentLinkStats"

    CreatePaymentLinkRequest:
      type: object
      required: [title, amount_mode]
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 200
          description: |
            **Required.** Name shown on the checkout page and in your dashboard (1–200 characters).
            Example: `"Summer fundraiser"`.
        description:
          type: string
          maxLength: 2000
          description: |
            **Optional.** Longer text for payers (max 2000 characters). Omit if not needed.
        image_url:
          type: [string, "null"]
          maxLength: 2048
          description: |
            **Optional.** HTTPS image URL for checkout (logo or product photo). Omit for no image.
        amount_mode:
          $ref: "#/components/schemas/PaymentLinkAmountMode"
        amount:
          type: [string, "null"]
          description: |
            **Required when `amount_mode` is `fixed_amount`.** Price as a decimal string (e.g. `"25.00"`).
            Omit when customers choose their own amount.
          example: "25.00"
        min_amount:
          type: [string, "null"]
          description: |
            **Optional.** Lowest amount a customer can pay when `amount_mode` is `customer_defined_amount`.
            Omit to allow any positive amount (subject to platform limits).
        max_amount:
          type: [string, "null"]
          description: |
            **Optional.** Highest amount a customer can pay when `amount_mode` is `customer_defined_amount`.
            Omit for no upper cap (subject to platform limits).
        suggested_amounts:
          type: array
          items:
            type: [number, "null"]
            exclusiveMinimum: 0
          description: |
            **Optional.** Quick-pick buttons on checkout (e.g. `[10, 25, 50]`). Omit for a free-form amount field only.
        allow_multiple_payments:
          type: boolean
          default: true
          description: |
            **Optional.** `true` (default): many customers can pay through this link.
            `false`: treat as one-time use after the first successful payment.
        public_visible:
          type: boolean
          default: true
          description: |
            **Optional.** `true` (default): anyone with the link URL can pay.
            `false`: hide from public checkout (API-only use).
        expires_at:
          type: [string, "null"]
          format: date-time
          description: |
            **Optional.** When the link stops accepting payments (UTC, e.g. `2025-09-01T14:31:44.171Z`).
            Omit for no expiration.
        success_url:
          type: string
          format: uri
          description: |
            **Optional.** Where to send the customer after a successful payment.
            Only works if redirect URLs are enabled for Pay Links on your account.
        metadata:
          type: object
          additionalProperties: true
          description: |
            **Optional.** Custom key-value data stored on the link and copied to invoices from it.
            Omit if not needed.

    UpdatePaymentLinkRequest:
      type: object
      description: Send only the fields you want to change; others stay unchanged.
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 200
          description: New checkout title. Omit to keep the current title.
        description:
          type: [string, "null"]
          maxLength: 2000
          description: New description, or `null` to remove it. Omit to keep unchanged.
        image_url:
          type: [string, "null"]
          maxLength: 2048
          description: New image URL, or `null` to remove. Omit to keep unchanged.
        amount_mode:
          $ref: "#/components/schemas/PaymentLinkAmountMode"
        amount:
          type: [string, "null"]
          description: New fixed price (e.g. `"25.00"`) when using `fixed_amount`. Omit to keep unchanged.
        min_amount:
          type: [string, "null"]
          description: New minimum for variable-amount links. Omit to keep unchanged.
        max_amount:
          type: [string, "null"]
          description: New maximum for variable-amount links. Omit to keep unchanged.
        suggested_amounts:
          type: array
          items:
            type: [number, "null"]
            exclusiveMinimum: 0
          description: Replace quick-pick amounts entirely. Omit to keep the current list.
        allow_multiple_payments:
          type: boolean
          description: Turn multiple payments on or off. Omit to keep unchanged.
        public_visible:
          type: boolean
          description: Show or hide the public checkout URL. Omit to keep unchanged.
        expires_at:
          type: [string, "null"]
          format: date-time
          description: New expiration (UTC), or `null` to remove expiry. Omit to keep unchanged.
        success_url:
          type: string
          format: uri
          description: |
            New redirect URL after successful payment. Requires redirect URLs to be enabled on your account.
            Omit to keep the current URL.
        metadata:
          type: object
          additionalProperties: true
          description: Replace all custom metadata. Omit to keep unchanged.

    PaymentLinkInvoiceSummary:
      type: object
      description: Invoice row returned from Pay Link invoice listing.
      properties:
        id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: Invoice public ID (`inv_...`).
        status:
          $ref: "#/components/schemas/InvoiceStatus"
        amount:
          type: string
          description: Invoice amount in the link currency.
          example: "25.00"
        currency:
          type: string
          description: Currency code (e.g. `USD`).
          example: USD
        created_at:
          type: string
          format: date-time
          description: When the invoice was created from this link.
        paid_at:
          type: [string, "null"]
          format: date-time
          description: When payment was confirmed. Null if unpaid.
        payment_link:
          type: [object, "null"]
          description: Nested Pay Link summary when included in the response.
          properties:
            id:
              type: string
              pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
              description: Pay Link merchant ID (`plink_...`).
            title:
              type: string
              description: Link title.
            public_id:
              type: string
              description: Public checkout token (`plpub_...`).

    WebhookEndpointStatus:
      type: string
      description: |
        Webhook endpoint state:
        - `ACTIVE`: deliveries are sent
        - `DISABLED`: paused by merchant
        - `ARCHIVED`: permanently retired
      enum: [ACTIVE, DISABLED, ARCHIVED]

    WebhookEndpoint:
      type: object
      description: Registered HTTPS endpoint that receives signed webhook events.
      properties:
        id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: Webhook endpoint ID (`wh_...`).
        name:
          type: string
          description: Human-readable label in the merchant dashboard.
          example: Production webhook
        url:
          type: string
          format: uri
          description: HTTPS URL that receives POST requests for subscribed events.
        secret_prefix:
          type: string
          description: First characters of the signing secret (for identification only).
          example: a1b2c3d4
        events:
          type: array
          items:
            type: string
          description: Event types this endpoint is subscribed to.
          example:
            - invoice.created
            - invoice.paid
            - invoice.expired
            - invoice.failed
        status:
          $ref: "#/components/schemas/WebhookEndpointStatus"
        managed:
          type: boolean
          description: True when owned by an integration (for example WooCommerce) and not fully editable in the dashboard.
        last_delivery_at:
          type: [string, "null"]
          format: date-time
          description: Timestamp of the most recent delivery attempt.
        last_delivery_status:
          type: [string, "null"]
          description: Outcome of the last delivery (for example `success`, `failed`).
        failure_count:
          type: integer
          description: Consecutive or recent failed delivery count, depending on endpoint policy.
        created_at:
          type: string
          format: date-time
          description: When the endpoint was registered.

    WebhookEndpointCreateResponse:
      type: object
      description: Response immediately after creating a webhook endpoint. The signing secret is shown only once.
      properties:
        id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: Webhook endpoint ID (`wh_...`).
        name:
          type: string
          description: Endpoint display name.
        url:
          type: string
          format: uri
          description: Destination URL for webhook deliveries.
        secret:
          type: string
          description: Signing secret. Store this securely; it is shown only at creation.
          example: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
        secret_prefix:
          type: string
          description: Prefix of the secret for later identification.
          example: "01234567"
        events:
          type: array
          items:
            type: string
          description: Subscribed event types.
        message:
          type: string
          description: Reminder to save the secret before leaving the creation flow.
          example: Save this secret now. It will not be shown again.

    CreateWebhookEndpointRequest:
      type: object
      required: [url]
      properties:
        name:
          type: string
          minLength: 1
          default: Default
          description: |
            **Optional.** Label for this endpoint in your dashboard. Defaults to `"Default"` if omitted.
        url:
          type: string
          format: uri
          description: |
            **Required.** HTTPS URL on your server that accepts POST requests.
            Must respond with HTTP 2xx when delivery succeeds.
        events:
          type: array
          items:
            type: string
          description: |
            **Optional.** Which events to send (e.g. `invoice.paid`, `invoice.expired`).
            Defaults to standard invoice lifecycle events if omitted.

    UpdateWebhookEndpointRequest:
      type: object
      description: Partial update for a webhook endpoint.
      properties:
        name:
          type: string
          minLength: 1
          description: Updated display name.
        url:
          type: string
          format: uri
          description: New destination URL for deliveries.
        events:
          type: array
          items:
            type: string
          description: Replace the list of subscribed event types.
        status:
          type: string
          enum: [ACTIVE, DISABLED]
          description: Set to `DISABLED` to pause deliveries without deleting the endpoint.

    WebhookDeliverySummary:
      type: object
      description: Compact record of a single webhook delivery attempt.
      properties:
        id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: Delivery attempt ID (`whd_...`).
        event_id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: ID of the webhook event that was delivered.
        attempt_number:
          type: integer
          description: Retry attempt number (1 for the first try).
        status_code:
          type: [integer, "null"]
          description: HTTP status code returned by your server, if a response was received.
          example: 200
        attempted_at:
          type: string
          format: date-time
          description: When this delivery attempt was made (UTC).

    WebhookDeliveryDetail:
      type: object
      description: Full delivery record including response body snippet for debugging.
      properties:
        id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: Delivery attempt ID (`whd_...`).
        endpoint_id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: Webhook endpoint that received this delivery.
        event_id:
          type: string
          pattern: "^(inv_|txn_|plink_|wh_|whd_|evt_|int_)[0-9a-z]{20,32}$"
          description: Underlying webhook event ID.
        event_type:
          type: string
          description: Event name (for example `invoice.paid`).
          example: invoice.paid
        attempt_number:
          type: integer
          description: Retry attempt number for this event.
        status_code:
          type: [integer, "null"]
          description: HTTP status code from your server.
        response_body_truncated:
          type: [string, "null"]
          description: Truncated response body from your server, for debugging failed deliveries.
        attempted_at:
          type: string
          format: date-time
          description: When the delivery was attempted.
        event_created_at:
          type: string
          format: date-time
          description: When the webhook event was originally created.

    WooConnectRequest:
      type: object
      required: [webhook_url, site_url]
      properties:
        webhook_url:
          type: string
          format: uri
          description: |
            **Required.** URL on your WordPress site that receives payment updates from Meum.
            Usually: `https://your-store.com/wp-json/meum/v1/webhook`
          example: https://shop.example.com/wp-json/meum/v1/webhook
        site_url:
          type: string
          format: uri
          description: |
            **Required.** Your store's public homepage URL (used to identify this WooCommerce site).
          example: https://shop.example.com
        plugin_version:
          type: string
          description: |
            **Optional.** Meum WooCommerce plugin version (e.g. `"1.2.0"`). Helps support diagnose issues.
          example: "1.2.0"
        wordpress_version:
          type: string
          description: "**Optional.** WordPress version (e.g. \"6.5\"). Omit if unknown."
          example: "6.5"
        woocommerce_version:
          type: string
          description: "**Optional.** WooCommerce version (e.g. \"8.9\"). Omit if unknown."
          example: "8.9"

    WooConnectResponse:
      type: object
      description: Credentials returned after a successful WooCommerce connection handshake.
      properties:
        store_id:
          type: string
          format: uuid
          description: Meum store UUID linked to this site.
        store_name:
          type: string
          description: Store display name in Meum.
          example: Demo Store
        integration_id:
          type: string
          format: uuid
          description: Integration UUID for this WooCommerce installation.
        installation_id:
          type: string
          description: Unique installation identifier for support and rotation.
          example: inst_a1b2c3d4e5f678901234
        site_url:
          type: string
          format: uri
          description: Connected storefront URL.
        api_key:
          type: string
          description: Merchant API key for server-side calls (`sk_live_...`). Store securely.
          example: sk_live_EXAMPLE_DO_NOT_USE
        webhook_secret:
          type: string
          description: Secret for verifying inbound webhooks from Meum to WordPress.
          example: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
        webhook_url:
          type: string
          format: uri
          description: Registered webhook destination on the WordPress site.
        webhook_id:
          type: string
          format: uuid
          description: Meum webhook endpoint UUID for this integration.
        status:
          type: string
          description: Connection status (for example `ACTIVE`).
          example: ACTIVE
        credentials_rotated:
          type: boolean
          description: True when this response re-issued credentials for an existing installation.
        payout_configured:
          type: boolean
          description: False until a payout wallet is configured for the store.
        checkout_base_url:
          type: string
          format: uri
          description: Base URL for hosted checkout links.
          example: https://pay.meum.io
        warning:
          type: string
          description: Present when payout wallet is not configured; payments may be blocked until resolved.

    WooStatusResponse:
      type: object
      description: Health and connection status for a WooCommerce integration.
      properties:
        status:
          type: string
          description: Overall integration status.
          example: CONNECTED
        connection_status:
          type: string
          description: Whether the plugin has completed the connect flow.
          example: CONNECTED
        health_status:
          type: string
          description: Operational health (for example `HEALTHY`, `DEGRADED`).
          example: HEALTHY
        issues:
          type: array
          items:
            type: string
          description: Human-readable list of problems requiring merchant action.
        integration_id:
          type: [string, "null"]
          format: uuid
          description: Integration UUID when connected.
        site_url:
          type: [string, "null"]
          format: uri
          description: Connected WordPress site URL.
        plugin_version:
          type: [string, "null"]
          description: Meum plugin version reported by the site.
        wordpress_version:
          type: [string, "null"]
          description: WordPress version reported by the site.
        woocommerce_version:
          type: [string, "null"]
          description: WooCommerce version reported by the site.
        credential_status:
          type: [string, "null"]
          description: Whether API credentials are valid and active.
        credential_prefix:
          type: [string, "null"]
          description: Prefix of the active API key for identification.
        webhook_health:
          type: [string, "null"]
          description: Recent webhook delivery health for the integration.
        webhook_url:
          type: [string, "null"]
          format: uri
          description: Configured WordPress webhook URL.
        last_seen_at:
          type: [string, "null"]
          format: date-time
          description: Last time the plugin checked in with Meum.
        pending_connection_key:
          type: [object, "null"]
          description: Temporary connect key when onboarding is in progress.
          properties:
            key_prefix:
              type: string
              description: Prefix of the pending connection key.
            expires_at:
              type: [string, "null"]
              format: date-time
              description: When the pending key expires if connect is not completed.
        connected:
          type: boolean
          description: True when the site is fully connected and ready to accept payments.
