{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/shapelessai/json-schema/shapelessai-pull-estimate-schema.json",
  "title": "PullEstimate",
  "x-generated": "2026-11-11",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/shapelessai-openapi.json#/components/schemas/PullEstimate",
  "type": "object",
  "properties": {
    "node": {
      "type": "string"
    },
    "expansion": {
      "type": "string",
      "enum": [
        "profile",
        "posts",
        "sponsors",
        "ads",
        "creators",
        "metrics",
        "labels"
      ]
    },
    "since": {
      "type": [
        "string",
        "null"
      ]
    },
    "until": {
      "type": [
        "string",
        "null"
      ]
    },
    "coveredFrom": {
      "type": [
        "string",
        "null"
      ],
      "description": "posts: the oldest publish date already held in the range."
    },
    "coveredTo": {
      "type": [
        "string",
        "null"
      ]
    },
    "alreadyHeld": {
      "type": "integer",
      "description": "posts: posts already held inside the range; they are not paid for again."
    },
    "gaps": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "estimatedUnits": {
            "type": "number"
          },
          "estimateUsd": {
            "type": "number"
          }
        }
      },
      "description": "The unread parts of the range, each priced; the whole range when forced."
    },
    "reachesRange": {
      "type": "boolean",
      "description": "False when the source cannot read back to since; note says how far it reaches."
    },
    "vendor": {
      "type": [
        "string",
        "null"
      ],
      "description": "Null when this deployment has no source for it; every number is then 0."
    },
    "unit": {
      "type": "string",
      "enum": [
        "record",
        "page",
        "call"
      ]
    },
    "perUnitUsd": {
      "type": "number"
    },
    "estimatedUnits": {
      "type": "number"
    },
    "estimateUsd": {
      "type": "number",
      "description": "Engine USD."
    },
    "capUsd": {
      "type": "number",
      "description": "The most the pull can spend at these settings, held to the budget."
    },
    "cadenceHours": {
      "type": [
        "number",
        "null"
      ],
      "description": "posts: the median hours between the account's newest posts (null under three held)."
    },
    "nextRefreshAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "description": "posts: when a plain refresh next pulls (half the cadence, 6 h to 7 days); null when never pulled, so due now."
    },
    "steps": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string"
          },
          "vendor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Null for a step that reads only what the graph holds."
          },
          "estimateUsd": {
            "type": "number"
          }
        },
        "required": [
          "label",
          "vendor",
          "estimateUsd"
        ]
      },
      "description": "creators: the pull's steps, cheapest first, each priced (0 for what the graph holds); empty for every other expansion. They sum to estimateUsd, held to the budget."
    },
    "note": {
      "type": "string"
    }
  },
  "required": [
    "node",
    "expansion",
    "estimateUsd",
    "capUsd",
    "note"
  ],
  "additionalProperties": true
}