openapi: 3.0.3
info:
  title: Tenderkart Client API
  version: "1.0"
  description: |
    Client API for assigned-filter tender sync, authenticated tender lookup/detail access, and document downloads.
    Filters are created and maintained for each customer account and exposed to the assigned API key.

    **Filter tender listing**:
    - Call `/filters` to list the Tenderkart Team-created filters available to your API key.
    - Call `/filters/{filter_id}/tenders` with `updated_after` to sync tenders changed in Tenderkart.
    - Use `pagination.next_cursor` to continue a page; `sync.next_updated_after` is returned only on the final page.
    - Use `updated_at` for integration sync because source pipelines can lag behind published dates.
      For some sources, normal portal/crawler lag can be up to 24 hours.
    - Use ISO-8601 timestamps such as `2026-05-24T10:30:00Z`; Unix timestamps are not accepted.

    **Direct lookup** (single tender):
    - Call `/tenders/lookup?tender_id=<id>` with a portal tender ID or tender reference number.
    - Returns full tender details and documents in a single call.

    **Stats metadata**:
    - Call `POST /stats` with a date range and free-form filters.
    - Returns floated and awarded tender counts and value totals in INR crores for up to 30 days.

    Document IDs from both flows can be used with `/documents/{id}`.

servers:
  - url: https://tenderkart.in/api/v1/client
    description: Production

security:
  - ApiKeyAuth: []

