openapi: 3.1.0
info:
  title: Advanta API
  description: |
    Banking infrastructure for Iberian factoring & confirming.
    27 endpoints across Connect, Score, ERP, Compliance, Insurance, Embed.
    This spec covers the core surface (Connect + Score + ERP).
  version: 1.0.0
  contact:
    name: Advanta Tech
    email: hello@advanta.pt
    url: https://tech.advanta.pt
servers:
  - url: https://api.advanta.pt/v1
    description: Production (live)
  - url: https://sandbox.advanta.pt/v1
    description: Sandbox (test data, no real money, identical schemas)
security:
  - bearerAuth: []
paths:
  /connect/sessions:
    post:
      summary: Create a Connect session
      description: |
        Create a session for an SME to connect their ERP. Returns a `link_token`
        (for the React SDK / iframe) and a `connect_url` (for direct redirect).
      operationId: createConnectSession
      tags: [Connect]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateConnectSessionRequest'
            example:
              customer:
                nif: "509123456"
                name: "Construções Ibéricas, Lda."
                email: "joao@construcoes.pt"
              branding: "co-branded"
              scopes: ["invoices:read", "receivables:read", "saft:read"]
              redirect_url: "https://factoring.banco.pt/connect/done"
              metadata:
                bank_customer_id: "BANCO-CUST-12345"
      responses:
        '201':
          description: Session created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectSession'
        '400': { $ref: '#/components/responses/ValidationError' }
        '401': { $ref: '#/components/responses/AuthError' }
        '429': { $ref: '#/components/responses/RateLimitError' }
  /connect/sessions/{sessionId}:
    get:
      summary: Inspect a Connect session
      operationId: getConnectSession
      tags: [Connect]
      parameters:
        - name: sessionId
          in: path
          required: true
          schema: { type: string, example: "cs_2026_xY7p9R8qK2mN4tL3" }
      responses:
        '200':
          description: Session
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectSession'
  /score:
    post:
      summary: Generate a risk score for an SME
      description: |
        Returns score 0-100 + E/S/G breakdown + recommended limit + spread + risk factors.
        Requires either `connection_id` (data pulled live from ERP) or `nif` (uses last cached pull).
        P95 latency < 2s.
      operationId: createScore
      tags: [Score]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                connection_id:
                  type: string
                  example: "conn_2026_aB3xC9D1fK7p"
                nif:
                  type: string
                  example: "509123456"
                product:
                  type: string
                  enum: [factoring, confirming]
                  example: "factoring"
                horizon_days:
                  type: integer
                  default: 90
              oneOf:
                - required: [connection_id]
                - required: [nif]
      responses:
        '200':
          description: Score
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Score'
              example:
                score_id: "sc_2026_xY7p9R"
                score: 78
                tier: "A-"
                esg_score: 71
                recommended_limit_eur: 240000
                recommended_spread_pct: 1.8
                factors:
                  payment_history: 82
                  concentration_risk: 65
                  sector_outlook: 75
                  sustainability_signals: 71
                data_freshness: "2026-04-26T03:00:00Z"
  /erp/{connectionId}/invoices:
    get:
      summary: List invoices from a connected ERP
      operationId: listInvoices
      tags: [ERP]
      parameters:
        - name: connectionId
          in: path
          required: true
          schema: { type: string }
        - name: status
          in: query
          schema:
            type: string
            enum: [open, paid, overdue, cancelled, all]
            default: open
        - name: since
          in: query
          schema: { type: string, format: date }
        - name: limit
          in: query
          schema: { type: integer, default: 100, maximum: 500 }
      responses:
        '200':
          description: List of invoices
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: '#/components/schemas/Invoice' }
                  next_cursor: { type: string, nullable: true }
  /erp/{connectionId}/saft/refresh:
    post:
      summary: Trigger an out-of-band SAFT-PT pull
      operationId: refreshSaft
      tags: [ERP]
      parameters:
        - name: connectionId
          in: path
          required: true
          schema: { type: string }
      responses:
        '202':
          description: Pull queued (webhook will fire on completion)
  /erp/{connectionId}:
    delete:
      summary: Disconnect an ERP (revoke OAuth grant)
      operationId: deleteConnection
      tags: [ERP]
      parameters:
        - name: connectionId
          in: path
          required: true
          schema: { type: string }
      responses:
        '204':
          description: Disconnected

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        Use your workspace API key. Format: `sk_live_...` (production) or `sk_test_...` (sandbox).
        Send as `Authorization: Bearer {key}`.
  schemas:
    CreateConnectSessionRequest:
      type: object
      required: [customer, branding]
      properties:
        customer:
          type: object
          required: [nif, name]
          properties:
            nif: { type: string, example: "509123456" }
            name: { type: string, example: "Construções Ibéricas, Lda." }
            email: { type: string, format: email }
        branding:
          type: string
          enum: [marketplace, co-branded, white-label]
        scopes:
          type: array
          items:
            type: string
            enum: [invoices:read, receivables:read, saft:read, balances:read]
        redirect_url:
          type: string
          format: uri
        metadata:
          type: object
          additionalProperties: true
    ConnectSession:
      type: object
      properties:
        session_id: { type: string }
        link_token: { type: string }
        connect_url: { type: string, format: uri }
        expires_at: { type: string, format: date-time }
        branding: { type: string }
        status:
          type: string
          enum: [pending, in_progress, completed, failed, expired]
        connection_id: { type: string, nullable: true }
        erp_provider:
          type: string
          nullable: true
          enum: [toconline, primavera, phc-go, moloni, sage, invoicexpress, odoo, dynamics365, netsuite, sap, holded, vendus, jasmin]
        nif: { type: string, nullable: true }
        completed_at: { type: string, format: date-time, nullable: true }
        metadata: { type: object, additionalProperties: true }
    Score:
      type: object
      properties:
        score_id: { type: string }
        score: { type: integer, minimum: 0, maximum: 100 }
        tier:
          type: string
          enum: [AAA, AA, A+, A, A-, B+, B, B-, C+, C, C-, D]
        esg_score: { type: integer, minimum: 0, maximum: 100 }
        recommended_limit_eur: { type: number }
        recommended_spread_pct: { type: number }
        factors:
          type: object
          properties:
            payment_history: { type: integer }
            concentration_risk: { type: integer }
            sector_outlook: { type: integer }
            sustainability_signals: { type: integer }
        data_freshness: { type: string, format: date-time }
    Invoice:
      type: object
      properties:
        id: { type: string }
        external_id: { type: string }
        issued_at: { type: string, format: date }
        due_at: { type: string, format: date }
        amount: { type: number }
        currency: { type: string, example: "EUR" }
        customer_nif: { type: string }
        customer_name: { type: string }
        status:
          type: string
          enum: [open, paid, overdue, cancelled]
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            type:
              type: string
              enum: [validation_error, auth_error, rate_limit, erp_unavailable, internal_error]
            code: { type: string }
            message: { type: string }
            field: { type: string, nullable: true }
            request_id: { type: string }
  responses:
    ValidationError:
      description: Validation error
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    AuthError:
      description: Authentication error
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    RateLimitError:
      description: Rate limit exceeded
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
