Skip to content

Get Started

There are two ways into Hosted Serving:

  • Callers need the router URL and one shared bearer token.
  • Operators and model developers also need the rnd checkout, its encrypted storage environment, the Simpcat checkout’s Modal credentials, and tag-push access for minting releases.

Ask the instance operator for the router URL and token, then keep them in your shell without committing either value:

Terminal window
export SERVING_ROUTER_URL='https://<serving-router-host>'
read -rsp 'Serving token: ' SERVING_API_TOKEN && export SERVING_API_TOKEN
echo

List active models:

Terminal window
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $SERVING_API_TOKEN" \
"$SERVING_ROUTER_URL/models" | jq

Fetch a model or alias before invoking it. The response contains the generated contract under manifest.serving.spec and reports whether the model is currently servable:

Terminal window
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $SERVING_API_TOKEN" \
"$SERVING_ROUTER_URL/models/fmtts/first-light" | jq

Use that spec—not a hard-coded global form—to construct the request. The required set is per-model: fmtts/first-light currently resolves to a dubbing model that requires style_audio (the performance whose timing and prosody the output follows) in addition to texts and ref_audio. The spec lists what’s required and carries a worked example under manifest.serving.spec.examples:

Terminal window
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $SERVING_API_TOKEN" \
"$SERVING_ROUTER_URL/models/fmtts/first-light" |
jq '.manifest.serving.spec | {required: [.params[] | select(.required) | .name], example: .examples[0].request}'

A minimal request for this model looks like:

{
"request": {
"texts": ["Hello there."],
"ref_audio": {"inline_b64": "<base64 voice-identity sample>"},
"style_audio": {"inline_b64": "<base64 performance to dub>"},
"seed": 1234
}
}

Send it to the same model ID or alias:

Terminal window
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $SERVING_API_TOKEN" \
-H 'Content-Type: application/json' \
--data-binary @request.json \
"$SERVING_ROUTER_URL/models/fmtts/first-light/invoke" > response.json

Inline binary results are JSON-safe: the contract’s audio field arrives as audio_b64. Decode the first FMTTS result with:

Terminal window
jq -r '.results[0].audio_b64' response.json | base64 --decode > result.wav

For batch work, use output_targets so the runtime uploads WAVs directly to presigned Tigris URLs and the router returns small receipts. See HTTP API.

From the rnd checkout, install the FMTTS serving extra:

Terminal window
cd /path/to/rnd
uv sync --project models/fmtts --extra serve

Serving state and storage credentials belong to rnd; the Modal service-user credentials currently belong to Simpcat. The following temporary shell function composes both encrypted environments without copying or printing secrets. Run it from outside the Simpcat checkout, as shown:

Terminal window
export RND_ROOT=/path/to/rnd
export SIMPCAT_ROOT=/path/to/simpcat
export SERVING_OPERATOR="$USER"
serve() (
cd "$RND_ROOT" || exit
npx --yes @dotenvx/dotenvx@1.75.1 run \
--strict --overload \
-f "$SIMPCAT_ROOT/.env.infra" -fk "$SIMPCAT_ROOT/.env.keys" -- \
./with_storage_env.sh ./with_infra_env.sh \
uv run --project models/fmtts --extra serve \
python -m fmtts.serving "$@"
)

SERVING_OPERATOR is written to audit records. If it is unset, the CLI uses the operating-system username.

Check access without changing anything:

Terminal window
serve status

status reads releases, deployment health, model readiness, scratch sessions, orphaned legacy manifests, and remote runtime/* tags. It does not deploy or mutate anything — and because it reads the store, a clean run proves your credentials and instance are right. (serve implementations lists the local implementation table without touching the store, so it proves nothing about access.)