Salesforce Models API setup for an AI coding agent
The Salesforce Models API (https://api.salesforce.com/ai/gpt/v1) is
authenticated with a signed OrgJWT (obtained via client_credentials with
the sfap_api scope — see scripts/get-orgjwt.sh; no proxy). That auth and the
base URL are the same for any agent. How each agent then talks to the
endpoint is agent-specific: Anthropic clients (Claude Code and the Claude
Agent SDK) route through Bedrock mode (the env vars in Step 3), whereas
other agents (e.g. Codex) use their own client config against the same endpoint
and token — Bedrock mode does not apply to them.
The steps below are the Claude Code / Claude Agent SDK reference
implementation (Bedrock mode + a JSON settings file + an API-key helper). For
a non-Bedrock agent, reuse the OrgJWT auth (Step 1) and the base URL, and apply
the equivalent client settings in that agent's own config location instead of
the Bedrock env vars.
Bundled scripts are in scripts/. Path placeholders below: <SKILL> = the
absolute path to this skill's own directory (the folder containing this
SKILL.md; resolve it from the skill path in context). <ABS> = the absolute
path to the user's project root. Always emit fully resolved absolute paths —
the API-key helper runs from an undefined working directory, so relative paths
break it.
Prerequisite
A connected app in the org with the sfap_api OAuth scope and the
client_credentials flow enabled (consumer key/secret + a run-as user).
Setup steps: https://developer.salesforce.com/docs/ai/agentforce/guide/access-models-api-with-rest.html
curl + jq installed.
Inputs to collect
SF_INSTANCE_URL — org My Domain, e.g. https://acme.my.salesforce.com
SF_CLIENT_ID, SF_CLIENT_SECRET — connected-app consumer key/secret
- Models API base URL:
https://api.salesforce.com/ai/gpt/v1
- Model: a fully qualified
sfdc_ai__… name, e.g.
sfdc_ai__DefaultBedrockAnthropicClaude46Sonnet
(full list: https://developer.salesforce.com/docs/ai/agentforce/guide/supported-models.html)
- Scope: project (
<cwd>/.claude/settings.json, default) or user (~/.claude/settings.json) — reference-agent settings paths
- Headers —
<FEAT> = x-client-feature-id (default ai-platform-models-connected-app),
<APP> = x-sfdc-app-context (default EinsteinGPT). Used in the Step 2 verify curl
and in ANTHROPIC_CUSTOM_HEADERS.
Steps (reference implementation)
Concrete values for a JSON-settings + API-key-helper agent. Reuse the OrgJWT
auth, verify curl, and base URL verbatim for any agent; adapt the settings-file
location and env-var wiring to the target agent.
- Write
<project>/.claude/.orgjwt.env (chmod 600), gitignore it:SF_INSTANCE_URL="..."
SF_CLIENT_ID="..."
SF_CLIENT_SECRET="..."
- Verify — must return
200 before writing settings:TOKEN=$(bash <SKILL>/scripts/get-orgjwt.sh <ABS>/.claude/.orgjwt.env)
curl -s -o /dev/null -w '%{http_code}\n' \
<MODELS_API_URL>/model/<MODEL>/invoke-with-response-stream \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-H 'x-client-feature-id: <FEAT>' -H 'x-sfdc-app-context: <APP>' \
--data '{"anthropic_version":"bedrock-2023-05-31","max_tokens":16,"messages":[{"role":"user","content":"hi"}]}'
- Write
.claude/settings.json (merge into existing; keep other keys):{
"apiKeyHelper": "bash <SKILL>/scripts/get-orgjwt.sh <ABS>/.claude/.orgjwt.env",
"model": "<MODEL>",
"env": {
"ANTHROPIC_AUTH_TOKEN": "",
"CLAUDE_CODE_USE_BEDROCK": "1",
"CLAUDE_CODE_SKIP_BEDROCK_AUTH": "1",
"ANTHROPIC_BEDROCK_BASE_URL": "<MODELS_API_URL>",
"ANTHROPIC_SMALL_FAST_MODEL": "<MODEL>",
"ANTHROPIC_DEFAULT_MODEL": "<MODEL>",
"ANTHROPIC_CUSTOM_HEADERS": "x-client-feature-id: <FEAT>\nx-sfdc-app-context: <APP>"
}
}
Use absolute paths in apiKeyHelper. (<FEAT> / <APP> defaults are in
"Inputs to collect" above.)
- Tell the admin to fully restart the agent (
claude for the reference agent) —
settings and the API-key helper load at startup only.
Capturing as a runbook (when asked to document, not apply)
If the user wants the setup written up for review instead of applied to their
machine (e.g. "save it as a Markdown runbook"), write all of the above into
the requested file (e.g. models-api-setup-runbook.md), in order and self-contained:
the exact .orgjwt.env contents, the chmod 600 + gitignore note, the
verification curl (with the "must be 200 before writing settings" note), the
full settings.json block with every key from Step 3, and the final
"fully restart claude" step. Don't omit any of the nine settings.json keys.
Verify before finishing
Must be exact (each prevents a specific failure)
"ANTHROPIC_AUTH_TOKEN": "" — clears any global token that would otherwise
outrank apiKeyHelper (precedence: ANTHROPIC_AUTH_TOKEN > ANTHROPIC_API_KEY
apiKeyHelper). Without it → wrong/old bearer → 401/404.
CLAUDE_CODE_USE_BEDROCK=1 — activates the Bedrock API client; without it
Claude Code uses the standard Anthropic API protocol and ignores
ANTHROPIC_BEDROCK_BASE_URL entirely, so every call bypasses the Models API.
CLAUDE_CODE_SKIP_BEDROCK_AUTH=1 — else Claude Code overwrites Authorization
with AWS SigV4 and the OrgJWT never lands.
apiKeyHelper must be invoked as bash <path> <credsfile> (avoids exit-126).
- Model must be a fully qualified
sfdc_ai__… name (see supported models).
- Auth is the OrgJWT from
client_credentials (a signed JWT, 2 dots, scope
sfap_api) — NOT sf org display (unsigned session token → 404). sf CLI
has no client_credentials command; the helper calls /services/oauth2/token.
- Only
ANTHROPIC_BEDROCK_BASE_URL routes; no tenant-id header needed.
Diagnose
| Error |
Meaning |
Check first |
401 |
Token is not a valid OrgJWT |
Connected App sfap_api scope, client_credentials flow enabled, consumer key/secret in .orgjwt.env; ANTHROPIC_AUTH_TOKEN not cleared to "" |
404 |
Token valid but model/env/org not routable |
Fully qualified sfdc_ai__… model alias, ANTHROPIC_BEDROCK_BASE_URL exactly https://api.salesforce.com/ai/gpt/v1, org entitled for the Models API, ANTHROPIC_AUTH_TOKEN cleared |
model not available |
Non-alias model id |
Replace with a fully qualified sfdc_ai__… alias (see supported models) |
1---
2name: platform-models-api-configure
3description: "Configure (or troubleshoot) an AI coding agent or CLI to route through the Salesforce Models API using a signed OrgJWT. Use this skill when pointing an agent at the Salesforce model endpoint (api.salesforce.com/ai/gpt/v1), setting up OrgJWT / Bedrock-mode auth, wiring the agent's settings, API-key helper, and credentials file for the Salesforce endpoint, or fixing Models API 401 / 404 / \"model not available\" errors. DO NOT TRIGGER when the user needs to create or configure the Salesforce Connected App itself (use integration-connectivity-connected-app-configure) or set up Named Credentials / callout auth (use integration-connectivity-generate)."
4metadata:
5 cliTools:
6 - tool: ["curl"]
7 semver: ">=7.29.0"
8 - tool: ["jq"]
9 semver: ">=1.6.0"
10 - tool: ["sf"]
11 semver: ">=2.0.0"
12 relatedSkills:
13 - "integration-connectivity-connected-app-configure"
14 - "integration-connectivity-generate"
15 version: "1.0"
16 domains: ["Platform", "Agentforce"]
17---
18
19# Salesforce Models API setup for an AI coding agent
20
21The Salesforce Models API (`https://api.salesforce.com/ai/gpt/v1`) is
22authenticated with a signed **OrgJWT** (obtained via `client_credentials` with
23the `sfap_api` scope — see `scripts/get-orgjwt.sh`; no proxy). That auth and the
24base URL are the same for **any** agent. How each agent then talks to the
25endpoint is agent-specific: Anthropic clients (**Claude Code** and the **Claude
26Agent SDK**) route through **Bedrock mode** (the env vars in Step 3), whereas
27other agents (e.g. Codex) use their own client config against the same endpoint
28and token — Bedrock mode does **not** apply to them.
29
30The steps below are the **Claude Code / Claude Agent SDK reference
31implementation** (Bedrock mode + a JSON settings file + an API-key helper). For
32a non-Bedrock agent, reuse the OrgJWT auth (Step 1) and the base URL, and apply
33the equivalent client settings in that agent's own config location instead of
34the Bedrock env vars.
35
36Bundled scripts are in `scripts/`. Path placeholders below: `<SKILL>` = the
37absolute path to **this skill's own directory** (the folder containing this
38`SKILL.md`; resolve it from the skill path in context). `<ABS>` = the absolute
39path to the user's project root. Always emit fully resolved absolute paths —
40the API-key helper runs from an undefined working directory, so relative paths
41break it.
42
43## Prerequisite
44
45A connected app in the org with the **`sfap_api`** OAuth scope and the
46**client_credentials** flow enabled (consumer key/secret + a run-as user).
47Setup steps: https://developer.salesforce.com/docs/ai/agentforce/guide/access-models-api-with-rest.html
48`curl` + `jq` installed.
49
50## Inputs to collect
51
52- `SF_INSTANCE_URL` — org My Domain, e.g. `https://acme.my.salesforce.com`
53- `SF_CLIENT_ID`, `SF_CLIENT_SECRET` — connected-app consumer key/secret
54- Models API base URL: `https://api.salesforce.com/ai/gpt/v1`
55- Model: a fully qualified `sfdc_ai__…` name, e.g.
56 `sfdc_ai__DefaultBedrockAnthropicClaude46Sonnet`
57 (full list: https://developer.salesforce.com/docs/ai/agentforce/guide/supported-models.html)
58- Scope: project (`<cwd>/.claude/settings.json`, default) or user (`~/.claude/settings.json`) — reference-agent settings paths
59- Headers — `<FEAT>` = `x-client-feature-id` (default `ai-platform-models-connected-app`),
60 `<APP>` = `x-sfdc-app-context` (default `EinsteinGPT`). Used in the Step 2 verify curl
61 and in `ANTHROPIC_CUSTOM_HEADERS`.
62
63## Steps (reference implementation)
64
65Concrete values for a JSON-settings + API-key-helper agent. Reuse the OrgJWT
66auth, verify curl, and base URL verbatim for any agent; adapt the settings-file
67location and env-var wiring to the target agent.
68
691. Write `<project>/.claude/.orgjwt.env` (chmod 600), gitignore it:
70 ```ini
71 SF_INSTANCE_URL="..."
72 SF_CLIENT_ID="..."
73 SF_CLIENT_SECRET="..."
74 ```
752. Verify — must return `200` before writing settings:
76 ```bash
77 TOKEN=$(bash <SKILL>/scripts/get-orgjwt.sh <ABS>/.claude/.orgjwt.env)
78 curl -s -o /dev/null -w '%{http_code}\n' \
79 <MODELS_API_URL>/model/<MODEL>/invoke-with-response-stream \
80 -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
81 -H 'x-client-feature-id: <FEAT>' -H 'x-sfdc-app-context: <APP>' \
82 --data '{"anthropic_version":"bedrock-2023-05-31","max_tokens":16,"messages":[{"role":"user","content":"hi"}]}'
83 ```
843. Write `.claude/settings.json` (merge into existing; keep other keys):
85 ```json
86 {
87 "apiKeyHelper": "bash <SKILL>/scripts/get-orgjwt.sh <ABS>/.claude/.orgjwt.env",
88 "model": "<MODEL>",
89 "env": {
90 "ANTHROPIC_AUTH_TOKEN": "",
91 "CLAUDE_CODE_USE_BEDROCK": "1",
92 "CLAUDE_CODE_SKIP_BEDROCK_AUTH": "1",
93 "ANTHROPIC_BEDROCK_BASE_URL": "<MODELS_API_URL>",
94 "ANTHROPIC_SMALL_FAST_MODEL": "<MODEL>",
95 "ANTHROPIC_DEFAULT_MODEL": "<MODEL>",
96 "ANTHROPIC_CUSTOM_HEADERS": "x-client-feature-id: <FEAT>\nx-sfdc-app-context: <APP>"
97 }
98 }
99 ```
100 Use absolute paths in `apiKeyHelper`. (`<FEAT>` / `<APP>` defaults are in
101 "Inputs to collect" above.)
1024. Tell the admin to fully restart the agent (`claude` for the reference agent) —
103 settings and the API-key helper load at startup only.
104
105### Capturing as a runbook (when asked to document, not apply)
106
107If the user wants the setup written up for review instead of applied to their
108machine (e.g. "save it as a Markdown runbook"), write **all** of the above into
109the requested file (e.g. `models-api-setup-runbook.md`), in order and self-contained:
110the exact `.orgjwt.env` contents, the `chmod 600` + gitignore note, the
111verification curl (with the "must be `200` before writing settings" note), the
112full `settings.json` block with every key from Step 3, and the final
113"fully restart `claude`" step. Don't omit any of the nine `settings.json` keys.
114
115## Verify before finishing
116
117- [ ] `.claude/.orgjwt.env` created, `chmod 600`, and gitignored
118- [ ] Verification curl returned HTTP `200` before `settings.json` was written
119- [ ] `ANTHROPIC_AUTH_TOKEN` set to `""` in `settings.json`
120- [ ] `CLAUDE_CODE_USE_BEDROCK` set to `"1"`
121- [ ] `CLAUDE_CODE_SKIP_BEDROCK_AUTH` set to `"1"`
122- [ ] `ANTHROPIC_BEDROCK_BASE_URL` is exactly `https://api.salesforce.com/ai/gpt/v1` (no trailing slash/path)
123- [ ] `model`, `ANTHROPIC_DEFAULT_MODEL`, and `ANTHROPIC_SMALL_FAST_MODEL` all use the fully qualified `sfdc_ai__…` alias
124- [ ] `ANTHROPIC_CUSTOM_HEADERS` contains `x-client-feature-id` and `x-sfdc-app-context`
125- [ ] `apiKeyHelper` uses absolute paths (`bash <SKILL>/scripts/get-orgjwt.sh <ABS>/.claude/.orgjwt.env`)
126- [ ] User told to fully restart `claude`
127
128## Must be exact (each prevents a specific failure)
129
130- `"ANTHROPIC_AUTH_TOKEN": ""` — clears any global token that would otherwise
131 outrank `apiKeyHelper` (precedence: `ANTHROPIC_AUTH_TOKEN` > `ANTHROPIC_API_KEY`
132 > `apiKeyHelper`). Without it → wrong/old bearer → 401/404.
133- `CLAUDE_CODE_USE_BEDROCK=1` — activates the Bedrock API client; without it
134 Claude Code uses the standard Anthropic API protocol and ignores
135 `ANTHROPIC_BEDROCK_BASE_URL` entirely, so every call bypasses the Models API.
136- `CLAUDE_CODE_SKIP_BEDROCK_AUTH=1` — else Claude Code overwrites `Authorization`
137 with AWS SigV4 and the OrgJWT never lands.
138- `apiKeyHelper` must be invoked as `bash <path> <credsfile>` (avoids exit-126).
139- Model must be a fully qualified `sfdc_ai__…` name (see supported models).
140- Auth is the OrgJWT from `client_credentials` (a signed JWT, 2 dots, scope
141 `sfap_api`) — NOT `sf org display` (unsigned session token → 404). `sf` CLI
142 has no client_credentials command; the helper calls `/services/oauth2/token`.
143- Only `ANTHROPIC_BEDROCK_BASE_URL` routes; no tenant-id header needed.
144
145## Diagnose
146
147| Error | Meaning | Check first |
148|-------|---------|-------------|
149| `401` | Token is not a valid OrgJWT | Connected App `sfap_api` scope, `client_credentials` flow enabled, consumer key/secret in `.orgjwt.env`; `ANTHROPIC_AUTH_TOKEN` not cleared to `""` |
150| `404` | Token valid but model/env/org not routable | Fully qualified `sfdc_ai__…` model alias, `ANTHROPIC_BEDROCK_BASE_URL` exactly `https://api.salesforce.com/ai/gpt/v1`, org entitled for the Models API, `ANTHROPIC_AUTH_TOKEN` cleared |
151| `model not available` | Non-alias model id | Replace with a fully qualified `sfdc_ai__…` alias (see supported models) |