# For agents: submit a capacity requirement (Nodehour)

Operated by Tempered Technologies LLC, 8 The Green STE B, Dover, DE 19901.

POST https://nodehour.forevenza.com/_c/request with content-type application/json. `need` is required; everything in `specifications` is optional. A partial requirement is accepted; required fields you leave out come back as `missing_specifications` and stay open on the request.

Also served: https://nodehour.forevenza.com/openapi.json, https://nodehour.forevenza.com/mcp (MCP; its tool list includes the request tool, same envelope), https://nodehour.forevenza.com/llms.txt. The HTML page https://nodehour.forevenza.com/for-agents/ has a console that sends an edited envelope to the same endpoint and shows the response as returned, with the open items (missing specifications, and contact when none was given) read out of it.

## Minimal request

```json
{
  "action": "quote",
  "need": "Enough capacity, any chip, for ~20,000 GPU-hours of fine-tuning in Q1 2027, US, InfiniBand preferred.",
  "specifications": {
    "gpu_hours": 20000,
    "region": "United States",
    "interconnect": "INFINIBAND",
    "any_accelerator": true,
    "start_text": "Q1 2027",
    "inferred_start_window": {
      "earliest": "2027-01-01",
      "latest": "2027-03-31",
      "from_words": "Q1 2027",
      "basis": "inferred from the words; to confirm"
    }
  },
  "quantity": {
    "amount": 20000,
    "unit": "GPU-hours"
  },
  "geography": {
    "text": "United States",
    "country": "US"
  }
}
```

## Response

```json
{
  "id": "the reference, quote it in any follow-up",
  "facets": { "actionability": "the endpoint's label; see Dates", "missing_specifications": ["..."] },
  "missing_specifications": ["$.specifications.accelerator", "$.specifications.accelerator_count", "$.specifications.start_earliest", "$.specifications.reply_email_included"],
  "what_happens": "what Nodehour does next with this request"
}
```

- id: the reference.
- missing_specifications: required fields not sent, including reply_email_included when no reply address was given; Nodehour proposes or asks.
- facets.actionability: the endpoint's own label. DATED means the envelope carried a timing object, not that a start date is set (see Dates).
- what_happens: the next step.
- errors (HTTP 400, nothing recorded): a value of the wrong type (words in a date field among them), a number outside a field's minimum or maximum, or a string over a field's maximum length. A request without need is refused too. Nothing else is.

```
HTTP 400
{
  "errors": ["specifications.start_earliest: ... (the field is named)"]
}
```

specifications.reply_email_included is a required field. Set it to true whenever you send contact.email. While it is absent, missing_specifications lists $.specifications.reply_email_included: no reply address was given, so no one can reply and nothing goes to providers until a request with an address arrives. The endpoint does not read contact.email to clear it, and the address itself belongs only in contact.email. A response is a receipt, not a quote, a reservation or an order. Contact details are deleted after 180 days.

## Dates

- start_earliest, start_latest, finish_by and finish_latest are whole numbers YYYYMMDD: 4 January 2027 is 20270104. Send them that way, or leave them out.
- Anything that is not a whole number is refused: "Q1 2027", "sometime in Q1" and "2027-01-04" written as a string all return HTTP 400 with errors naming the field, and nothing is recorded. A whole number below 20260101 or above 20991231 is refused the same way.
- A whole number in that range is recorded as sent. It is not checked as a calendar day, so check 20270231-style mistakes yourself.
- A start known only in words goes in specifications.start_text, with an optional specifications.inferred_start_window {earliest, latest as YYYY-MM-DD, from_words, basis}. Leave start_earliest out: it then stays in missing_specifications.
- facets.actionability is DATED whenever the envelope has a top-level timing object, whatever it holds. Send timing only with dates, written there as YYYY-MM-DD strings (timing belongs to the envelope itself, not to the specification fields), and read whether the start is set from missing_specifications.
- Enum values are not checked: a value outside the listed set is recorded as sent.

The HTML console names anything in a date field that is not a whole number YYYYMMDD, and a timing object without a date, before sending. It offers to fix it (a YYYY-MM-DD string becomes the number; words move to specifications.start_text and the field is left out) or to send exactly as written, which the endpoint refuses. When nothing about the start is sent it reads a quarter or month in need into specifications.inferred_start_window (marked inferred) and lists each change. A direct POST gets none of these adjustments; the endpoint's own checks are the ones listed above.

Top-level timing {earliest, latest, text} only with real dates.

## Specification fields

- intent_text: The team's own description of the run, verbatim
- workload_kind: PRETRAIN, FINE_TUNE, INFERENCE, GPU_HOURS or KNOWN_SPEC
- model_params_b, training_tokens_b, target_tokens_per_s: The workload, when known
- start_earliest, start_latest, finish_by, finish_latest: Dates as whole numbers YYYYMMDD (20270104). Anything else is refused with HTTP 400 naming the field. See "Dates" below
- start_text: A start you can only describe ("Q1 2027", "sometime in Q1"), verbatim. It sets no date: start_earliest stays in missing_specifications
- inferred_start_window: Optional {earliest, latest} as YYYY-MM-DD, with from_words and basis, when you infer a window from words. Recorded as an inference to confirm, not as a start
- accelerator, accelerator_count: Chip and count; omit to have them proposed
- accelerator_alternatives, min_accelerator_count: Substitutes and the smallest acceptable count
- any_accelerator: true when any chip that fits is acceptable; each is sized and quoted. "Any chip" in the need sentence is read the same way
- start_latest: The end of the start window; a range such as 4 to 15 January is start_earliest 20270104, start_latest 20270115
- deadline_basis, finish_latest: With a start window: FIXED_DATE (finish_by holds whatever the start), ASSUMED_FIXED_DATE (not yet answered) or WEEKS_FROM_START (finish_latest is the last day)
- reserved_gpu_hours_by_term, sizing_by_accelerator: Reserved GPU-hours per term, and the size on each chip to quote with its sizing_basis (RUN, MEASURED, SCALED, SHAPE, SPEC)
- alternative_quoted: Whether the alternative configuration is quoted too
- gpu_hours: Work in GPU-hours, if known
- terms_to_quote: Array of 6_WEEKS, 1_MONTH, 2_MONTHS, 3_MONTHS, 6_MONTHS, 1_YEAR
- interconnect: INFINIBAND, ROCE, ETHERNET or ANY
- region, residency_required: Where, and whether it is a hard constraint
- deployment: SLURM, KUBERNETES, BARE_METAL, VIRTUAL_MACHINES or MANAGED
- commitment: READY_TO_SIGN, COMPARING_QUOTES or EXPLORING
- budget_ceiling_usd_per_gpu_hour: Your own ceiling; not an offer
- open_questions, conflicts: Plain sentences, carried into the reply
- reply_email_included: Required. true when contact.email carries the reply address; absent, it is listed in missing_specifications as the signal that no one can reply
