openapi: "3.1.0"
info:
  title: Provenio Art Intelligence API
  description: |
    Institutional-grade art provenance and market intelligence.
    282,731 records, 101,114 priced auction transactions (1700–2025).
    Custody-gap detection with risk-window flagging (Washington Principles).
    Cross-indexed against Getty Provenance Index, Knoedler stock books,
    ERR Project, lostart.de, and MNR (France).
  version: "1.0.0"
  contact:
    email: research@provenio.art
  license:
    name: Proprietary

servers:
  - url: https://provenio.art/api/v1
    description: Production

# Free tier (200 calls/month) works without authentication (IP-rate-limited).
# Solo/Studio tiers authenticate with a license key via x-provenio-key header
# or as a Bearer token.
security:
  - {}
  - provenioKey: []
  - bearerAuth: []

paths:
  /artists:
    get:
      operationId: searchArtists
      summary: Search artists by name, nationality, or era
      description: |
        Returns a shortlist of person nodes. Use partial names for best results
        (e.g. "Basquiat" not "Jean-Michel Basquiat"). Returns person IDs in
        "person:slug" format — use these in /artworks?artist= filters.
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
          description: "Free-text substring match on display_name. Use empty string to browse."
        - name: nationality
          in: query
          schema:
            type: string
          description: "e.g. 'dutch', 'french', 'japanese'"
        - name: era
          in: query
          schema:
            type: string
          description: "e.g. 'baroque', 'modernism', 'contemporary'"
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        "200":
          description: Artist shortlist
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ArtistList"

  /artists/{id}:
    get:
      operationId: getArtist
      summary: Full artist profile — reception, influence, and market in one call
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: "Person ID slug, e.g. 'rembrandt-van-rijn' or full 'person:rembrandt-van-rijn'"
      responses:
        "200":
          description: Full artist profile
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ArtistProfile"
        "404":
          description: Artist not found

  /artists/{id}/influence:
    get:
      operationId: getInfluenceNetwork
      summary: Influence claims for an artist (formative / significant / peripheral)
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: direction
          in: query
          schema:
            type: string
            enum: [influenced_by, influenced, both]
            default: both
        - name: min_strength
          in: query
          schema:
            type: string
            enum: [formative, significant, peripheral, speculative]
            default: peripheral
      responses:
        "200":
          description: Influence network
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InfluenceNetwork"

  /artists/{id}/career:
    get:
      operationId: getCareerTrajectory
      summary: Career arc — exhibition milestones, reception shifts, price trajectory
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Career trajectory
          content:
            application/json:
              schema:
                type: object

  /artworks:
    get:
      operationId: searchArtworks
      summary: Search artworks by title, artist, medium, date range, or provenance risk
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
          description: "Substring match on title. Do NOT embed artist names here — use artist= instead."
        - name: artist
          in: query
          schema:
            type: string
          description: "Person ID slug from /artists, e.g. 'person:rembrandt-van-rijn'"
        - name: medium
          in: query
          schema:
            type: string
          description: "e.g. 'oil', 'bronze', 'watercolor'"
        - name: date_from
          in: query
          schema:
            type: integer
          description: "Earliest production year (inclusive)"
        - name: date_to
          in: query
          schema:
            type: integer
          description: "Latest production year (inclusive)"
        - name: attribution_status
          in: query
          schema:
            type: string
            enum: [single_author, workshop, disputed, anonymous]
        - name: provenance_risk
          in: query
          schema:
            type: string
            enum: [nazi_era_gap, colonial_seizure, forced_sale, repatriation_pending, knoedler_forgery_window]
          description: "Surface artworks matching a risk profile for bulk due-diligence screening"
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 10
      responses:
        "200":
          description: Artwork shortlist
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ArtworkList"

  /artworks/{id}:
    get:
      operationId: getArtwork
      summary: Full artwork profile — iconography, custody, polity, and transactions
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: "Artwork ID slug, e.g. 'artwork:klimt-portrait-adele-bloch-bauer-1907'"
      responses:
        "200":
          description: Full artwork profile
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ArtworkProfile"
        "404":
          description: Artwork not found

  /artworks/{id}/provenance:
    get:
      operationId: getProvenanceChain
      summary: Full custody chain with risk-window flagging per Washington Principles
      description: |
        Returns ordered custody transfer sequence with gap detection and risk-window flags:
        - Nazi-era: 1933–1945 (ERR Project, lostart.de, MNR cross-reference)
        - Colonial-era: 1800–1960
        - Knoedler forgery: 1970–2009
        - Russian Revolution: 1917–1923
        Gaps flagged when any portion of undocumented period overlaps a risk window.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Custody chain with gap analysis
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProvenanceChain"

  /auctions:
    get:
      operationId: searchAuctionHistory
      summary: Auction transaction records with optional P25/P50/P75 percentile summary
      parameters:
        - name: artist
          in: query
          schema:
            type: string
          description: "Person ID slug (e.g. 'person:pablo-picasso') or artist name (e.g. 'Picasso')"
        - name: artwork
          in: query
          schema:
            type: string
          description: "Artwork ID slug"
        - name: date_from
          in: query
          schema:
            type: integer
          description: "Earliest sale year"
        - name: date_to
          in: query
          schema:
            type: integer
          description: "Latest sale year"
        - name: house
          in: query
          schema:
            type: string
          description: "Auction house substring match, e.g. 'Christie', 'Sotheby'"
        - name: include_percentiles
          in: query
          schema:
            type: boolean
            default: false
          description: "Include P25/P50/P75 summary for the result set"
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 20
      responses:
        "200":
          description: Auction transaction records
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuctionResult"

  /concepts/{id}:
    get:
      operationId: getMovementContext
      summary: Art movement or concept — definition, reception arc, sample artworks
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: "Concept slug. Prefix by type: 'movement-impressionism', 'movement-baroque', 'era-modern', 'period-edo-japan', 'medium-oil-on-canvas', 'iconography-last-supper-narrative'"
      responses:
        "200":
          description: Movement / concept profile
          content:
            application/json:
              schema:
                type: object

  /provenance/{id}/chain:
    get:
      operationId: getProvenanceChainByProvenance
      summary: Ordered custody chain with all verification states (alias of /artworks/{id}/provenance)
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: "Artwork ID slug, e.g. 'klimt-portrait-adele-bloch-bauer-1907'"
        - name: period_start
          in: query
          schema:
            type: integer
          description: "Restrict chain to entries overlapping this start year"
        - name: period_end
          in: query
          schema:
            type: integer
      responses:
        "200":
          description: Custody chain with gap analysis
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProvenanceChain"
        "404":
          description: Artwork not found

  /provenance/{id}/gaps:
    get:
      operationId: getCustodyGaps
      summary: Custody gap intervals only — undocumented ownership periods within risk windows
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Gap intervals
          content:
            application/json:
              schema:
                type: object
                properties:
                  artwork_id:
                    type: string
                  gap_count:
                    type: integer
                  gap_risks:
                    type: array
                    items:
                      type: object
                  note:
                    type: string
        "404":
          description: Artwork not found

  /artworks/{id}/comparables:
    get:
      operationId: getComparableArtworks
      summary: Price-comparable works to a given artwork (subject, medium, period, patron class)
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        "200":
          description: Comparable set
          content:
            application/json:
              schema:
                type: object
                properties:
                  artwork_id:
                    type: string
                  comparables:
                    type: array
                    items:
                      type: object

  /records/search:
    get:
      operationId: searchRecords
      summary: Federated full-text search across artworks and artists
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
          description: "Free-text query matched against artwork titles and artist names"
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        "200":
          description: Federated results
          content:
            application/json:
              schema:
                type: object
                properties:
                  query:
                    type: string
                  total:
                    type: integer
                  artists:
                    type: array
                    items:
                      type: object
                  artworks:
                    type: array
                    items:
                      type: object
        "400":
          description: Missing query parameter

  /restitution/claims:
    get:
      operationId: getRestitutionClaims
      summary: Artworks flagged with documented seizure / restitution risk
      description: |
        Returns artworks carrying a documented risk flag in the custody index, cross-referenced
        against the ERR Project, lostart.de, MNR (France), and the Art Loss Register. Coverage is
        expanding; absence of a flag is not a clean-title determination.
      parameters:
        - name: risk
          in: query
          schema:
            type: string
            enum: [nazi_era_gap, colonial_seizure, forced_sale, repatriation_pending, knoedler_forgery_window]
            default: nazi_era_gap
        - name: artist
          in: query
          schema:
            type: string
          description: "Person ID slug"
        - name: period_from
          in: query
          schema:
            type: integer
        - name: period_to
          in: query
          schema:
            type: integer
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
      responses:
        "200":
          description: Flagged works
          content:
            application/json:
              schema:
                type: object
                properties:
                  risk_filter:
                    type: string
                  count:
                    type: integer
                  works:
                    type: array
                    items:
                      type: object
                  coverage_note:
                    type: string

  /batch:
    post:
      operationId: batchLookup
      summary: Bulk lookup — up to 500 artwork or artist IDs per request
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [ids]
              properties:
                ids:
                  type: array
                  maxItems: 500
                  items:
                    type: string
                  example: ["artwork:klimt-portrait-adele-bloch-bauer-1907", "person:gustav-klimt"]
      responses:
        "200":
          description: Batch results
          content:
            application/json:
              schema:
                type: object
                properties:
                  requested:
                    type: integer
                  found:
                    type: integer
                  results:
                    type: array
                    items:
                      type: object
        "400":
          description: Missing/invalid ids or over 500 limit

