Recover blocked model calls safely: claim an unclaimed backend, keep provider keys server-side, and distinguish the hosted gateway from direct provider APIs.
Why a model call fails before claiming
An unclaimed backend has no outbound internet. It can serve incoming requests and use its own Postgres, but cannot reach api.openai.com, api.anthropic.com or resolve public DNS names. A working homepage or healthy rollout does not prove a model call works. This is a platform restriction, not an SDK error: do not change networking, DNS or security policy to bypass it.A static site’s requests come from the visitor’s browser, not the hosted backend. Never put a provider key in browser code, a VITE_* or NEXT_PUBLIC_* variable, a public runtime config, or a built static asset. Use a backend for secret-bearing calls.
Hosted gateway is not a direct provider key
Forge has a hosted LLM configuration contract:
# In the deployed environment's forge.Bundle:llm = forge.LLM {runtime = forge.OnHosted {}}# In the hosted backend's fw.Workload:env = forge.llm_env()
This is Reliant’s gateway, not a way to use your OpenAI or Anthropic account key. forge.llm_env() declares OPENAI_BASE_URL, OPENAI_API_KEY, ANTHROPIC_BASE_URL, ANTHROPIC_API_KEY, RELIANT_LLM_BASE_URL and RELIANT_LLM_API_KEY. The hosted render leaves them blank for the platform to fill; render success is not proof that credentials were provisioned or a model is reachable.
The Forge deploy/llm skill documents that an unclaimed pod’s gateway variables stay blank until the platform’s in-cluster gateway route is enabled. Do not promise gateway access on an unclaimed deploy. If credentials or a base URL are missing, disable the LLM feature and show an unavailable message; do not crash at startup, silently fall back to a public provider, or retry indefinitely.
On a claimed environment, gateway calls use the environment’s platform-issued key and the org’s credit and limits. A third-party API key and third-party billing do not fund that gateway. Load the installed version’s contract with forge skill load deploy/llm; verify the populated variables without printing their values and make an actual server-side request before reporting it works. This guide does not claim a live gateway inference test.
Recovery: claim first, then use a server-side provider key
This path does not depend on an unclaimed gateway route.
Retrieve the private claim link with forge env status prod --claimable. Give it only to the owner: whoever opens it first owns the deploy. Do not paste it in an issue, PR, repository or public log.
The owner opens that link and claims the deploy. Claiming lifts the backend egress restriction. A backend and managed Postgres need paid app hosting; free signup does not mean free ongoing backend compute. A static site alone cannot hold the key: add a backend under an account first.
Sign in to the owning account with forge login. Use a funded provider API key with access to the model your app requests. Keep it in the managed secret store, not git:
forge secret set --env prod OPENAI_API_KEY
The command reads the value from stdin. Do not put it in shell arguments, command history or agent transcripts.
On the hosted backend workload in deploy/kcl/workloads.k, merge these fields into its existing environment map (keep its database and other settings):
This is a direct OpenAI API connection. Do not also merge forge.llm_env() into this workload: choose the gateway contract or direct provider configuration, not both. The server-side OpenAI SDK can read these variables. For Anthropic, use the server-side Anthropic SDK and ANTHROPIC_API_KEY as a managed secret with ANTHROPIC_BASE_URL = "https://api.anthropic.com". Other clients must explicitly honor the configured endpoint and secret.
If the environment declares control_plane = forge.ControlPlane {claimable = True}, remove the claimable = True field before a signed-in deploy; it deliberately refuses signed-in credentials. Deploy as the signed-in owner, without--claimable:
Only exit 0 proves the release check passed. Exit 1 means a regression to investigate, exit 2 is not proven good, and exit 8 needs more data. See Forge obs.
Exercise your app’s LLM feature through its backend with a small prompt. Confirm a successful model response, not just HTTP 200 for the homepage. Check server logs for missing credentials, DNS/connection failures, provider 401/403, quota errors or unsupported models, without logging secrets or prompt contents. Do not report model access as working until that request succeeds.
What is tested
The Forge fixtures kcl/tests/positive_llm_claimed_direct_key.k, positive_llm_endpoint.k and positive_llm_hosted.k exercise managed-secret wiring, endpoint rendering and the hosted gateway’s blank-variable contract. The MCP recovery unit test checks the claim-first advice and prevents the old unconditional gateway promise. These are configuration and guidance tests, not proof of a live claim, billing setup, provider connectivity or gateway inference. A real request in the owner’s claimed environment remains the final verification.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Reliant","colors":{"primary":"#6E39FF","light":"#8B5CF6","dark":"#6E39FF"},"logo":{"light":"https://mintcdn.com/reliantlabs/NoVh64u5lvQ64evP/images/reliant-logo.svg?fit=max\u0026auto=format\u0026n=NoVh64u5lvQ64evP\u0026q=85\u0026s=23abaccb8c58fa15c19b8080b9ed23ca","dark":"https://mintcdn.com/reliantlabs/NoVh64u5lvQ64evP/images/reliant-logo.svg?fit=max\u0026auto=format\u0026n=NoVh64u5lvQ64evP\u0026q=85\u0026s=23abaccb8c58fa15c19b8080b9ed23ca","href":"/"},"favicon":"https://mintcdn.com/reliantlabs/-3ixAWphy8BqD5FC/favicon.ico?fit=max\u0026auto=format\u0026n=-3ixAWphy8BqD5FC\u0026q=85\u0026s=6f3063bf97699e04c9e99b03d4b29630","navbar":{"links":[{"href":"/changelog","label":"Changelog"},{"href":"https://reliantlabs.io","label":"Main Site"},{"href":"https://github.com/reliant-labs/reliant","label":"GitHub"}],"primary":{"type":"button","label":"Download","href":"https://reliantlabs.io/#downloads"}},"footer":{"socials":{"github":"https://github.com/reliant-labs/reliant","x":"https://x.com/reliantlabs"}},"seo":{"indexing":"navigable"},"icons":{"library":"lucide"},"styling":{"latex":true},"navigation":{"global":{"anchors":[{"anchor":"Main Site","icon":"globe","href":"https://reliantlabs.io"},{"anchor":"GitHub","icon":"github","href":"https://github.com/reliant-labs/reliant"},{"anchor":"Slack","icon":"slack","href":"https://join.slack.com/t/reliant-pn51441/shared_invite/zt-4c7wet6o2-W_Tksw80LaRGxZqUz9GQdA"}]},"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"reliantlabs","hasAudioNarration":false,"actualSubdomain":"reliantlabs","customDomains":["docs.reliantlabs.io"],"gitSource":{"type":"github","owner":"reliant-labs","repo":"reliant","deployBranch":"main","contentDirectory":"docs","isPrivate":true,"mountPath":"","label":""},"entitlements":{"AI_CHAT":{"status":"DISABLED"}},"buildId":"6af837910752de1b553665eb","clientVersion":"0.0.4143","searchFilterCounts":[{"language":"default","tag":"Reliant","count":76},{"language":"default","tag":"Forge","count":38},{"language":"default","tag":"Operate","count":16},{"language":"default","tag":"Automations \u0026 integrations","count":11},{"language":"default","tag":"Deploy \u0026 host","count":11},{"language":"default","tag":"Use Reliant","count":10},{"language":"default","tag":"Reference","count":9},{"language":"default","tag":"Build workflows","count":8},{"language":"default","tag":"Linting","count":8},{"language":"default","tag":"Help","count":7}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":true,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true,"deflection":{"enabled":true,"email":"support@reliantlabs.io","showHelpButton":true}},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[],"starterQuestions":[{"_id":"69b4351bb4eb429b59805b4e","deploymentId":"69b2f13ec3efe3e81c3160ad","questionText":"How does Reliant work?","status":"active","generatedBy":"manual","createdAt":"2026-03-13T16:02:35.042Z","updatedAt":"2026-03-13T16:02:35.042Z","__v":0},{"_id":"69b43515b4eb429b59805b27","deploymentId":"69b2f13ec3efe3e81c3160ad","questionText":"What is Reliant used for?","status":"active","generatedBy":"manual","createdAt":"2026-03-13T16:02:29.723Z","updatedAt":"2026-03-13T16:02:29.723Z","__v":0},{"_id":"69b4350fb4eb429b59805afc","deploymentId":"69b2f13ec3efe3e81c3160ad","questionText":"What can Reliant do?","status":"active","generatedBy":"manual","createdAt":"2026-03-13T16:02:23.336Z","updatedAt":"2026-03-13T16:02:23.336Z","__v":0}],"googleFontsInlined":false},"customCss":[{"filePath":"styles.css","content":":root{--reliant-accent: #6E39FF}#navbar{border-bottom:1px solid rgba(110, 57, 255, 0.16)}#footer{border-top:1px solid rgba(110, 57, 255, 0.16)}#navbar a[href=\"/\"]{display:inline-flex;align-items:center;gap:0.625rem}#navbar a[href=\"/\"] .nav-logo{height:1.5rem;width:auto}#navbar a[href=\"/\"]::after{content:\"Reliant\";display:inline-block;font-size:1.5rem;line-height:1;font-weight:700;letter-spacing:-0.04em;color:inherit}#assistant-entry,#assistant-entry-mobile,#assistant-entry button,#assistant-entry-mobile button{font-weight:600}"}],"page":{"pagePath":"forge/llm-provider","slug":"/forge/llm-provider","pageMetadata":{"title":"LLM calls from a Forge app","sidebarTitle":"LLM provider \u0026 recovery","description":"Recover blocked model calls safely: claim an unclaimed backend, keep provider keys server-side, and distinguish the hosted gateway from direct provider APIs.","href":"/forge/llm-provider"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Recover blocked model calls safely: claim an unclaimed backend, keep provider keys server-side, and distinguish the hosted gateway from direct provider APIs.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,li:`li`,ol:`ol`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,..._provideComponents(),...props.components},{CodeBlock,Heading,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsx(Heading,{level:`2`,id:`why-a-model-call-fails-before-claiming`,children:`Why a model call fails before claiming`}),`\n`,_jsxs(_components.p,{children:[`An `,_jsx(_components.a,{href:`/forge/deploy-without-an-account#limits`,children:`unclaimed backend`}),` has `,_jsx(_components.strong,{children:`no outbound internet`}),`. It can serve incoming requests and use its own Postgres, but cannot reach `,_jsx(_components.code,{children:`api.openai.com`}),`, `,_jsx(_components.code,{children:`api.anthropic.com`}),` or resolve public DNS names. A working homepage or healthy rollout does not prove a model call works. This is a platform restriction, not an SDK error: do not change networking, DNS or security policy to bypass it.`]}),`\n`,_jsxs(_components.p,{children:[`A static site’s requests come from the visitor’s browser, not the hosted backend. `,_jsx(_components.strong,{children:`Never put a provider key in browser code`}),`, a `,_jsx(_components.code,{children:`VITE_*`}),` or `,_jsx(_components.code,{children:`NEXT_PUBLIC_*`}),` variable, a public runtime config, or a built static asset. Use a backend for secret-bearing calls.`]}),`\n`,_jsx(Heading,{level:`2`,id:`hosted-gateway-is-not-a-direct-provider-key`,children:`Hosted gateway is not a direct provider key`}),`\n`,_jsx(_components.p,{children:`Forge has a hosted LLM configuration contract:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`6`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`6`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`# In the deployed environment's forge.Bundle:`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`llm = forge.LLM {runtime = forge.OnHosted {}}`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`# In the hosted backend's fw.Workload:`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`env = forge.llm_env()`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[`This is Reliant’s gateway, not a way to use your OpenAI or Anthropic account key. `,_jsx(_components.code,{children:`forge.llm_env()`}),` declares `,_jsx(_components.code,{children:`OPENAI_BASE_URL`}),`, `,_jsx(_components.code,{children:`OPENAI_API_KEY`}),`, `,_jsx(_components.code,{children:`ANTHROPIC_BASE_URL`}),`, `,_jsx(_components.code,{children:`ANTHROPIC_API_KEY`}),`, `,_jsx(_components.code,{children:`RELIANT_LLM_BASE_URL`}),` and `,_jsx(_components.code,{children:`RELIANT_LLM_API_KEY`}),`. The hosted render leaves them blank for the platform to fill; render success is `,_jsx(_components.strong,{children:`not`}),` proof that credentials were provisioned or a model is reachable.`]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[`The Forge `,_jsx(_components.code,{children:`deploy/llm`}),` skill documents that an unclaimed pod’s gateway variables stay blank until the platform’s in-cluster gateway route is enabled. Do not promise gateway access on an unclaimed deploy. If credentials or a base URL are missing, disable the LLM feature and show an unavailable message; do not crash at startup, silently fall back to a public provider, or retry indefinitely.`]})}),`\n`,_jsxs(_components.p,{children:[`On a claimed environment, gateway calls use the environment’s platform-issued key and the org’s credit and limits. A third-party API key and third-party billing do not fund that gateway. Load the installed version’s contract with `,_jsx(_components.code,{children:`forge skill load deploy/llm`}),`; verify the populated variables without printing their values and make an actual server-side request before reporting it works. This guide does not claim a live gateway inference test.`]}),`\n`,_jsx(Heading,{level:`2`,id:`recovery-claim-first-then-use-a-server-side-provider-key`,children:`Recovery: claim first, then use a server-side provider key`}),`\n`,_jsx(_components.p,{children:`This path does not depend on an unclaimed gateway route.`}),`\n`,_jsxs(_components.ol,{children:[`\n`,_jsxs(_components.li,{children:[`\n`,_jsxs(_components.p,{children:[`Retrieve the private claim link with `,_jsx(_components.code,{children:`forge env status prod --claimable`}),`. Give it only to the owner: whoever opens it first owns the deploy. Do not paste it in an issue, PR, repository or public log.`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsxs(_components.p,{children:[`The owner opens that link and claims the deploy. Claiming lifts the backend egress restriction. A backend and managed Postgres need `,_jsx(_components.a,{href:`/forge/hosting#billing`,children:`paid app hosting`}),`; free signup does not mean free ongoing backend compute. A static site alone cannot hold the key: add a backend under an account first.`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsxs(_components.p,{children:[`Sign in to the owning account with `,_jsx(_components.code,{children:`forge login`}),`. Use a funded provider API key with access to the model your app requests. Keep it in the `,_jsx(_components.a,{href:`/forge/secrets`,children:`managed secret store`}),`, not git:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`forge`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` secret`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` set`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` --env`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` prod`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` OPENAI_API_KEY`})]}),`\n`]})})}),`\n`,_jsx(_components.p,{children:`The command reads the value from stdin. Do not put it in shell arguments, command history or agent transcripts.`}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsxs(_components.p,{children:[`On the hosted backend workload in `,_jsx(_components.code,{children:`deploy/kcl/workloads.k`}),`, merge these fields into its existing environment map (keep its database and other settings):`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`5`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`5`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`env = {`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` OPENAI_BASE_URL = \"https://api.openai.com/v1\"`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` OPENAI_API_KEY = forge.ManagedSecret {name = \"OPENAI_API_KEY\"}`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`}`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[`This is a `,_jsx(_components.strong,{children:`direct OpenAI API`}),` connection. Do not also merge `,_jsx(_components.code,{children:`forge.llm_env()`}),` into this workload: choose the gateway contract or direct provider configuration, not both. The server-side OpenAI SDK can read these variables. For Anthropic, use the server-side Anthropic SDK and `,_jsx(_components.code,{children:`ANTHROPIC_API_KEY`}),` as a managed secret with `,_jsx(_components.code,{children:`ANTHROPIC_BASE_URL = \"https://api.anthropic.com\"`}),`. Other clients must explicitly honor the configured endpoint and secret.`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsxs(_components.p,{children:[`If the environment declares `,_jsx(_components.code,{children:`control_plane = forge.ControlPlane {claimable = True}`}),`, remove the `,_jsx(_components.code,{children:`claimable = True`}),` field before a signed-in deploy; it deliberately refuses signed-in credentials. Deploy as the signed-in owner, `,_jsx(_components.strong,{children:`without`}),` `,_jsx(_components.code,{children:`--claimable`}),`:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`2`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`forge`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` env`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` deploy`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` prod`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`forge`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` obs`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` check`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` prod`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` --wait`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` --json`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Only exit 0 proves the release check passed. Exit 1 means a regression to investigate, exit 2 is not proven good, and exit 8 needs more data. See `,_jsx(_components.a,{href:`/forge/observability/forge-obs`,children:`Forge obs`}),`.`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsx(_components.p,{children:`Exercise your app’s LLM feature through its backend with a small prompt. Confirm a successful model response, not just HTTP 200 for the homepage. Check server logs for missing credentials, DNS/connection failures, provider 401/403, quota errors or unsupported models, without logging secrets or prompt contents. Do not report model access as working until that request succeeds.`}),`\n`]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`what-is-tested`,children:`What is tested`}),`\n`,_jsxs(_components.p,{children:[`The Forge fixtures `,_jsx(_components.code,{children:`kcl/tests/positive_llm_claimed_direct_key.k`}),`, `,_jsx(_components.code,{children:`positive_llm_endpoint.k`}),` and `,_jsx(_components.code,{children:`positive_llm_hosted.k`}),` exercise managed-secret wiring, endpoint rendering and the hosted gateway’s blank-variable contract. The MCP recovery unit test checks the claim-first advice and prevents the old unconditional gateway promise. These are configuration and guidance tests, `,_jsx(_components.strong,{children:`not`}),` proof of a live claim, billing setup, provider connectivity or gateway inference. A real request in the owner’s claimed environment remains the final verification.`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"LLM calls from a Forge app","sidebarTitle":"LLM provider \u0026 recovery","description":"Recover blocked model calls safely: claim an unclaimed backend, keep provider keys server-side, and distinguish the hosted gateway from direct provider APIs.","href":"/forge/llm-provider"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Why a model call fails before claiming","slug":"why-a-model-call-fails-before-claiming","depth":2,"children":[]},{"title":"Hosted gateway is not a direct provider key","slug":"hosted-gateway-is-not-a-direct-provider-key","depth":2,"children":[]},{"title":"Recovery: claim first, then use a server-side provider key","slug":"recovery-claim-first-then-use-a-server-side-provider-key","depth":2,"children":[]},{"title":"What is tested","slug":"what-is-tested","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"reliantlabs","siteName":"Reliant","title":"LLM calls from a Forge app - Reliant","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-11-13T03:51:29.980Z","originalFileLocation":"forge/llm-provider.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Recover blocked model calls safely: claim an unclaimed backend, keep provider keys server-side, and distinguish the hosted gateway from direct provider APIs.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Reliant\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/reliantlabs/uVGZZjE6OJeONTe_/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/reliantlabs/uVGZZjE6OJeONTe_/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/reliantlabs/uVGZZjE6OJeONTe_/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/reliantlabs/uVGZZjE6OJeONTe_/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/reliantlabs/uVGZZjE6OJeONTe_/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/reliantlabs/uVGZZjE6OJeONTe_/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/reliantlabs/uVGZZjE6OJeONTe_/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/reliantlabs/uVGZZjE6OJeONTe_/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/reliantlabs/uVGZZjE6OJeONTe_/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"LLM calls from a Forge app - Reliant\">\u003cmeta data-mint-head property=\"og:description\" content=\"Recover blocked model calls safely: claim an unclaimed backend, keep provider keys server-side, and distinguish the hosted gateway from direct provider APIs.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Reliant\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.reliantlabs.io/forge/llm-provider\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://reliantlabs.mintlify.site/_mintlify/api/og/v2?division=Deploy+%26+host\u0026amp;title=LLM+calls+from+a+Forge+app\u0026amp;description=Recover+blocked+model+calls+safely%3A+claim+an+unclaimed+backend%2C+keep+provider+keys+server-side%2C+and+distinguish+the+hosted+gateway+from+direct+provider+APIs.\u0026amp;theme=ff8ae397513332688c5ea603\u0026amp;signature=dbe9bc0c51080e9614248cad09b2c88b04b43dd04719c52ac5d29ed1df881f48\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"LLM calls from a Forge app - Reliant\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Recover blocked model calls safely: claim an unclaimed backend, keep provider keys server-side, and distinguish the hosted gateway from direct provider APIs.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://reliantlabs.mintlify.site/_mintlify/api/og/v2?division=Deploy+%26+host\u0026amp;title=LLM+calls+from+a+Forge+app\u0026amp;description=Recover+blocked+model+calls+safely%3A+claim+an+unclaimed+backend%2C+keep+provider+keys+server-side%2C+and+distinguish+the+hosted+gateway+from+direct+provider+APIs.\u0026amp;theme=ff8ae397513332688c5ea603\u0026amp;signature=dbe9bc0c51080e9614248cad09b2c88b04b43dd04719c52ac5d29ed1df881f48\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/reliantlabs/uVGZZjE6OJeONTe_/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Reliant\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#6E39FF\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.reliantlabs.io/forge/llm-provider\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/forge/llm-provider.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.reliantlabs.io/#organization\",\"name\":\"Reliant\",\"url\":\"https://docs.reliantlabs.io\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/reliantlabs/NoVh64u5lvQ64evP/images/reliant-logo.svg?fit=max\u0026auto=format\u0026n=NoVh64u5lvQ64evP\u0026q=85\u0026s=23abaccb8c58fa15c19b8080b9ed23ca\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.reliantlabs.io#website\",\"name\":\"Reliant\",\"url\":\"https://docs.reliantlabs.io\",\"publisher\":{\"@id\":\"https://docs.reliantlabs.io/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.reliantlabs.io/forge/llm-provider#webpage\",\"url\":\"https://docs.reliantlabs.io/forge/llm-provider\",\"name\":\"LLM calls from a Forge app\",\"description\":\"Recover blocked model calls safely: claim an unclaimed backend, keep provider keys server-side, and distinguish the hosted gateway from direct provider APIs.\",\"dateModified\":\"2026-11-13T03:51:29.980Z\",\"isPartOf\":{\"@id\":\"https://docs.reliantlabs.io#website\"},\"breadcrumb\":{\"@id\":\"https://docs.reliantlabs.io/forge/llm-provider#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.reliantlabs.io/forge/llm-provider#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Deploy \u0026 host\",\"item\":\"https://docs.reliantlabs.io/forge/environments-and-deploys\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"LLM calls from a Forge app\",\"item\":\"https://docs.reliantlabs.io/forge/llm-provider\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.reliantlabs.io/forge/llm-provider#article\",\"headline\":\"LLM calls from a Forge app\",\"name\":\"LLM calls from a Forge app\",\"description\":\"Recover blocked model calls safely: claim an unclaimed backend, keep provider keys server-side, and distinguish the hosted gateway from direct provider APIs.\",\"url\":\"https://docs.reliantlabs.io/forge/llm-provider\",\"mainEntityOfPage\":{\"@id\":\"https://docs.reliantlabs.io/forge/llm-provider#webpage\"},\"dateModified\":\"2026-11-13T03:51:29.980Z\",\"publisher\":{\"@id\":\"https://docs.reliantlabs.io/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.reliantlabs.io#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Reliant\",\"publisher\":{\"@type\":\"Organization\",\"name\":\"Reliant\"},\"provider\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","agentPointerHref":"/forge/llm-provider.md","siteName":"Reliant","nav":{"global":{"anchors":[{"anchor":"Main Site","icon":"globe","href":"https://reliantlabs.io"},{"anchor":"GitHub","icon":"github","href":"https://github.com/reliant-labs/reliant"},{"anchor":"Slack","icon":"slack","href":"https://join.slack.com/t/reliant-pn51441/shared_invite/zt-4c7wet6o2-W_Tksw80LaRGxZqUz9GQdA"}]},"tabs":[{"tab":"Reliant","pages":[{"title":"Reliant","href":"/getting-started/quick-start"}]},{"tab":"Forge","groups":[{"group":"Get started","pages":[{"title":"Forge: Go + React framework for AI-written code","sidebarTitle":"Forge overview","href":"/forge/overview"},{"title":"Forge quick start: build and deploy a Go + React app","sidebarTitle":"Forge quick start","href":"/forge/quick-start"},{"title":"Deploy a Vite or Next.js app without an account","sidebarTitle":"Deploy without an account","href":"/forge/deploy-without-an-account"}]},{"group":"Build","pages":[{"title":"Project anatomy","href":"/forge/project-anatomy"},{"title":"Adding a feature","href":"/forge/adding-features"},{"group":"Concepts","pages":[{"title":"Two sources of truth","href":"/forge/concepts/two-sources-of-truth"},{"title":"Contracts and dependency injection","href":"/forge/concepts/contracts-and-injection"},{"title":"Middleware and the request path","href":"/forge/concepts/middleware-and-request-path"},{"title":"The config system","href":"/forge/concepts/config-system"},{"title":"Generated vs yours","href":"/forge/concepts/generated-vs-yours"}]}]},{"group":"Deploy \u0026 host","pages":[{"group":"Deploy","pages":[{"title":"Environments \u0026 deploys","href":"/forge/environments-and-deploys"},{"title":"Environments","href":"/forge/deploy/environments"},{"title":"What gets deployed","href":"/forge/deploy/what-gets-deployed"},{"title":"Releases and promotion","href":"/forge/deploy/releases-and-promotion"},{"title":"Self-hosted delivery","href":"/forge/deploy/self-hosted-delivery"},{"title":"Escape hatches","href":"/forge/deploy/escape-hatches"}]},{"title":"Hosting on Reliant","href":"/forge/hosting"},{"title":"Secrets","description":"Setting secrets for a hosted environment, how they reach your workloads, and why you can never read one back","href":"/forge/secrets"},{"title":"LLM calls from a Forge app","sidebarTitle":"LLM provider \u0026 recovery","href":"/forge/llm-provider"},{"title":"Custom domains","description":"Serve your project on a hostname you own — the DNS records to publish, why the TXT record exists, and how long each step takes","href":"/forge/custom-domains"},{"title":"Forge sign-in","href":"/forge/sign-in"}]},{"group":"Operate","pages":[{"group":"Observability","pages":[{"title":"Observability","href":"/forge/observability"},{"title":"What every boundary records","href":"/forge/observability/signals"},{"title":"Where the telemetry goes","href":"/forge/observability/where-it-goes"},{"title":"Querying with forge obs","href":"/forge/observability/forge-obs"},{"title":"Hosted dashboards, alerts and issues","href":"/forge/observability/dashboards-and-alerts"},{"title":"Health probes and graceful shutdown","href":"/forge/observability/health-and-shutdown"},{"title":"Forge MCP server: deploy from Claude Code, Cursor, Codex","sidebarTitle":"Why this helps an agent","href":"/forge/observability/agents"}]},{"group":"Linting","pages":[{"title":"Lint AI-generated code: what forge lint checks","sidebarTitle":"Linting: what forge lint checks","href":"/forge/linting/overview"},{"title":"Linting: new rules warn before they gate","href":"/forge/linting/rollout"},{"title":"Lint rule index","href":"/forge/linting/rules-index"},{"title":"Lint rules: migrations","href":"/forge/linting/rules-migrations"},{"title":"Lint rules: conventions, contracts and Deps","href":"/forge/linting/rules-conventions"},{"title":"Lint rules: schema markers and write policy","href":"/forge/linting/rules-schema-and-writes"},{"title":"Lint rules: deploy, secrets and the composition graph","href":"/forge/linting/rules-deploy-and-secrets"},{"title":"Lint rules: tracing, tests, generated files and ownership","href":"/forge/linting/rules-tracing-and-tests"}]},{"title":"Troubleshooting","href":"/forge/troubleshooting"}]},{"group":"Forge CLI","pages":[{"title":"Forge through Reliant","href":"/cli/forge"}]}]}]},"basePath":"","title":"LLM calls from a Forge app - Reliant","buildId":"dpl_Fyv1NbzwP68g2bEH2Roy98uY1zmE","deploymentId":"dpl_Fyv1NbzwP68g2bEH2Roy98uY1zmE","dynamics":[],"chunks":[]}