# BRAINSALT Server Configurator API

Public JSON API to find a matching BRAINSALT media server for a set of video streams
and to request a quote. No authentication; any HTTP client can call it directly
(cross-origin browser access is limited to approved partner websites). **No prices
are exposed** -
quotes go through the `proposal` action, which emails the responsible sales contact.

Base endpoint (all actions go through one URL, selected by the `action` query parameter):

    https://www.brainsalt.com/embed/ConfiguratorApi.ashx

Machine-readable schema: [openapi.json](https://www.brainsalt.com/embed/openapi.json)

MCP clients: the same functionality is available as a remote MCP server
(Streamable HTTP) at `https://www.brainsalt.com/embed/mcp.ashx` with tools
`find_servers`, `get_server_specs`, `get_configurator_options`,
`get_spec_sheet_link` and `request_proposal`.

## Typical agent workflow

1. `GET ?action=options` - learn the valid option values and help texts.
2. `POST ?action=match` - send the stream requirements, receive matching servers.
3. `GET ?action=specs&article=NR` - full specification of one server.
4. `GET ?action=pdf&article=NR` - branded spec sheet PDF (link for the user).
5. `POST ?action=proposal` - request a quote by email for a chosen server, or send a
   project description without an article number for multi-server projects.

## Actions

### GET ?action=options

Returns all valid values for match requests plus user-facing help texts:
`types`, `codecs`, `formats` (value/label pairs), `outputResolutions`, `outputs`,
`psuTypes`, `genlock`, `addOnCardsB8`, `addOnCardsB9`, `addOnCardDescriptions`
(card name -> description), `genlockMandatoryAboveOutputs`, `texts`, `tooltips`.

### POST ?action=match

Bandwidth and storage are sized **from the streams only**: each content/realtime stream
contributes `Width × Height × Fps × Streams`, summed over all entries, and content storage
adds `DurationMin`. `Outputs` does **not** affect bandwidth or storage - it only sets how
many physical connectors the content is distributed across and selects servers with enough
output channels. Enter each distinct video once at its real source resolution (e.g. the full
canvas); never multiply a resolution by the output count or fold `Outputs` into the bandwidth.

Framerate, pixel `Format` and (for stored content) `DurationMin` are critical for an accurate
match - always try to obtain them. When the customer cannot specify, fall back to these defaults
and state the assumption: `Format` `U_444_10Bit`, `Codec` `Uncompressed`, `Fps` 60, and 15 minutes
of stored content per stream. With NotchLC the same storage holds roughly 4× the duration (storage
drops to 25%), so ~60 minutes fit in the space of 15 uncompressed minutes.

Request body (JSON):

    {
      "Type": "player",                 // "player" (media server) or "cms"
      "ContentStreams": [               // videos played from storage, simultaneously
        { "Width": 3840, "Height": 2160, "Fps": 60, "Streams": 2, "DurationMin": 30 }
      ],
      "RealtimeStreams": [              // live inputs (capture/NDI), no storage
        { "Width": 1920, "Height": 1080, "Fps": 60, "Streams": 1, "DurationMin": 0 }
      ],
      "Codec": "Uncompressed",          // or "NotchLC" (reduces storage to 25%)
      "Format": "U_444_10Bit",          // from options.formats; NotchLC forces U_444_10Bit
      "Outputs": 4,                     // physical outputs, players only
      "OutputResolution": "_4K",        // from options.outputResolutions, players only
      "Psu": "Any",                     // "Any", "Swappable" or "Redundant"
      "Genlock": "None",                // "None" or "Genlock"; forced to "Genlock" above 4 outputs
      "AddOn1": "None", "AddOn2": "None", "AddOn3": "None", "AddOn4": "None"
    }

Response (JSON):

    {
      "Error": null,                    // human-readable error or null
      "Issues": [],                     // non-fatal adjustments, e.g. genlock forced
      "GenlockForced": false,
      "AddOnCards34Cleared": false,
      "ExceedsSingleServerBandwidth": false,  // true -> multi-server project, use proposal
      "EffectiveFormat": "U_444_10Bit",
      "EquivalentContent": { ... },     // streams reduced to one equivalent video
      "EquivalentRealtime": { ... },
      "Bandwidth": {
        "StorageBandwidthMB": 0, "StorageSizeGB": 0, "InternalBandwidthMB": 0,
        "ContentPixelRate": 0.0, "RealtimePixelRate": 0.0
      },
      "Compact": [ ServerResult ], "Entry": [ ServerResult ], "Pro": [ ServerResult ]
    }

ServerResult:

    { "ArticleNr": "B8P4RN0000005T8G8", "Title": "...", "Description": "...",
      "MaxPowerW": 800, "TypicalPowerW": 450, "WeightKg": 14.2,
      "ShipSize": "650x550x280", "CountryOfOrigin": "Germany", "CustomsTariff": "..." }

Rules the API enforces (mirroring the product constraints):

- More than 4 outputs requires the genlock option (multi-GPU sync) and clears add-on
  cards 3 and 4.
- NotchLC always uses format `U_444_10Bit`; storage size and storage bandwidth drop to
  25% while internal processing still needs uncompressed bandwidth.
- If `ExceedsSingleServerBandwidth` is true, no single server fits: such projects are
  realized with multiple synchronized servers. Agents are encouraged to plan the split
  themselves: divide the content between servers along output boundaries and run `match`
  per partition. For blended projections add about 10% storage overhead per server for
  the overlapping blend regions; the genlock option is mandatory on every server of the
  setup. Send the resulting setup as a project description via `proposal` (leave
  `Article` empty) so BRAINSALT can verify and quote it. Detailed planning guide:
  [multi-server-projects.md](https://www.brainsalt.com/embed/multi-server-projects.md)

### GET ?action=specs&article=NR

Full `ServerResult` for one article number, 404 if unknown.

### GET ?action=pdf&article=NR

Generates and returns the spec sheet PDF (content-disposition attachment).

### GET ?action=image&article=NR&view=front|rear[&invert=1]

PNG of the server: `front` is a product photo, `rear` is composed for the exact
configuration encoded in the article number. `invert=1` returns the rear view as
white line art for dark backgrounds. 404 when no image is available.

### POST ?action=proposal

Requests a quote by email. The recipient and any reseller rebate are configured
server-side; the requester's address becomes Reply-To.

    {
      "Article": "B8P4RN0000005T8G8",  // empty/omitted = project request (multi-server)
      "Email": "customer@example.com",  // required, the requester
      "Message": "optional message",    // required for project requests
      "Configuration": "optional short summary of the requirements"
    }

Response: `{ "ok": true }` on success. Duplicate requests are suppressed for a few
minutes.

## Limits

Per-IP rate limits per minute: options/specs/image 120, match 30, pdf 6, proposal 3.
Request bodies are limited (match 64 KB, proposal 8 KB). HTTP 429 on limit, 503 with
Retry-After under load.
