openapi: 3.0.3
info:
  title: ND8 API
  description: >
    **Payments:** hosted checkout (`POST /checkout/create/` on the API base,
    e.g. `…/api/checkout/create/`), get payment by id (`GET
    /transactions/{id}/`), create refund (`POST /transactions/{id}/refund/`),
    list/get refunds (`GET /refunds/`, `GET /refunds/{id}/`).

    Charge currencies: **USD** and **EUR** only.


    **Merchant (dashboard session):** organization profile, API keys, webhooks,
    and **payment links**

    (payment links are not available with API keys—use `Authorization: Token`
    from the merchant web app).


    **Subscriptions:** lifecycle (create, update, cancel, charges) is managed in
    the merchant dashboard; subscription REST endpoints for API keys are not
    part of this reference until published.


    **Webhooks:** configure under Organization in the dashboard; events such as
    `transaction.status_changed` and `refund.status_changed` are documented in
    the product guides.
  version: 1.1.0
  contact:
    name: ND8
servers:
  - url: https://api.sandbox.nd8.com/api
    description: Production
  - url: https://api.sandbox.nd8.com/api
    description: Sandbox
tags:
  - name: Create payment
    description: Start a payment (hosted checkout session); **USD** or **EUR**
  - name: Get payment
    description: Retrieve payment by ID (amount, status, etc.)
  - name: Refunds
    description: Create and retrieve refunds for payments (API key)
  - name: Organization
    description: >-
      Merchant organization, dashboard snapshot, API keys, webhooks (requires
      **Token** session)
  - name: Payment links
    description: Payment links — **dashboard only** (Token); not for API keys