paths:
  /filters:
    get:
      operationId: listFilters
      summary: List filters
      responses:
        "200":
          description: Filters retrieved successfully.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FiltersResponse"
              example:
                filters:
                  - id: f3b90a82-8bd3-4d1c-9d4e-a6a4a4b6a7de
                    name: Railway Signalling
                    criteria:
                      keywords:
                        - signalling
                        - railway
                      states:
                        - Karnataka
                      tender_types:
                        - open
                  - id: 7d5e8cc1-58eb-4fb3-9536-a2f6c9b7f26d
                    name: Solar Water Pumping
                    criteria:
                      keywords:
                        - solar
                        - pumping
                      categories:
                        - Works
                      tender_amount_min: 500000
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalError"

  /filters/{filter_id}/tenders:
    get:
      operationId: listFilterTenders
      summary: Sync tenders changed in Tenderkart
      description: |
        Returns tenders for a single assigned filter ordered by `updated_at` ascending, then Tenderkart UUID.

        Use this endpoint for scheduled integration sync. `updated_at` is Tenderkart's system timestamp,
        so it catches newly ingested tenders, delayed source-pipeline records, corrigendums, results,
        awards, cancellations, and technical refreshes. Do not sync on `published_at`; it is the
        source portal's business publication date and can be older than the Tenderkart ingestion time.
        For some sources, normal portal/crawler lag can be up to 24 hours.

        `updated_after` is exclusive. Use the previous `sync.next_updated_after`.
        `updated_before` is optional and defaults to a server-chosen safe upper bound slightly behind
        current server time. The chosen upper bound is frozen into the cursor so records created while
        you are paging are picked up by the next sync, and very recent writes are not skipped while
        they become searchable.

        If `pagination.has_more` is true, call the same endpoint with `cursor` set to `pagination.next_cursor`, optionally with `limit`.
        Do not combine `cursor` with `updated_after`, `updated_before`, or `status`.
        Cursors are scoped to the filter ID and current filter configuration.

        Timestamp parameters must be ISO-8601 date-time strings with timezone, normally UTC `Z`.
        Date-only values and Unix timestamps are not accepted for sync.

        Process rows idempotently by `id`. If a tender changes again while you are paging, it can appear
        again with a newer `updated_at`.
      parameters:
        - name: filter_id
          in: path
          required: true
          description: Filter ID.
          schema:
            type: string
        - name: updated_after
          in: query
          required: false
          description: Exclusive lower bound for `updated_at`. Omit for the first full backfill.
          schema:
            type: string
            format: date-time
          example: "2026-05-22T10:30:00Z"
        - name: updated_before
          in: query
          required: false
          description: Inclusive upper bound for `updated_at`. Defaults to a server-chosen safe upper bound and is frozen into the cursor.
          schema:
            type: string
            format: date-time
          example: "2026-05-24T12:30:00Z"
        - name: status
          in: query
          required: false
          description: Comma-separated simplified status buckets. Defaults to `active`.
          schema:
            type: string
            example: active,under_evaluation
        - name: limit
          in: query
          required: false
          description: Number of records per page. Default is 100, maximum is 100.
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 100
        - name: cursor
          in: query
          required: false
          description: Opaque cursor from `pagination.next_cursor`. Send it exactly as returned for the next page.
          schema:
            type: string
      responses:
        "200":
          description: Tenders retrieved successfully.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FilterTendersResponse"
              example:
                tenders:
                  - id: 6c4c27e1-4c8a-4d62-9a32-2b1c9c4f73e9
                    tenderkart_url: https://tenderkart.in/tender/6c4c27e1-4c8a-4d62-9a32-2b1c9c4f73e9
                    portal_name: gem
                    tender_id: "186782"
                    tender_reference_number: PWD/2025-26/RD/WORK_INDENT21248
                    status: under_evaluation
                    raw_status: technical_evaluation
                    title: Improvement works to Attigundi Kolagame road
                    organisation: Public Works Department
                    tender_type: Open Tender
                    tender_category: Works
                    product_category: Road Work
                    state:
                      - Karnataka
                    city:
                      - CHIKMAGALUR
                    tender_value: 13898883.41
                    emd_fee: 208485
                    published_at: "2026-05-21T08:00:00Z"
                    closing_at: "2026-06-08T12:00:00Z"
                    added_at: "2026-05-22T04:12:00Z"
                    updated_at: "2026-05-24T09:45:00Z"
                pagination:
                  next_cursor: eyJmaWx0ZXJfaWQiOiJmM2I5MGE4Mi...
                  has_more: true
                sync:
                  updated_after: "2026-05-22T10:30:00Z"
                  updated_before: "2026-05-24T12:30:00Z"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalError"

  /stats:
    post:
      operationId: getStatsMetadata
      summary: Get stats metadata
      description: |
        Returns aggregate tender market metadata for a free-form filter set.

        The date range is inclusive and can cover at most 30 days.
        New tender counts and amount use `publish_date`.
        Awarded tender counts and amount use awarded tender activity dates.
        The request uses `date_from` and `date_to` because the same range is applied to
        publish date for new tenders and to last activity date for awarded tenders.
        New tender amount uses `tender_value` when available, otherwise estimates
        tender value as `emd_fee * 100`. Response amounts are returned in INR crores.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StatsRequest"
            example:
              date_from: "2026-05-01"
              date_to: "2026-05-20"
              keywords:
                - solar pump
                - battery storage
              states:
                - Maharashtra
                - Gujarat
              portals:
                - gem
              tender_amount_min: 10000000
              tender_amount_max: 500000000
      responses:
        "200":
          description: Stats retrieved successfully.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatsResponse"
              example:
                range:
                  from: "2026-05-01"
                  to: "2026-05-20"
                filters:
                  keywords:
                    - solar pump
                    - battery storage
                  states:
                    - Maharashtra
                    - Gujarat
                  portals:
                    - gem
                  tender_amount_min: 10000000
                  tender_amount_max: 500000000
                new_tenders_count: 128
                awarded_tenders_count: 17
                total_new_tender_amount_crores: 425
                total_awarded_amount_crores: 78
                new_tender_amount:
                  total_crores: 425
                  known_tender_value_crores: 398
                  estimated_from_emd_crores: 27
                  known_tender_value_count: 113
                  estimated_from_emd_count: 9
                  missing_tender_value_count: 6
                awarded_amount:
                  total_crores: 78
                  known_contract_value_count: 15
                  missing_contract_value_count: 2
                generated_at: "2026-05-20T08:15:30Z"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalError"

  /tenders/lookup:
    get:
      operationId: lookupTender
      summary: Lookup tender by ID or reference number
      description: |
        Returns full details for a tender identified by its portal tender ID or
        tender reference number.

        The `tender_id` parameter is matched against both the portal tender ID
        and the tender reference number fields. The API tries the tender ID
        first, then falls back to the reference number.

        This endpoint does not require a prior sync or filter context. It can return
        any tender available in Tenderkart when you provide a valid portal tender ID
        or reference number.
        Awarded tenders include canonical winning bidder/company names when AOC data is available.
        Document IDs returned in the response can be used with `/documents/{id}`.
      parameters:
        - name: tender_id
          in: query
          required: true
          description: Portal tender ID or tender reference number.
          schema:
            type: string
          examples:
            portalId:
              summary: Lookup by portal tender ID
              value: "2026_DRDO_905134_1"
            referenceNumber:
              summary: Lookup by tender reference number
              value: "GEM/2026/B/7406250"
      responses:
        "200":
          description: Tender retrieved successfully.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TenderDetails"
              example:
                id: 6c4c27e1-4c8a-4d62-9a32-2b1c9c4f73e9
                tenderkart_url: https://tenderkart.in/tender/6c4c27e1-4c8a-4d62-9a32-2b1c9c4f73e9
                portal_name: eprocure
                tender_id: "2026_DRDO_905134_1"
                tender_reference_number: "81246 CCE RnD SM EMU NGP MO CW 2026 27"
                status: active
                raw_status: active
                title: Civil works for compound wall at Nagpur
                organisation: Defence Research and Development Organisation
                tender_type: Open Tender
                tender_category: Works
                product_category: Civil Works
                state:
                  - Maharashtra
                city:
                  - Nagpur
                tender_value: 11791453
                emd_fee: 235829
                published_at: "2026-04-11T07:00:00Z"
                closing_at: "2026-04-30T11:30:00Z"
                added_at: "2026-04-11T07:20:00Z"
                updated_at: "2026-04-11T07:44:07Z"
                description: Civil works for strengthening compound wall at Mount Office Complex
                documents:
                  - id: doc_9sqGzrVDY0zxVwZD31XkLbvwOfTo6JH8ey
                    name: details.html
                    document_type: html
                  - id: doc_sxvOE__9dKwSlNmeR0TXh0hxVXtt40vw
                    name: BOQ_951381.xls
                    document_type: BOQ
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalError"

  /tenders/{tender_uuid}:
    get:
      operationId: getTender
      summary: Get tender
      description: |
        Returns full details for a tender by TenderKart tender UUID.
        Awarded tenders include canonical winning bidder/company names when AOC data is available.
        Document IDs include available NIT, BOQ, corrigendum, result, financial bid opening, and AOC/award documents.
        This endpoint is global for authenticated API clients. It does not require
        a filter ID; a valid API key can fetch any Tenderkart tender UUID returned
        by sync or lookup.
      parameters:
        - name: tender_uuid
          in: path
          required: true
          description: TenderKart tender UUID returned by `/filters/{filter_id}/tenders` or `/tenders/lookup`.
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Tender retrieved successfully.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TenderDetails"
              example:
                id: 6c4c27e1-4c8a-4d62-9a32-2b1c9c4f73e9
                tenderkart_url: https://tenderkart.in/tender/6c4c27e1-4c8a-4d62-9a32-2b1c9c4f73e9
                portal_name: gem
                tender_id: "186782"
                tender_reference_number: PWD/2025-26/RD/WORK_INDENT21248
                status: awarded
                raw_status: aoc
                title: Improvement works to Attigundi Kolagame road
                organisation: Public Works Department
                tender_type: Open Tender
                tender_category: Works
                product_category: Road Work
                state:
                  - Karnataka
                city:
                  - CHIKMAGALUR
                tender_value: 13898883.41
                emd_fee: 208485
                published_at: "2026-03-30T08:00:00Z"
                closing_at: "2026-04-08T12:00:00Z"
                added_at: "2026-03-30T08:30:00Z"
                updated_at: "2026-04-02T09:45:00Z"
                description: Improvement works to Attigundi Kolagame road from Km 9.25 to 11.00 Km
                award:
                  winning_bidders:
                    - company_name: Raw Infra Private Limited
                      awarded_value: 13898883.41
                      awarded_currency: INR
                documents:
                  - id: doc_8sLpQ2mV6xNd4kHr9cTw1yB7uF5eJ3aZ0i
                    name: NIT.pdf
                    document_type: nit
                  - id: doc_6fAocV2mV6xNd4kHr9cTw1yB7uF5eJ3
                    name: AOC.pdf
                    document_type: aoc
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalError"

  /documents/{id}:
    get:
      operationId: downloadDocument
      summary: Download document
      parameters:
        - name: id
          in: path
          required: true
          description: Opaque document ID returned by `/tenders/{tender_uuid}`.
          schema:
            type: string
      responses:
        "200":
          description: Document downloaded successfully.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalError"

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key

  schemas:
    Filter:
      type: object
      required:
        - id
        - name
        - criteria
      properties:
        id:
          type: string
        name:
          type: string
        criteria:
          $ref: "#/components/schemas/FilterCriteria"

    FilterCriteria:
      type: object
      description: Subject criteria assigned by Tenderkart. Tender status and sync dates are selected with `/filters/{filter_id}/tenders` query parameters.
      properties:
        keywords:
          type: array
          items:
            type: string
        exclude_keywords:
          type: array
          items:
            type: string
        states:
          type: array
          items:
            type: string
        cities:
          type: array
          items:
            type: string
        authorities:
          type: array
          items:
            type: string
        categories:
          type: array
          items:
            type: string
        procurement_types:
          type: array
          items:
            type: string
        tender_types:
          type: array
          description: Tender type groups. Supported values are `open` and `limited`.
          items:
            type: string
            enum:
              - open
              - limited
        payment_modes:
          type: array
          description: Accepted payment modes or grouped payment-mode values.
          items:
            type: string
        portals:
          type: array
          items:
            type: string
        tender_amount_min:
          type: number
          format: double
          nullable: true
          description: Minimum tender amount in absolute INR.
        tender_amount_max:
          type: number
          format: double
          nullable: true
          description: Maximum tender amount in absolute INR.
        show_na_amount:
          type: boolean
          description: When an amount range is set, also include tenders with no usable tender value or EMD-derived amount.
          default: false
        tender_fee_exempted:
          type: boolean
          nullable: true
        emd_fee_exempted:
          type: boolean
          nullable: true
        has_contract:
          type: boolean
          nullable: true
        low_competition:
          type: boolean
          nullable: true
          description: Include only tenders with low competition signals such as bid auto extension.
        gem_bid_types:
          type: array
          description: GeM bid type filters.
          items:
            type: string
        gem_mse_exemption:
          type: boolean
          nullable: true
        gem_startup_exemption:
          type: boolean
          nullable: true
        gem_turnover_min:
          type: number
          format: double
          nullable: true
        gem_turnover_max:
          type: number
          format: double
          nullable: true
        gem_oem_turnover_min:
          type: number
          format: double
          nullable: true
        gem_oem_turnover_max:
          type: number
          format: double
          nullable: true
        gem_experience_min:
          type: integer
          nullable: true
        gem_experience_max:
          type: integer
          nullable: true
    FiltersResponse:
      type: object
      required:
        - filters
      properties:
        filters:
          type: array
          items:
            $ref: "#/components/schemas/Filter"

    TenderSummary:
      type: object
      required:
        - id
        - tenderkart_url
        - portal_name
        - tender_id
        - status
        - title
        - organisation
      properties:
        id:
          type: string
          format: uuid
          description: TenderKart tender UUID.
        tenderkart_url:
          type: string
          format: uri
          description: Public TenderKart page for this tender.
        portal_name:
          type: string
        tender_id:
          type: string
        tender_reference_number:
          type: string
        status:
          type: string
          enum:
            - active
            - under_evaluation
            - awarded
            - cancelled
            - unknown
          description: Simplified customer-facing status bucket. `unknown` can appear for unmapped source statuses and is not accepted as a query filter.
        raw_status:
          type: string
          description: Original Tenderkart tender status.
        title:
          type: string
        organisation:
          type: string
        tender_type:
          type: string
        tender_category:
          type: string
        product_category:
          type: string
        state:
          type: array
          items:
            type: string
        city:
          type: array
          items:
            type: string
        tender_value:
          type: number
          format: double
          nullable: true
          description: Tender value in INR, when available.
        emd_fee:
          type: number
          format: double
          nullable: true
        published_at:
          type: string
          format: date-time
          nullable: true
          description: Source portal publication timestamp. Use for tender-age reporting, not integration sync.
        closing_at:
          type: string
          format: date-time
          nullable: true
          description: Tender bid submission deadline.
        added_at:
          type: string
          format: date-time
          nullable: true
          description: Timestamp when Tenderkart first added the tender.
        updated_at:
          type: string
          format: date-time
          nullable: true
          description: Tenderkart system timestamp for the latest tender record change. Use this field through `updated_after` for integration sync.

    CursorPagination:
      type: object
      required:
        - has_more
      properties:
        next_cursor:
          type: string
          description: Opaque cursor for the next page. Omitted when `has_more` is false.
        has_more:
          type: boolean

    FilterTendersResponse:
      type: object
      required:
        - tenders
        - pagination
        - sync
      properties:
        tenders:
          type: array
          items:
            $ref: "#/components/schemas/TenderSummary"
        pagination:
          $ref: "#/components/schemas/CursorPagination"
        sync:
          $ref: "#/components/schemas/TendersSync"

    TendersSync:
      type: object
      required:
        - updated_before
      properties:
        updated_after:
          type: string
          format: date-time
          description: Normalized UTC exclusive lower bound, when provided.
        updated_before:
          type: string
          format: date-time
          description: Frozen UTC inclusive upper bound for this sync run.
        next_updated_after:
          type: string
          format: date-time
          description: Returned only on the final page. Store it and pass it as `updated_after` on the next run.

    StatsRequest:
      type: object
      required:
        - date_from
        - date_to
      properties:
        date_from:
          type: string
          format: date
          description: Inclusive start date in Asia/Kolkata.
        date_to:
          type: string
          format: date
          description: Inclusive end date in Asia/Kolkata. Maximum range is 30 days.
        keywords:
          type: array
          maxItems: 20
          description: Free-form tender keywords. Multiple values are matched with OR logic.
          items:
            type: string
            maxLength: 120
        states:
          type: array
          maxItems: 50
          items:
            type: string
            maxLength: 120
        portals:
          type: array
          maxItems: 50
          items:
            type: string
            maxLength: 120
        tender_amount_min:
          type: number
          format: double
          nullable: true
          description: Minimum tender amount in absolute INR.
        tender_amount_max:
          type: number
          format: double
          nullable: true
          description: Maximum tender amount in absolute INR.

    StatsFilters:
      type: object
      properties:
        keywords:
          type: array
          maxItems: 20
          items:
            type: string
            maxLength: 120
        states:
          type: array
          maxItems: 50
          items:
            type: string
            maxLength: 120
        portals:
          type: array
          maxItems: 50
          items:
            type: string
            maxLength: 120
        tender_amount_min:
          type: number
          format: double
          nullable: true
        tender_amount_max:
          type: number
          format: double
          nullable: true

    StatsDateRange:
      type: object
      required:
        - from
        - to
      properties:
        from:
          type: string
          format: date
        to:
          type: string
          format: date

    StatsAmountBreakdown:
      type: object
      required:
        - total_crores
        - known_tender_value_crores
        - estimated_from_emd_crores
        - known_tender_value_count
        - estimated_from_emd_count
        - missing_tender_value_count
      properties:
        total_crores:
          type: number
          format: double
          description: Amount in INR crores.
        known_tender_value_crores:
          type: number
          format: double
          description: Amount in INR crores.
        estimated_from_emd_crores:
          type: number
          format: double
          description: Amount in INR crores.
        known_tender_value_count:
          type: integer
          format: int64
        estimated_from_emd_count:
          type: integer
          format: int64
        missing_tender_value_count:
          type: integer
          format: int64

    StatsAwardedAmountBreakdown:
      type: object
      required:
        - total_crores
        - known_contract_value_count
        - missing_contract_value_count
      properties:
        total_crores:
          type: number
          format: double
          description: Amount in INR crores.
        known_contract_value_count:
          type: integer
          format: int64
        missing_contract_value_count:
          type: integer
          format: int64

    StatsResponse:
      type: object
      required:
        - range
        - filters
        - new_tenders_count
        - awarded_tenders_count
        - total_new_tender_amount_crores
        - total_awarded_amount_crores
        - new_tender_amount
        - awarded_amount
        - generated_at
      properties:
        range:
          $ref: "#/components/schemas/StatsDateRange"
        filters:
          $ref: "#/components/schemas/StatsFilters"
        new_tenders_count:
          type: integer
          format: int64
        awarded_tenders_count:
          type: integer
          format: int64
        total_new_tender_amount_crores:
          type: number
          format: double
          description: Amount in INR crores.
        total_awarded_amount_crores:
          type: number
          format: double
          description: Amount in INR crores.
        new_tender_amount:
          $ref: "#/components/schemas/StatsAmountBreakdown"
        awarded_amount:
          $ref: "#/components/schemas/StatsAwardedAmountBreakdown"
        generated_at:
          type: string
          format: date-time

    DocumentSummary:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: string
          description: Opaque document ID.
        name:
          type: string
        document_type:
          type: string

    WinningBidder:
      type: object
      required:
        - company_name
      properties:
        company_name:
          type: string
          description: Canonical company name for the winning bidder when available; otherwise the AOC bidder name.
        awarded_value:
          type: number
          format: double
          description: Awarded value from the AOC when available.
        awarded_currency:
          type: string
          description: Award currency from the AOC when available.

    TenderAward:
      type: object
      required:
        - winning_bidders
      properties:
        winning_bidders:
          type: array
          items:
            $ref: "#/components/schemas/WinningBidder"

    TenderDetails:
      allOf:
        - $ref: "#/components/schemas/TenderSummary"
        - type: object
          properties:
            description:
              type: string
            award:
              $ref: "#/components/schemas/TenderAward"
            documents:
              type: array
              items:
                $ref: "#/components/schemas/DocumentSummary"

    ErrorResponse:
      type: object
      required:
        - error
        - message
      properties:
        error:
          type: string
        message:
          type: string

  responses:
    BadRequest:
      description: Invalid request.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            error: Bad Request
            message: updated_after must be an ISO-8601 timestamp with timezone

    Unauthorized:
      description: Invalid or missing API key.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            error: Unauthorized
            message: Invalid API key

    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            error: Not Found
            message: Tender not found

    Conflict:
      description: Resource is not available for download yet.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            error: Conflict
            message: Document is not available yet

    TooManyRequests:
      description: Rate limit exceeded.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            error: Rate Limit Exceeded
            message: 30 requests per minute allowed. Retry after 12 seconds.

    InternalError:
      description: Internal server error.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            error: Internal Server Error
            message: Please try again later
