{
  "openapi": "3.1.0",
  "info": {
    "title": "Nodehour",
    "version": "1",
    "description": "A buyer can describe a training run, fine-tune or serving target in plain words (model size, tokens or tokens/s, start, deadline, region, stack) or give a known cluster spec. The page proposes a configuration with its GPU-hours arithmetic, one alternative and the decisions that change the quote, then sends the requirement. Each request gets a reference at once; Nodehour takes the configuration to providers, asks each to quote the same terms and replies by email with options side by side. Nothing is priced, held or ordered until the buyer accepts an option's terms with the provider. Agents can POST the same envelope to /_c/request; date fields are whole numbers YYYYMMDD and anything else in them is refused with HTTP 400 naming the field; a request without contact.email and specifications.reply_email_included: true comes back with reply_email_included in missing_specifications, because no one can reply to it."
  },
  "servers": [
    {
      "url": "https://nodehour.forevenza.com"
    }
  ],
  "paths": {
    "/_c/request": {
      "post": {
        "operationId": "submit_request",
        "summary": "Send a requirement to Nodehour",
        "description": "You get a reference number at once, and we keep exactly what you sent: your words, the proposed configuration, the terms to quote and the open questions. If sizing or an open question changes the quote we email you first; then we take the configuration to providers that can run it, ask each to quote the same terms, and send the options side by side to the address you left. Without an address no one can reply, and nothing goes to providers until you add one. Nothing is priced, held or ordered until you accept an option's terms with the provider. Contact details are deleted after 180 days.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "need"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "buy",
                      "source",
                      "quote",
                      "verify",
                      "lease",
                      "license",
                      "hire",
                      "retrieve",
                      "convert",
                      "other"
                    ],
                    "description": "What you want done"
                  },
                  "need": {
                    "type": "string",
                    "maxLength": 2000,
                    "description": "The requirement in your own words"
                  },
                  "specifications": {
                    "type": "object",
                    "properties": {
                      "intent_text": {
                        "type": "string",
                        "description": "The buyer's own description of the run or fleet, verbatim.",
                        "x-facet": "specification"
                      },
                      "workload_kind": {
                        "type": "string",
                        "enum": [
                          "PRETRAIN",
                          "FINE_TUNE",
                          "INFERENCE",
                          "GPU_HOURS",
                          "KNOWN_SPEC"
                        ],
                        "description": "What the capacity is for.",
                        "x-facet": "specification"
                      },
                      "model_params_b": {
                        "type": "number",
                        "description": "Model size in billions of parameters.",
                        "x-facet": "specification"
                      },
                      "training_tokens_b": {
                        "type": "number",
                        "description": "Training tokens in billions.",
                        "x-facet": "quantity"
                      },
                      "target_tokens_per_s": {
                        "type": "number",
                        "description": "Sustained serving throughput target, tokens per second.",
                        "x-facet": "quantity"
                      },
                      "start_earliest": {
                        "type": "integer",
                        "minimum": 20260101,
                        "maximum": 20991231,
                        "description": "Earliest start date as a whole number YYYYMMDD, e.g. 20270104. Anything that is not a whole number, such as 'Q1 2027', 'sometime in Q1' or '2027-01-04' written as a string, is refused: HTTP 400 with errors naming the field, and nothing is recorded. A whole number outside 20260101 to 20991231 is refused the same way. A whole number in that range is recorded as sent; it is not checked as a calendar day. For a start known only in words, leave this field out and put the words in start_text: start_earliest then stays in missing_specifications.",
                        "x-facet": "timing"
                      },
                      "start_text": {
                        "type": "string",
                        "maxLength": 300,
                        "description": "A start the buyer can only describe ('Q1 2027', 'sometime in Q1'), verbatim. Sets no date: start_earliest stays in missing_specifications. Use it instead of a top-level timing object holding words alone.",
                        "x-facet": "specification"
                      },
                      "inferred_start_window": {
                        "type": "object",
                        "description": "A start window inferred from words, to confirm: earliest and latest as YYYY-MM-DD, from_words, basis. Recorded as an inference, not as a start; start_earliest stays in missing_specifications.",
                        "x-facet": "specification"
                      },
                      "start_latest": {
                        "type": "integer",
                        "minimum": 20260101,
                        "maximum": 20991231,
                        "description": "Latest acceptable start date (start flexibility) as a whole number YYYYMMDD. Same rule as start_earliest: anything but a whole number in range is refused with HTTP 400 naming the field.",
                        "x-facet": "timing"
                      },
                      "finish_by": {
                        "type": "integer",
                        "minimum": 20260101,
                        "maximum": 20991231,
                        "description": "Date the run must be finished by, as a whole number YYYYMMDD. Same rule as start_earliest: anything but a whole number in range is refused with HTTP 400 naming the field.",
                        "x-facet": "timing"
                      },
                      "deadline_basis": {
                        "type": "string",
                        "enum": [
                          "FIXED_DATE",
                          "ASSUMED_FIXED_DATE",
                          "WEEKS_FROM_START"
                        ],
                        "description": "With a start window: whether finish_by holds whatever the start (sized from start_latest), was assumed so pending the buyer's answer, or moves with the start (see finish_latest).",
                        "x-facet": "timing"
                      },
                      "finish_latest": {
                        "type": "integer",
                        "minimum": 20260101,
                        "maximum": 20991231,
                        "description": "When the deadline moves with the start: start_latest plus the window, as a whole number YYYYMMDD. Same rule as start_earliest: anything but a whole number in range is refused with HTTP 400 naming the field.",
                        "x-facet": "timing"
                      },
                      "alternative_quoted": {
                        "type": "boolean",
                        "description": "Whether the alternative configuration is quoted too.",
                        "x-facet": "specification"
                      },
                      "accelerator": {
                        "type": "string",
                        "description": "Accelerator to quote, e.g. NVIDIA H100 SXM 80GB. Proposed by Nodehour unless proposal_source is buyer_spec.",
                        "x-facet": "specification"
                      },
                      "accelerator_count": {
                        "type": "integer",
                        "description": "Number of accelerators.",
                        "x-facet": "quantity"
                      },
                      "accelerator_alternatives": {
                        "type": "array",
                        "description": "Substitute accelerators the buyer accepts, quoted on the same terms.",
                        "x-facet": "specification",
                        "items": {
                          "type": "string"
                        }
                      },
                      "any_accelerator": {
                        "type": "boolean",
                        "description": "True when the buyer left the chip open: every chip that fits is sized and quoted, listed in accelerator_alternatives.",
                        "x-facet": "specification"
                      },
                      "sizing_by_accelerator": {
                        "type": "array",
                        "description": "Each chip to quote with its size and sizing_basis (RUN, MEASURED, SCALED by memory bandwidth from a figure measured on another chip, SHAPE for a starting shape to confirm, SPEC): accelerator, accelerator_count, nodes, gpu_hours and, for serving, gpus_per_replica, replicas and tokens_per_s_per_replica.",
                        "x-facet": "quantity",
                        "items": {
                          "type": "object"
                        }
                      },
                      "reserved_gpu_hours_by_term": {
                        "type": "object",
                        "description": "Reserved GPU-hours at the proposed count for each term to quote, keyed by term.",
                        "x-facet": "quantity"
                      },
                      "min_accelerator_count": {
                        "type": "integer",
                        "description": "Smallest count the buyer would accept.",
                        "x-facet": "quantity"
                      },
                      "gpu_hours": {
                        "type": "number",
                        "description": "GPU-hours the job needs (estimate or the buyer's figure).",
                        "x-facet": "quantity"
                      },
                      "reserved_gpu_hours": {
                        "type": "number",
                        "description": "GPU-hours reserved at the count over the shortest term that covers the run.",
                        "x-facet": "quantity"
                      },
                      "terms_to_quote": {
                        "type": "array",
                        "description": "Each term the buyer wants quoted, side by side.",
                        "x-facet": "timing",
                        "items": {
                          "type": "string",
                          "enum": [
                            "6_WEEKS",
                            "1_MONTH",
                            "2_MONTHS",
                            "3_MONTHS",
                            "6_MONTHS",
                            "1_YEAR"
                          ]
                        }
                      },
                      "interconnect": {
                        "type": "string",
                        "enum": [
                          "INFINIBAND",
                          "ROCE",
                          "ETHERNET",
                          "ANY"
                        ],
                        "description": "Fabric between nodes.",
                        "x-facet": "specification"
                      },
                      "region": {
                        "type": "string",
                        "description": "Where the capacity may be, e.g. United States, European Union, Any region.",
                        "x-facet": "geography"
                      },
                      "residency_required": {
                        "type": "boolean",
                        "description": "True when the region is a hard data-residency constraint, false when it is a preference.",
                        "x-facet": "geography"
                      },
                      "deployment": {
                        "type": "string",
                        "enum": [
                          "SLURM",
                          "KUBERNETES",
                          "BARE_METAL",
                          "VIRTUAL_MACHINES",
                          "MANAGED"
                        ],
                        "description": "How the capacity is delivered.",
                        "x-facet": "specification"
                      },
                      "commitment": {
                        "type": "string",
                        "enum": [
                          "READY_TO_SIGN",
                          "COMPARING_QUOTES",
                          "EXPLORING"
                        ],
                        "description": "How firmly the buyer would commit.",
                        "x-facet": "specification"
                      },
                      "budget_ceiling_usd_per_gpu_hour": {
                        "type": "number",
                        "description": "The buyer's own ceiling in USD per GPU-hour; not an offer and not a quote.",
                        "x-facet": "budget"
                      },
                      "sizing_assumptions": {
                        "type": "object",
                        "description": "Assumptions behind the estimate: utilisation, margin, precision, chip peak and its source, headroom, measured throughput.",
                        "x-facet": "specification"
                      },
                      "proposal_source": {
                        "type": "string",
                        "enum": [
                          "nodehour_estimate",
                          "buyer_spec"
                        ],
                        "description": "Whether chip and count were proposed by Nodehour or given by the buyer.",
                        "x-facet": "identifier"
                      },
                      "proposal_text": {
                        "type": "string",
                        "description": "The proposed configuration in one line.",
                        "x-facet": "specification"
                      },
                      "alternative_text": {
                        "type": "string",
                        "description": "The alternative configuration and its trade-off.",
                        "x-facet": "specification"
                      },
                      "open_questions": {
                        "type": "array",
                        "description": "Decisions not yet stated, with the value used, in plain sentences.",
                        "x-facet": "specification",
                        "items": {
                          "type": "string"
                        }
                      },
                      "conflicts": {
                        "type": "array",
                        "description": "Inputs that cannot all be true as stated, in plain sentences.",
                        "x-facet": "specification",
                        "items": {
                          "type": "string"
                        }
                      },
                      "reply_email_included": {
                        "type": "boolean",
                        "description": "Set true when contact.email carries the address for the reply. Required so that a request without a reply address is machine-readable: while it is absent, missing_specifications lists $.specifications.reply_email_included, which means no one can reply and nothing goes to providers. The endpoint does not read contact.email to clear it, so set it whenever you send contact.email. The address itself goes only in contact.email.",
                        "x-facet": "identifier"
                      }
                    },
                    "description": "Details you already know. The business helps resolve missing specifications in the continuing engagement."
                  },
                  "quantity": {
                    "type": "object",
                    "properties": {
                      "amount": {
                        "type": "number"
                      },
                      "unit": {
                        "type": "string"
                      },
                      "per": {
                        "type": "string"
                      }
                    }
                  },
                  "timing": {
                    "type": "object",
                    "properties": {
                      "earliest": {
                        "type": "string"
                      },
                      "latest": {
                        "type": "string"
                      },
                      "recurrence": {
                        "type": "string"
                      },
                      "text": {
                        "type": "string"
                      }
                    }
                  },
                  "geography": {
                    "type": "object",
                    "properties": {
                      "text": {
                        "type": "string"
                      },
                      "country": {
                        "type": "string"
                      },
                      "from": {
                        "type": "string"
                      },
                      "to": {
                        "type": "string"
                      }
                    }
                  },
                  "budget": {
                    "type": "object",
                    "properties": {
                      "amount": {
                        "type": "number"
                      },
                      "currency": {
                        "type": "string",
                        "description": "ISO 4217, default USD"
                      },
                      "per": {
                        "type": "string",
                        "description": "total, unit, month, year, load, ..."
                      }
                    }
                  },
                  "requested_fields": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 30,
                    "description": "Result fields you need"
                  },
                  "contact": {
                    "type": "object",
                    "description": "Optional; stored apart and deleted after 180 days",
                    "properties": {
                      "email": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      },
                      "organization": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Recorded, with a reference number"
          },
          "400": {
            "description": "What to fix"
          }
        }
      }
    }
  }
}