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
rndcheckout, its encrypted storage environment, the Simpcat checkout’s Modal credentials, and tag-push access for minting releases.
Call an existing model
Section titled “Call an existing model”Ask the instance operator for the router URL and token, then keep them in your shell without committing either value:
export SERVING_ROUTER_URL='https://<serving-router-host>'read -rsp 'Serving token: ' SERVING_API_TOKEN && export SERVING_API_TOKENechoList active models:
curl --fail-with-body --silent --show-error \ -H "Authorization: Bearer $SERVING_API_TOKEN" \ "$SERVING_ROUTER_URL/models" | jqFetch 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:
curl --fail-with-body --silent --show-error \ -H "Authorization: Bearer $SERVING_API_TOKEN" \ "$SERVING_ROUTER_URL/models/fmtts/first-light" | jqUse 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:
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:
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.jsonInline binary results are JSON-safe: the contract’s audio field arrives as audio_b64. Decode the first FMTTS result with:
jq -r '.results[0].audio_b64' response.json | base64 --decode > result.wavFor batch work, use output_targets so the runtime uploads WAVs directly to presigned Tigris URLs and the router returns small receipts. See HTTP API.
Prepare the operator CLI
Section titled “Prepare the operator CLI”From the rnd checkout, install the FMTTS serving extra:
cd /path/to/rnduv sync --project models/fmtts --extra serveServing 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:
export RND_ROOT=/path/to/rndexport SIMPCAT_ROOT=/path/to/simpcatexport 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:
serve statusstatus 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.)
Next steps
Section titled “Next steps”- To change inference code, start with Scratch Sessions.
- To publish or deploy, follow Preview and Release.
- To integrate a caller, use the complete HTTP API reference.