components:
  securitySchemes:
    provenioKey:
      type: apiKey
      in: header
      name: x-provenio-key
      description: "License key for Solo/Studio tiers. Omit for the free tier (200 calls/month, IP-rate-limited)."
    bearerAuth:
      type: http
      scheme: bearer
      description: "Alternatively pass the license key as a Bearer token."
  schemas:
    ArtistList:
      type: object
      properties:
        total:
          type: integer
        has_more:
          type: boolean
        artists:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              display_name:
                type: string
              birth_year:
                type: integer
              death_year:
                type: integer
              nationality:
                type: string
              era:
                type: string

    ArtistProfile:
      type: object
      properties:
        id:
          type: string
        display_name:
          type: string
        birth_year:
          type: integer
        death_year:
          type: integer
        nationality:
          type: string
        ulan_uri:
          type: string
        wikidata_uri:
          type: string
        reception_summary:
          type: object
        market_summary:
          type: object
        influence_count:
          type: integer

    InfluenceNetwork:
      type: object
      properties:
        artist_id:
          type: string
        influenced_by:
          type: array
          items:
            type: object
        influenced:
          type: array
          items:
            type: object

    ArtworkList:
      type: object
      properties:
        total:
          type: integer
        has_more:
          type: boolean
        artworks:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              title:
                type: string
              creator_name:
                type: string
              production_date_earliest:
                type: integer
              medium:
                type: string
              attribution_status:
                type: string

    ArtworkProfile:
      type: object
      properties:
        id:
          type: string
        title:
          type: string
        creator:
          type: object
        production_date_earliest:
          type: integer
        production_date_latest:
          type: integer
        medium:
          type: string
        iconography:
          type: object
        custody_summary:
          type: object
        transaction_summary:
          type: object

    ProvenanceChain:
      type: object
      properties:
        artwork_id:
          type: string
        chain:
          type: array
          items:
            type: object
            properties:
              keeper:
                type: string
              acquisition_type:
                type: string
              period_start:
                type: integer
              period_end:
                type: integer
              legal_status:
                type: string
              verified:
                type: boolean
        gap_risks:
          type: array
          items:
            type: object
            properties:
              gap_start:
                type: integer
              gap_end:
                type: integer
              risk_window:
                type: string
              risk_level:
                type: string
        checked_databases:
          type: array
          items:
            type: string
          example: ["ERR Project", "lostart.de", "MNR (France)", "Art Loss Register"]

    AuctionResult:
      type: object
      properties:
        total:
          type: integer
        transactions:
          type: array
          items:
            type: object
            properties:
              lot_id:
                type: string
              artwork_id:
                type: string
              sale_date:
                type: integer
              house:
                type: string
              hammer_price_usd:
                type: number
              currency:
                type: string
        percentiles:
          type: object
          properties:
            p25:
              type: number
            p50:
              type: number
            p75:
              type: number