paths:
  /transactions/{id}/:
    get:
      summary: Get payment by ID
      description: >-
        Retrieve a payment by its public ID (e.g. TXabc123) or numeric ID.
        Returns amount, status, currency, products, and timestamps. Requires API
        key.
      operationId: getPaymentById
      tags:
        - Get payment
      security:
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Payment ID — use public ID (e.g. TXabc123) or numeric transaction ID
          schema:
            type: string
      responses:
        '200':
          description: Payment details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Payment'
        '401':
          description: Invalid or missing API key
        '404':
          description: Payment not found
  /transactions/{id}/refund/:
    post:
      summary: Create refund for payment
      description: >
        Request a full or partial refund for a paid transaction. Requires API
        key with `refunds:write` or `payments:write` scope.

        No 2FA is required for API requests (unlike the merchant dashboard).
        Partial refunds are supported via `amount`.

        Refunds are subject to the same eligibility rules as the dashboard (paid
        status, wallet balance, provider limits). Partial refunds are supported
        when wallet balance is below the full payment amount.

        On success the refund status is `processing` while Inflow processes the
        request. Subscribe to `refund.status_changed` webhooks for updates.
      operationId: createRefundForPayment
      tags:
        - Refunds
      security:
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          description: Payment ID — public ID (e.g. TXabc123) or numeric transaction ID
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefundCreateRequest'
      responses:
        '200':
          description: Refund accepted and sent to provider
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RefundCreateResponse'
        '400':
          description: Validation or eligibility error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Invalid or missing API key
        '403':
          description: Missing required scope
        '404':
          description: Payment not found
  /refunds/:
    get:
      summary: List refunds
      description: >-
        List refunds for the organization. Requires API key with `refunds:read`
        or `payments:read` scope.
      operationId: listRefunds
      tags:
        - Refunds
      security:
        - ApiKeyAuth: []
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum:
              - pending
              - processing
              - completed
              - failed
              - rejected
        - name: created_at__gte
          in: query
          schema:
            type: string
            format: date-time
        - name: created_at__lte
          in: query
          schema:
            type: string
            format: date-time
        - name: search
          in: query
          schema:
            type: string
          description: Search payment ID or refund reason
        - name: ordering
          in: query
          schema:
            type: string
          description: Sort field, e.g. -created_at, amount
        - name: page
          in: query
          schema:
            type: integer
        - name: page_size
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Refund list
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Refund'
        '401':
          description: Invalid or missing API key
  /refunds/{id}/:
    get:
      summary: Get refund by ID
      description: >-
        Retrieve a refund by public ID (e.g. RFabc123) or numeric ID. Requires
        `refunds:read` or `payments:read`.
      operationId: getRefundById
      tags:
        - Refunds
      security:
        - ApiKeyAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Refund details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Refund'
        '401':
          description: Invalid or missing API key
        '404':
          description: Refund not found
  /checkout/create/:
    post:
      summary: Create payment (hosted checkout)
      description: >-
        Create a payment session (hosted checkout). Returns checkout_url and
        token. Redirect the customer to checkout_url to complete payment.
        Requires API key with checkout:create scope.
      operationId: createPayment
      tags:
        - Create payment
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CheckoutCreateRequest'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheckoutCreateResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Invalid or missing API key
  /organization/:
    get:
      summary: Get organization (merchant session)
      description: >-
        Returns the current user's organization profile. Requires dashboard
        session token.
      operationId: getOrganization
      tags:
        - Organization
      security:
        - TokenAuth: []
      responses:
        '200':
          description: Organization
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          description: Not authenticated
    patch:
      summary: Update organization (merchant session)
      description: >-
        Partial update of editable organization fields (e.g. name, website,
        contact_email, telegram, price_mode).
      operationId: patchOrganization
      tags:
        - Organization
      security:
        - TokenAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: Updated organization
        '401':
          description: Not authenticated
  /organization/dashboard/:
    get:
      summary: Organization dashboard snapshot
      description: Stats and wallet summary for the merchant dashboard.
      operationId: getOrganizationDashboard
      tags:
        - Organization
      security:
        - TokenAuth: []
      responses:
        '200':
          description: Dashboard payload
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          description: Not authenticated
  /organization/api-keys/:
    get:
      summary: List current API key metadata
      operationId: getOrganizationApiKeys
      tags:
        - Organization
      security:
        - TokenAuth: []
      responses:
        '200':
          description: API key metadata (no raw secret except on create/rotate)
    post:
      summary: Create organization API key
      operationId: createOrganizationApiKey
      tags:
        - Organization
      security:
        - TokenAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                allowed_scopes:
                  type: string
      responses:
        '201':
          description: Created; includes raw_key once
  /organization/api-keys/rotate/:
    post:
      summary: Rotate API key
      operationId: rotateOrganizationApiKey
      tags:
        - Organization
      security:
        - TokenAuth: []
      responses:
        '200':
          description: New key; includes raw_key once
  /organization/webhook/:
    get:
      summary: Get webhook configuration
      operationId: getOrganizationWebhook
      tags:
        - Organization
      security:
        - TokenAuth: []
      responses:
        '200':
          description: Webhook endpoint preview and metadata
    put:
      summary: Set webhook endpoint URL
      operationId: putOrganizationWebhook
      tags:
        - Organization
      security:
        - TokenAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - endpoint_url
              properties:
                endpoint_url:
                  type: string
                  format: uri
      responses:
        '200':
          description: Updated webhook config
  /payment-links/:
    get:
      summary: List payment links
      description: Merchant-only. Not available with API key authentication.
      operationId: listPaymentLinks
      tags:
        - Payment links
      security:
        - TokenAuth: []
      responses:
        '200':
          description: List of payment links
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
        '401':
          description: Not authenticated
    post:
      summary: Create payment link
      description: >
        Create a shareable link with a product snapshot. Currency must be
        **USD** or **EUR**.

        Requires catalog product ids from your organization.
      operationId: createPaymentLink
      tags:
        - Payment links
      security:
        - TokenAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaymentLinkCreateRequest'
      responses:
        '201':
          description: Created payment link
        '400':
          description: Validation error
        '401':
          description: Not authenticated
  /payment-links/{id}/:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Get payment link
      operationId: getPaymentLink
      tags:
        - Payment links
      security:
        - TokenAuth: []
      responses:
        '200':
          description: Payment link detail and dashboard stats
    patch:
      summary: Update payment link
      description: Update name, is_active, expires_at, max_uses, metadata.
      operationId: patchPaymentLink
      tags:
        - Payment links
      security:
        - TokenAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: Updated
    delete:
      summary: Delete payment link (soft)
      operationId: deletePaymentLink
      tags:
        - Payment links
      security:
        - TokenAuth: []
      responses:
        '204':
          description: Deleted / deactivated
  /payment-links/{id}/payments/:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: List payments for a payment link
      operationId: listPaymentLinkPayments
      tags:
        - Payment links
      security:
        - TokenAuth: []
      responses:
        '200':
          description: Transactions associated with this link
  /payment-links/public/{token}/:
    parameters:
      - name: token
        in: path
        required: true
        schema:
          type: string
          format: uuid
        description: Payment link id (UUID) shared with the customer
    get:
      summary: Public payment link preview (customer)
      description: No authentication. Returns 404/410 when unavailable.
      operationId: getPublicPaymentLink
      tags:
        - Payment links
      security: []
      responses:
        '200':
          description: Link preview
        '404':
          description: Not found
        '410':
          description: Gone (inactive, expired, or max uses)
  /payment-links/public/{token}/start/:
    parameters:
      - name: token
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      summary: Start checkout from payment link (customer)
      description: >
        Creates a hosted checkout session from the link snapshot. **USD** or
        **EUR**.

        No merchant API key required.
      operationId: startPaymentLinkCheckout
      tags:
        - Payment links
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaymentLinkStartRequest'
      responses:
        '201':
          description: Checkout session created
        '400':
          description: Validation error
        '410':
          description: Link unavailable
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: >-
        Server integrations: use "Api-Key YOUR_KEY" or header "Api-Key:
        YOUR_KEY"
    TokenAuth:
      type: apiKey
      in: header
      name: Authorization
      description: >-
        Merchant dashboard session: use "Token YOUR_ACCESS_TOKEN" (from login
        cookie flow)
  schemas:
    Error:
      type: object
      properties:
        detail:
          type: string
    DepositAttempt:
      type: object
      description: One deposit (card) attempt synced from the payment provider
      properties:
        status:
          type: string
          description: e.g. succeeded, requires_payment_method, failed
          example: succeeded
        paymentId:
          type: string
          description: Provider payment id
        attemptedAt:
          type: string
          format: date-time
        errorMessage:
          type: string
          nullable: true
          description: Decline or error message when the attempt failed
        paymentMethod:
          type: string
          example: card
    Payment:
      type: object
      description: Payment (transaction) returned by GET /transactions/{id}/
      properties:
        id:
          type: string
          description: Public payment ID (e.g. TXabc123)
        amount:
          type: string
          description: Net amount (after fees)
          example: '99.00'
        status:
          type: string
          description: paid, failed, refund_pending, refunded, canceled, processing
        currency:
          type: string
          example: USD
        payer_email:
          type: string
          format: email
        products:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
        created_at:
          type: string
          format: date-time
        depositAttempts:
          type: array
          description: >-
            Deposit attempt history (newest first). See product guide Deposit
            attempts.
          items:
            $ref: '#/components/schemas/DepositAttempt'
    RefundCreateRequest:
      type: object
      required:
        - reason
      properties:
        reason:
          type: string
          maxLength: 500
          description: Reason shown to the customer and sent to the payment provider
          example: Customer request
        amount:
          type: string
          description: >-
            Partial refund amount in charge currency (TTC). Omit for full
            remaining refundable amount.
          example: '40.00'
    RefundCreateResponse:
      type: object
      properties:
        status:
          type: string
          example: processing
        refund_id:
          type: integer
          description: Internal numeric refund ID
        refund_public_id:
          type: string
          description: Public refund ID without RF prefix
        transaction_id:
          type: integer
        amount:
          type: string
        currency:
          type: string
        amount_usd:
          type: string
          description: Merchant wallet hold in USD
        amount_in_cents:
          type: integer
          description: Amount sent to Inflow (HT basis)
    Refund:
      type: object
      description: Refund returned by GET /refunds/ and GET /refunds/{id}/
      properties:
        id:
          type: string
          description: Public refund ID (e.g. RFabc123)
        transaction_id:
          type: string
          description: Parent payment public ID (e.g. TXabc123)
        amount:
          type: string
        currency:
          type: string
        amount_in_cents:
          type: integer
          nullable: true
        amount_usd:
          type: string
          nullable: true
        reason:
          type: string
        status:
          type: string
          enum:
            - pending
            - processing
            - completed
            - failed
            - rejected
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    Billing:
      type: object
      properties:
        first_name:
          type: string
        last_name:
          type: string
        email:
          type: string
          format: email
        country:
          type: string
          maxLength: 2
        postal_code:
          type: string
    ProductItem:
      type: object
      oneOf:
        - required:
            - id
          properties:
            id:
              type: string
            quantity:
              type: integer
              minimum: 1
              default: 1
        - required:
            - name
            - unit_price
          properties:
            name:
              type: string
            quantity:
              type: integer
              minimum: 1
              default: 1
            unit_price:
              type: number
              format: double
              minimum: 0
              exclusiveMinimum: true
    CheckoutCreateRequest:
      type: object
      required:
        - billing_country
        - billing_first_name
        - billing_last_name
        - billing_email
        - products
      properties:
        billing_country:
          type: string
          maxLength: 2
          example: US
        billing_postal_code:
          type: string
        billing_first_name:
          type: string
          maxLength: 100
        billing_last_name:
          type: string
          maxLength: 100
        billing_email:
          type: string
          format: email
        billing_phone:
          type: string
        currency:
          type: string
          enum:
            - USD
            - EUR
          default: USD
          description: Charge currency; omitted values default to USD (matches the API).
        products:
          type: array
          items:
            $ref: '#/components/schemas/ProductItem'
        successUrl:
          type: string
          format: uri
        failedUrl:
          type: string
          format: uri
        cancelUrl:
          type: string
          format: uri
        sandbox_simulate:
          type: string
          description: >
            Sandbox API key only; rejected with production keys. Scripted
            provider outcomes apply only when the server runs internal
            simulation (e.g. CI). Otherwise the value may be stored on the
            session but does not change live provider GET results.
          enum:
            - payment_failed
            - checkout_canceled
            - canceled
            - success_pending_deposit
            - checkout_pending
    CheckoutCreateResponse:
      type: object
      required:
        - checkout_id
        - order_id
        - checkout_url
        - token
        - expires_at
      properties:
        checkout_id:
          type: string
          description: Same as order_id; stable id for webhooks and reconciliation.
        order_id:
          type: string
        checkout_url:
          type: string
          format: uri
        token:
          type: string
        expires_at:
          type: integer
          format: int64
          description: Unix timestamp
    PaymentLinkLine:
      type: object
      required:
        - product_id
        - quantity
      properties:
        product_id:
          type: string
        quantity:
          type: integer
          minimum: 1
    PaymentLinkCreateRequest:
      type: object
      required:
        - name
        - products
      properties:
        name:
          type: string
          description: Internal label for the merchant dashboard
        products:
          type: array
          items:
            $ref: '#/components/schemas/PaymentLinkLine'
        currency:
          type: string
          enum:
            - USD
            - EUR
          default: USD
        pricing_mode:
          type: string
          enum:
            - TAX_EXCLUSIVE
            - TAX_INCLUSIVE
        expires_at:
          type: string
          format: date-time
          nullable: true
        max_uses:
          type: integer
          minimum: 1
          nullable: true
        metadata:
          type: object
          additionalProperties: true
          nullable: true
    PaymentLinkStartRequest:
      type: object
      required:
        - billing_country
        - billing_first_name
        - billing_last_name
        - billing_email
      properties:
        billing_country:
          type: string
          maxLength: 2
        billing_postal_code:
          type: string
        billing_first_name:
          type: string
        billing_last_name:
          type: string
        billing_email:
          type: string
          format: email
        billing_phone:
          type: string
        currency:
          type: string
          enum:
            - USD
            - EUR
        successUrl:
          type: string
          format: uri
        failedUrl:
          type: string
          format: uri
        cancelUrl:
          type: string
          format: uri
