# 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

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.
