{
  "openapi": "3.0.1",
  "info": {
    "title": "Regione Lombardia Open Grants & Tenders Registry Delta Monitor",
    "description": "Delta monitor for Regione Lombardia's Socrata open-data grants/tenders registry (bandi). Computes a real OPEN/UPCOMING/CLOSED status from source dates, classifies each bando into a policy-area taxonomy, and emits NEW_LISTING/STATUS_CHANGE/UPDATED events. Pay-per-event: billed only for what changed.",
    "version": "1.0",
    "x-build-id": "VfN69YljL8DRbJifF"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/stefano_seggio~regione-lombardia-grants-registry-monitor/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-stefano_seggio-regione-lombardia-grants-registry-monitor",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/stefano_seggio~regione-lombardia-grants-registry-monitor/runs": {
      "post": {
        "operationId": "runs-sync-stefano_seggio-regione-lombardia-grants-registry-monitor",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/stefano_seggio~regione-lombardia-grants-registry-monitor/run-sync": {
      "post": {
        "operationId": "run-sync-stefano_seggio-regione-lombardia-grants-registry-monitor",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "onlyNew": {
            "title": "Deliver only new/changed records",
            "type": "boolean",
            "description": "When true (default), only NEW_LISTING, STATUS_CHANGE and UPDATED events are delivered - never a BASELINE_SNAPSHOT or SNAPSHOT_NO_DIFF row. When false, every bando currently in scope is delivered every run, including the free first-run baseline and unchanged rows - useful to preview the full dataset before committing spend.",
            "default": true
          },
          "eventTypes": {
            "title": "Change-event types to deliver",
            "type": "array",
            "description": "Restricts which of the three charged event types are delivered. Leave empty to deliver all three. Has no effect on BASELINE_SNAPSHOT/SNAPSHOT_NO_DIFF, which are controlled solely by onlyNew.",
            "items": {
              "type": "string",
              "enum": [
                "NEW_LISTING",
                "STATUS_CHANGE",
                "UPDATED"
              ]
            },
            "default": []
          },
          "statusFilter": {
            "title": "Lifecycle status filter (optional)",
            "type": "array",
            "description": "Deliver only bandi whose computed status (derived by this Actor from apertura_adesione/chiusura_adesione against the run's own clock - the source itself publishes no lifecycle-status column) is one of these. This is a delivery-time filter only - every bando is still tracked internally regardless, so one that transitions into or out of your filtered status is still correctly reported as STATUS_CHANGE the moment it does. Leave empty to deliver all statuses.",
            "items": {
              "type": "string",
              "enum": [
                "UPCOMING",
                "OPEN",
                "CLOSED"
              ]
            },
            "default": []
          },
          "policyAreaFilter": {
            "title": "Policy area filter (optional)",
            "type": "array",
            "description": "Deliver only bandi classified into one of these policy areas by this Actor's own taxonomy mapper (built from the real, observed direzione_generale values in the live dataset - a free-text field with no controlled vocabulary in the source itself). Delivery-time only, same tracking guarantee as statusFilter. Leave empty for all areas. See README for the full taxonomy and its source mapping.",
            "items": {
              "type": "string",
              "enum": [
                "EDUCATION_TRAINING_LABOR",
                "CULTURE_TOURISM",
                "ECONOMIC_DEVELOPMENT_INNOVATION",
                "SOCIAL_WELFARE_FAMILY_DISABILITY",
                "PUBLIC_SAFETY_CIVIL_PROTECTION",
                "ENVIRONMENT_TERRITORY",
                "AGRICULTURE_FOOD",
                "SPORT_YOUTH",
                "INSTITUTIONAL_ADMINISTRATION",
                "FINANCE_BUDGET",
                "INFRASTRUCTURE_TRANSPORT",
                "IN_HOUSE_COMPANIES_AGENCIES",
                "PROVINCIAL_EMPLOYMENT_OFFICES",
                "UNCLASSIFIED"
              ]
            },
            "default": []
          },
          "keyword": {
            "title": "Keyword search (optional)",
            "type": "string",
            "description": "Full-text search passed server-side as Socrata's own $q parameter, matched against every text column (titolo_bando, direzione_generale, ente, tipo_strumento). Example: \"formazione\" or \"ICT\". Leave empty to skip.",
            "default": ""
          },
          "directorateFilter": {
            "title": "Directorate filter - direzione_generale (optional)",
            "type": "array",
            "description": "Deliver only bandi whose direzione_generale case-insensitively contains one of these values. Free-text matching against whatever Regione Lombardia's own back office typed in, not a stable enum - real values observed live include \"ISTRUZIONE, FORMAZIONE, LAVORO\", \"CULTURA\", \"ARIA SPA\". Delivery-time only. Leave empty for all directorates.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "maxItems": {
            "title": "Max records to push this run",
            "minimum": 1,
            "type": "integer",
            "description": "Stops the run once this many records have been pushed (and charged, where applicable). Records not yet reached when the limit is hit are simply re-evaluated fresh next run. Leave unset for no limit - the full register is only ~1,912 rows, so an unlimited run is inexpensive even on a full onlyNew:false baseline pass.",
            "default": 20
          },
          "deltaStateName": {
            "title": "Delta state name",
            "type": "string",
            "description": "Names the persistent Key-Value Store this run's delta memory lives in. Use a distinct name per independent schedule/filter configuration so they don't share baseline progress.",
            "default": "default"
          },
          "resetState": {
            "title": "Reset delta state",
            "type": "boolean",
            "description": "When true, ignores all previously-remembered bandi for this deltaStateName and re-baselines from scratch (every bando becomes a fresh, uncharged BASELINE_SNAPSHOT again, subject to onlyNew).",
            "default": false
          },
          "socrataAppToken": {
            "title": "Socrata App Token (optional, BYO)",
            "type": "string",
            "description": "An optional Socrata app token (free, self-service at dati.lombardia.it) sent as the X-App-Token header on every request. Unauthenticated requests share a per-IP throttle Socrata does not fully document publicly. Supplying your own token removes the shared-IP ceiling entirely. Never logged or persisted beyond this run."
          },
          "requestTimeoutSecs": {
            "title": "Per-request timeout (seconds)",
            "minimum": 5,
            "maximum": 30,
            "type": "integer",
            "description": "Aborts a single Socrata API request that takes longer than this, so one slow response never stalls the whole run. Capped at 30 (down from an earlier 120) because this Actor's own configured run timeout is 300s and a full baseline walk needs 2 Socrata pages ($limit=1000 against the live ~1,912-row dataset): worst case per page is (maxRetries+1) attempts x this timeout, plus backoff. Even holding maxRetries at its own new maximum of 2 (3 attempts/page), the old 120s ceiling alone gave 2 pages x [3x120s + ~3.5s backoff] = 727s - over 2x the run timeout before a single row was even parsed. 30 keeps that same worst case at 187s (62% of the 300s budget) with real margin left for per-row processing.",
            "default": 30
          },
          "maxRetries": {
            "title": "Max retry attempts per request",
            "minimum": 0,
            "maximum": 2,
            "type": "integer",
            "description": "Retry attempts (exponential backoff with jitter) before a single page fetch is given up on as failed. Applies only to retryable outcomes (429 and 5xx responses, or a request timeout) - any other non-2xx response is treated as final immediately. Capped at 2 (down from an earlier 8, default down from 4) so this call site's own worst case can never exceed this Actor's configured 300s run timeout: a full baseline walk is 2 Socrata pages, and at the new maxRetries=2 ceiling with requestTimeoutSecs at its own new 30s ceiling, worst case is 2 x [(2+1)x30s + ~3.5s backoff] = 187s (62% of budget) - still 3 total attempts per page, just no longer enough retries-x-timeout to blow the run's own time budget on their own.",
            "default": 2
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}