{"id":211,"date":"2026-08-18T21:00:00","date_gmt":"2026-08-18T18:00:00","guid":{"rendered":"https:\/\/john-nessime.com\/blog\/?p=211"},"modified":"2026-08-06T22:34:54","modified_gmt":"2026-08-06T19:34:54","slug":"salesforce-ai-assistant-amazon-bedrock","status":"publish","type":"post","link":"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/","title":{"rendered":"Building a Salesforce AI Assistant on Amazon Bedrock Without Leaking Your CRM"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The demo went fine. Someone from the support team asked the assistant which accounts looked at risk this quarter, and it answered. Good answer, too. Right opportunities, right open cases, sensible summary.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then somebody noticed that the person who asked couldn&#8217;t see half those opportunities in the Salesforce UI. Different role, different branch of the hierarchy. They saw them through the assistant.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nothing errored. No 403, no exception in the logs, no alert. The integration user had access, so the API call succeeded, so the model got the records, so it summarised them. Every layer did exactly what it was configured to do. That is the failure mode that should worry you most when you build a Salesforce AI assistant with Amazon Bedrock: it is silent, it is correct-looking, and you will not find it in a load test.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This post covers what actually goes wrong when you wire a large language model into a CRM: which door AWS closed on the entry path, whose permissions your agent is really running under, how to shape the tool surface so the model can&#8217;t wander, and what happens to your org&#8217;s API budget when an agent starts looping. There&#8217;s a build walkthrough, but the build is the easy part.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Two doors closed while you were reading the old tutorial<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Most of what ranks for this topic was written against an architecture you can no longer build in a fresh account. Both vendors changed the front door within a few months of each other, and the tutorials haven&#8217;t caught up. Check this before you follow anything, including this post.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">On the AWS side: Bedrock Agents is now Bedrock Agents Classic<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">AWS renamed the original Bedrock Agents service to Bedrock Agents Classic and put it into maintenance mode. It stopped accepting new customers on 30 July 2026. This one is worth stating with the date attached, because the date is the whole point.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The mechanics matter more than the branding:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Only <code>CreateAgent<\/code> and <code>InvokeInlineAgent<\/code> are restricted. Every other API stays available to everyone.<\/li>\n\n<li>Allowlisting is per AWS account, based on whether that account used Bedrock Agents in the previous twelve months. Multi-account estates get a split result: your old sandbox works, your new production account doesn&#8217;t.<\/li>\n\n<li>A non-allowlisted account calling <code>CreateAgent<\/code> gets an <code>AccessDeniedException<\/code> with HTTP 403, not a quota error. If you&#8217;re debugging IAM and seeing 403s, this is a plausible cause that no policy change will fix.<\/li>\n\n<li>There is no exception process. AWS documents this plainly.<\/li>\n\n<li>The model catalogue inside Classic is frozen. Bedrock itself keeps getting new models; the Classic orchestration layer does not.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The replacement is Amazon Bedrock AgentCore. It splits into a managed harness (config-driven, closest to the old experience) and code-defined agents on the AgentCore runtime (bring Strands, LangGraph, CrewAI, or your own loop). For a Salesforce assistant, the harness is usually enough, and you can move to code later without re-plumbing the tools.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Existing Classic agents keep running. There&#8217;s no announced end-of-life. But a frozen model catalogue sets a ceiling on the thing, and if your team can&#8217;t recreate the agent in a new account, your disaster recovery story has a hole in it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">On the Salesforce side: Connected Apps gave way to External Client Apps<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Salesforce blocked creation of new Connected Apps through both UI and API, with package installation as the exception. External Client Apps are the replacement. Existing Connected Apps keep working and keep their OAuth flows, so this is a wall on new creation rather than a sunset.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The practical symptom is that the <em>New Connected App<\/em> button is greyed out with no explanation. Every AWS-to-Salesforce tutorial that opens with &#8220;first, create a Connected App&#8221; stops working right there.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One difference matters for agent work specifically: External Client Apps do not support the username-password OAuth flow at all. If your integration design assumed you&#8217;d stash a service account username and password somewhere and call it a day, that design is gone. Which is a good thing, and it forces the question the next section is about.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Failure family one: whose permissions is the assistant using?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is the one from the opening, and it&#8217;s the reason most of these projects should slow down before they ship.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Salesforce&#8217;s security model is genuinely good. An OAuth token runs in a user context, and object permissions, field-level security, and sharing rules all still apply to API calls. A token with the <code>api<\/code> scope does not bypass CRUD, FLS, or sharing. People hear that and relax.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The catch is the phrase &#8220;a user context.&#8221; Which user?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you authenticate the gateway to Salesforce with client credentials, every request from every person runs as one integration user. Salesforce enforces that user&#8217;s permissions perfectly. It has no idea a different human asked the question. You&#8217;ve built what&#8217;s sometimes called a multiplexor: one licence fronting many people, with all the real access control now living in your application layer instead of the platform&#8217;s.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At that point the sharing model is decoration. A prompt that talks the model into calling a query tool with a wider filter returns data the asker was never entitled to, and Salesforce logs it as a legitimate read by a legitimate user. There&#8217;s nothing to alert on, because nothing went wrong.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Two-legged versus three-legged, and when each is honest<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">AgentCore Gateway supports both client credentials (two-legged) and authorization code (three-legged) flows for reaching downstream targets, and handles ingress and egress authorisation separately. That distinction is the control you need.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Two-legged is honest when the data is genuinely org-wide.<\/strong> Product catalogues, published knowledge articles, case deflection content. Nobody&#8217;s entitlement varies, so a single service identity represents the truth.<\/li>\n\n<li><strong>Three-legged is the only correct answer for record data.<\/strong> Opportunities, accounts, contacts, custom objects with sharing rules on them. The token is minted for the actual human, Salesforce applies their sharing, and the answer is bounded by what they could have looked up themselves.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Three-legged costs you something real, and I&#8217;d rather say so than pretend it&#8217;s free. You need per-user consent, token storage, refresh handling, and a way to deal with a user whose token has expired mid-conversation. AgentCore Identity provides a token vault and handles the consent flow, including binding the authorisation URL to the session that started it so a shared link can&#8217;t be completed by someone else. It&#8217;s still more moving parts than a single secret in Secrets Manager.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pay the cost anyway for record data. The alternative is a system whose blast radius is &#8220;everything the integration user can see,&#8221; discovered by a customer or an auditor rather than by you.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you already run Okta, Auth0, or Ping as your identity provider, Gateway can sit in front of them for inbound auth rather than you standing up a parallel Cognito user pool. Reusing the IdP you already audit is worth more here than saving a config file.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Failure family two: the shape of the tool surface<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The second mistake is giving the model a tool called <code>run_soql<\/code> that accepts an arbitrary query string. It demos beautifully. Ask anything, get anything.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You&#8217;ve now made the model your query planner and your authorisation layer at once. Every safety property of the system depends on prompt text. Guardrails help with content, not with whether a generated <code>WHERE<\/code> clause happened to be too generous.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Narrow tools are the fix, and they&#8217;re boring on purpose. <code>get_open_cases_for_account(account_id)<\/code> beats <code>run_soql(query)<\/code> because the query shape is fixed in your code, the model only supplies an identifier, and you can log and rate-limit each tool independently. When something goes wrong you&#8217;re reading a parameter, not parsing SOQL to work out what the model asked for.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">AgentCore Gateway takes OpenAPI specs, Smithy models, and Lambda functions as tool input types, and exposes them to the agent as MCP tools. It also has one-click integrations for several SaaS products including Salesforce. Practically that gives you three routes:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>The built-in Salesforce integration<\/strong> for the fastest path to something working. Good for a proof of concept. Look closely at which operations it exposes before you take it further, because breadth is exactly what you don&#8217;t want.<\/li>\n\n<li><strong>An OpenAPI target<\/strong> describing a small purpose-built API in front of Salesforce. This is the one I reach for first. You control the surface completely, and the spec doubles as the tool description the model reads.<\/li>\n\n<li><strong>Lambda targets<\/strong> when a tool needs to combine several Salesforce calls, or talk to something else at the same time.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">MCP server targets are also supported, with the gateway aggregating tools from multiple targets into a single <code>tools\/list<\/code> response. If you&#8217;re running your own MCP server as the Salesforce facade, it needs a stable HTTPS endpoint the gateway can reach; a small VPS from a provider like InterServer or Hetzner is plenty for the development and staging copies, with the production one behind whatever your standard ingress is.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Failure family three: the write path<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Reads leak. Writes destroy. They deserve different treatment and usually get the same treatment, which is how you end up with three hundred opportunities silently restaged.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Start read-only and stay there longer than feels necessary. When you do add writes, the constraints that actually help:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>One record per call.<\/strong> No bulk update tool. If the model wants to change twenty records it makes twenty calls, each individually logged and individually rate-limited. Slow is a feature here.<\/li>\n\n<li><strong>Separate the write identity.<\/strong> A distinct External Client App and permission set for writes means you can revoke the write path at 2am without taking the assistant offline.<\/li>\n\n<li><strong>Human confirmation on anything irreversible.<\/strong> The AgentCore harness supports inline function tools that pause the agent and hand control back to your client code, equivalent to return-of-control. That pause is where a person says yes.<\/li>\n\n<li><strong>Stamp the source.<\/strong> A custom field recording that a change came from the assistant, with the session and actor ID, is the difference between a ten-minute rollback and an afternoon of forensics.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Salesforce field history tracking is on a limited number of fields per object. If the field your assistant writes to isn&#8217;t tracked, you have no before-value. Check that before go-live, not after.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Failure family four: what happens under load<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Salesforce meters API requests against a daily org-wide allocation that depends on your edition and licence count. Your assistant does not consume that budget the way a nightly sync does.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A single conversational turn can be several tool calls. A model that doesn&#8217;t get a clean answer will often retry with a different filter. Ten support agents having ten conversations each becomes a request pattern nobody modelled, and the org-wide limit means the integration that breaks first might be your ETL job, your marketing platform, or your billing sync. Not the assistant. That&#8217;s what makes it hard to diagnose.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Three things that help, in order of how much they buy you:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Cap tool calls per session in your agent config, and return a clear message when the cap is hit rather than letting the loop run.<\/li>\n\n<li>Cache anything org-wide and slow-moving. Picklist values, record type metadata, product catalogue. These are a surprising share of calls and almost never change mid-day.<\/li>\n\n<li>Watch API usage as a leading indicator, not a post-mortem. Salesforce exposes remaining daily requests via the REST limits resource. Scrape it on a schedule into CloudWatch, Grafana Cloud, or whatever you already page from, and alert on the trend rather than the wall.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Wiring it up<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">With the design decisions settled, the build is short. The AgentCore CLI scaffolds and deploys the harness.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create an External Client App in Salesforce with the narrowest OAuth scopes the tools need. Assign it via permission set, not profile.<\/li>\n\n<li>Build the tool layer: an OpenAPI spec for your purpose-built endpoints, or Lambda functions for composite operations.<\/li>\n\n<li>Create an AgentCore Gateway, attach the target, and configure the credential provider in AgentCore Identity for the egress side.<\/li>\n\n<li>Create the harness, point it at the gateway, and deploy.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Scaffolding a harness and attaching a gateway looks like this. The <code>--gateway-arn<\/code> value is the gateway you created in step three, and the <code>--type<\/code> tells the CLI this tool is a gateway rather than one of the built-in tools such as the browser or code interpreter:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>agentcore create --name sf-assistant\n\nagentcore add tool --harness sf-assistant \n  --type agentcore_gateway --name sf-gateway \n  --gateway-arn arn:aws:bedrock-agentcore:REGION:ACCOUNT_ID:gateway\/GATEWAY_ID<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then declare the model and the system prompt, and deploy. Check the current model identifiers in the Bedrock console rather than copying one from any blog post, this one included, because they change:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>agentcore add harness \n  --name sf-assistant \n  --model-id YOUR_MODEL_ID \n  --system-prompt \"You answer questions about CRM records using only the provided tools. Never guess a record value.\" \n  --tools sf-gateway\n\nagentcore deploy<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Invoking it takes a session ID, and an actor ID if you want memory scoped per user. Skipping <code>--actor-id<\/code> is the memory equivalent of the integration user problem: one shared memory store, everyone&#8217;s context in it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>agentcore invoke --harness sf-assistant \n  --session-id \"$(uuidgen)\" --actor-id alice \n  \"Which of my open opportunities have had no activity in three weeks?\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Note &#8220;my&#8221; in that question. With three-legged auth it means something. With an integration user it means nothing, and the model will cheerfully invent a definition.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Troubleshooting<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><code>AccessDeniedException<\/code> when creating an agent.<\/strong> If you&#8217;re calling <code>CreateAgent<\/code> or <code>InvokeInlineAgent<\/code> on the Classic API, check whether the account has prior Bedrock Agents usage before you touch IAM. No policy fixes this one. Build on AgentCore instead.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The New Connected App button is greyed out.<\/strong> Expected. Create an External Client App. If you have a documented technical requirement that only a Connected App satisfies, that&#8217;s a Salesforce Support request, not a setting you can toggle.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Tools appear in <code>tools\/list<\/code> but calls fail.<\/strong> Gateway can cache tool schemas from a target without holding a working credential for calls. Discovery succeeding proves the schema synced, not that egress auth works. Look at the credential provider.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The assistant returns fewer records than the user expects.<\/strong> Under three-legged auth this is often correct behaviour that looks like a bug. Have the user run the same query in the Salesforce UI. If the UI shows more, check FLS on the specific fields; if it shows the same, the assistant is right and the user&#8217;s mental model is wrong.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Answers are confidently wrong about record values.<\/strong> Usually the model answering from training data instead of calling a tool. Tighten the system prompt to forbid answering record questions without a tool result, and check the traces to confirm which turns actually called out.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Everything works, then breaks at the same time daily.<\/strong> Look at org-wide API consumption before you look at your own code. Something else in the org may be eating the allocation on a schedule.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common mistakes<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Using a System Administrator profile for the integration identity because it makes the errors go away.<\/li>\n\n<li>Exposing a generic SOQL tool and calling the prompt a security control.<\/li>\n\n<li>Testing only in a sandbox with one user, where the sharing model is invisible because there&#8217;s nothing to share.<\/li>\n\n<li>Treating Guardrails as an access control layer. They filter content; they don&#8217;t know who&#8217;s asking.<\/li>\n\n<li>Shipping writes in the first release because the read-only version felt underwhelming in the demo.<\/li>\n\n<li>Building on Bedrock Agents Classic in a new account without checking allowlist status first.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Best practices<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use three-legged OAuth for anything governed by sharing rules. Reserve client credentials for genuinely org-wide data.<\/li>\n\n<li>Give each tool one job and a fixed query shape. The model supplies parameters, never structure.<\/li>\n\n<li>Separate read and write identities so you can kill one without the other.<\/li>\n\n<li>Scope memory per actor, not per deployment.<\/li>\n\n<li>Test with at least three users at different levels of the role hierarchy, and diff the answers. Identical answers to &#8220;show me my pipeline&#8221; is the signal that identity isn&#8217;t propagating.<\/li>\n\n<li>Keep end-to-end traces and ship them somewhere you already look, so agent behaviour lands in the same place as the rest of your telemetry.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Frequently asked questions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Can I still use Bedrock Agents for a new Salesforce project?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Only if the AWS account already used Bedrock Agents in the previous twelve months. Accounts without that history get an <code>AccessDeniedException<\/code> on <code>CreateAgent<\/code>, and AWS has said there&#8217;s no exception process. For anything new, build on AgentCore.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Do I need Agentforce to build a Salesforce AI assistant with Amazon Bedrock?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. Agentforce is Salesforce&#8217;s own agent layer, and it can call out to AWS, which is useful when you want the assistant to live inside the Salesforce UI and inherit the Einstein Trust Layer. But an AgentCore agent calling Salesforce APIs through a gateway is a complete architecture on its own, and it&#8217;s the better fit when the assistant also needs to reach systems outside Salesforce.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Does the model see all my CRM data?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">It sees whatever your tools return. That&#8217;s determined by the OAuth identity and the tool definitions, not by the model. This is why the identity decision matters more than the model choice.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Will Bedrock Guardrails stop the assistant leaking records?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No, and this is worth being blunt about. Guardrails filter content categories and can mask sensitive data patterns. They have no concept of which user is asking or which records that user is entitled to. A record correctly retrieved under an over-permissioned token passes every guardrail check, because there&#8217;s nothing objectionable about it. Guardrails are a good layer. They are not this layer.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How do I keep the assistant from burning the org&#8217;s API limit?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Cap tool calls per session, cache org-wide reference data, and monitor remaining daily requests via the REST limits resource on a schedule. The allocation is org-wide, so the symptom of overuse usually shows up in some other integration first.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Should I migrate an existing Bedrock Agents Classic setup?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">There&#8217;s no deadline and no announced end-of-life, so there&#8217;s no emergency. The reasons to move are the frozen model catalogue and the fact that new accounts can&#8217;t recreate your agent. If you&#8217;re still actively developing the thing, move. If it&#8217;s stable and finished, it can wait. AWS publishes a capability mapping and a CLI import path for existing configurations.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Is an External Client App harder to set up than a Connected App?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Slightly different rather than harder. The main structural change is that developer settings and admin policies are separated, so the app definition and the per-org access controls are configured independently. The one real removal is the username-password flow, which no longer exists in the ECA model.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The one thing worth remembering<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A Salesforce AI assistant with Amazon Bedrock is not really a model problem. Model quality is the part that will take care of itself, and the part you can change with one config line later.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The part that won&#8217;t take care of itself is identity. If the assistant answers as one integration user, you have quietly moved your entire access control model from a platform that enforces it well into a prompt that enforces it not at all, and nothing in your monitoring will tell you. Get that right first. Then argue about models.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Need help building or auditing this?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Most of the work on these projects is unglamorous: deciding what the agent is allowed to touch, and proving it can&#8217;t touch anything else. That&#8217;s the part I&#8217;m useful for.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Auditing an existing Salesforce AI integration for identity propagation gaps, including testing across role hierarchy levels to find silent over-permissioning.<\/li>\n\n<li>Migrating a Bedrock Agents Classic deployment to AgentCore, including mapping action groups to gateway targets.<\/li>\n\n<li>Designing the tool surface: replacing generic query tools with narrow, logged, individually rate-limited operations.<\/li>\n\n<li>Setting up three-legged OAuth between AgentCore Gateway and Salesforce, including token vault and consent handling.<\/li>\n\n<li>Moving integrations from Connected Apps to External Client Apps without breaking existing OAuth flows.<\/li>\n\n<li>Instrumenting API consumption and agent traces so you see problems before an unrelated integration starts failing.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;ve got something concrete, send it over: a gateway target config, an agent trace, a tool schema, or just the error you&#8217;re stuck on. Easier to say something useful about a real setup than a hypothetical one.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.upwork.com\/freelancers\/~01f15a912ad84a6620\" target=\"_blank\" rel=\"noreferrer noopener\">Work with me on Upwork<\/a><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>The demo works, then someone sees records they shouldn&#8217;t. Nothing errors. Here&#8217;s how to build a Salesforce AI assistant with Amazon Bedrock that respects your sharing model: which doors AWS and Salesforce just closed, why identity propagation is the failure mode that bites, and how to shape the tool surface so the agent can&#8217;t wander.<\/p>\n","protected":false},"author":1,"featured_media":212,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[25,24,52],"tags":[325,239,240,194,243,192,93,211,103,273,155,326,196,327,242,170,230,171,159],"class_list":["post-211","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cloud-computing","category-devops","category-technical-guides","tag-agentcore","tag-agentforce","tag-ai-agents","tag-amazon-bedrock","tag-api-gateway","tag-architecture","tag-aws","tag-bedrock-guardrails","tag-cloud-security","tag-cognito","tag-data-integration","tag-external-client-apps","tag-generative-ai","tag-identity-propagation","tag-mcp","tag-oauth","tag-rate-limiting","tag-rest-api","tag-salesforce","entry","has-media"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Salesforce AI Assistant With Amazon Bedrock: Build It Right<\/title>\n<meta name=\"description\" content=\"Build a Salesforce AI assistant with Amazon Bedrock without leaking records. Covers AgentCore, identity propagation, tool scoping and API limits.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Salesforce AI Assistant With Amazon Bedrock: Build It Right\" \/>\n<meta property=\"og:description\" content=\"Build a Salesforce AI assistant with Amazon Bedrock without leaking records. Covers AgentCore, identity propagation, tool scoping and API limits.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/\" \/>\n<meta property=\"og:site_name\" content=\"John Nessime\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-18T18:00:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/salesforce-ai-assistant-amazon-bedrock.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"627\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"John Nessime\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"John Nessime\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"15 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/salesforce-ai-assistant-amazon-bedrock\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/salesforce-ai-assistant-amazon-bedrock\\\/\"},\"author\":{\"name\":\"John Nessime\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\"},\"headline\":\"Building a Salesforce AI Assistant on Amazon Bedrock Without Leaking Your CRM\",\"datePublished\":\"2026-08-18T18:00:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/salesforce-ai-assistant-amazon-bedrock\\\/\"},\"wordCount\":3311,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\"},\"image\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/salesforce-ai-assistant-amazon-bedrock\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/salesforce-ai-assistant-amazon-bedrock.png\",\"keywords\":[\"AgentCore\",\"Agentforce\",\"AI Agents\",\"Amazon Bedrock\",\"API Gateway\",\"Architecture\",\"AWS\",\"Bedrock Guardrails\",\"Cloud Security\",\"Cognito\",\"Data Integration\",\"External Client Apps\",\"Generative AI\",\"Identity Propagation\",\"MCP\",\"OAuth\",\"Rate Limiting\",\"REST API\",\"Salesforce\"],\"articleSection\":[\"Cloud Computing\",\"DevOps\",\"Technical Guides\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/salesforce-ai-assistant-amazon-bedrock\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/salesforce-ai-assistant-amazon-bedrock\\\/\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/salesforce-ai-assistant-amazon-bedrock\\\/\",\"name\":\"Salesforce AI Assistant With Amazon Bedrock: Build It Right\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/salesforce-ai-assistant-amazon-bedrock\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/salesforce-ai-assistant-amazon-bedrock\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/salesforce-ai-assistant-amazon-bedrock.png\",\"datePublished\":\"2026-08-18T18:00:00+00:00\",\"description\":\"Build a Salesforce AI assistant with Amazon Bedrock without leaking records. Covers AgentCore, identity propagation, tool scoping and API limits.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/salesforce-ai-assistant-amazon-bedrock\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/salesforce-ai-assistant-amazon-bedrock\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/salesforce-ai-assistant-amazon-bedrock\\\/#primaryimage\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/salesforce-ai-assistant-amazon-bedrock.png\",\"contentUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/salesforce-ai-assistant-amazon-bedrock.png\",\"width\":1200,\"height\":627,\"caption\":\"Diagram comparing two identity models for a Salesforce AI assistant on Amazon Bedrock: client credentials returning twelve records with seven not permitted, versus per-user three-legged OAuth returning only the five the user is entitled to see.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/devops\\\/salesforce-ai-assistant-amazon-bedrock\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Building a Salesforce AI Assistant on Amazon Bedrock Without Leaking Your CRM\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/\",\"name\":\"John Nessime\",\"description\":\"Cloud, DevOps, Data &amp; AI \u2014 Built, Tested, Explained\",\"publisher\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\",\"name\":\"John Nessime\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\",\"contentUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\",\"width\":512,\"height\":512,\"caption\":\"John Nessime\"},\"logo\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\"},\"description\":\"AWS Certified Solutions Architect helping businesses build reliable cloud, data, reporting, and automation solutions. I help startups, agencies, and growing businesses replace manual processes and disconnected data with practical AWS architectures, clean data pipelines, useful dashboards, and maintainable automation.\",\"sameAs\":[\"https:\\\/\\\/john-nessime.com\\\/blog\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/john-m-nessime\"],\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/author\\\/johnnessime\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Salesforce AI Assistant With Amazon Bedrock: Build It Right","description":"Build a Salesforce AI assistant with Amazon Bedrock without leaking records. Covers AgentCore, identity propagation, tool scoping and API limits.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/","og_locale":"en_US","og_type":"article","og_title":"Salesforce AI Assistant With Amazon Bedrock: Build It Right","og_description":"Build a Salesforce AI assistant with Amazon Bedrock without leaking records. Covers AgentCore, identity propagation, tool scoping and API limits.","og_url":"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/","og_site_name":"John Nessime","article_published_time":"2026-08-18T18:00:00+00:00","og_image":[{"width":1200,"height":627,"url":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/salesforce-ai-assistant-amazon-bedrock.png","type":"image\/png"}],"author":"John Nessime","twitter_card":"summary_large_image","twitter_misc":{"Written by":"John Nessime","Est. reading time":"15 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/#article","isPartOf":{"@id":"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/"},"author":{"name":"John Nessime","@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105"},"headline":"Building a Salesforce AI Assistant on Amazon Bedrock Without Leaking Your CRM","datePublished":"2026-08-18T18:00:00+00:00","mainEntityOfPage":{"@id":"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/"},"wordCount":3311,"commentCount":0,"publisher":{"@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105"},"image":{"@id":"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/#primaryimage"},"thumbnailUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/salesforce-ai-assistant-amazon-bedrock.png","keywords":["AgentCore","Agentforce","AI Agents","Amazon Bedrock","API Gateway","Architecture","AWS","Bedrock Guardrails","Cloud Security","Cognito","Data Integration","External Client Apps","Generative AI","Identity Propagation","MCP","OAuth","Rate Limiting","REST API","Salesforce"],"articleSection":["Cloud Computing","DevOps","Technical Guides"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/","url":"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/","name":"Salesforce AI Assistant With Amazon Bedrock: Build It Right","isPartOf":{"@id":"https:\/\/john-nessime.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/#primaryimage"},"image":{"@id":"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/#primaryimage"},"thumbnailUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/salesforce-ai-assistant-amazon-bedrock.png","datePublished":"2026-08-18T18:00:00+00:00","description":"Build a Salesforce AI assistant with Amazon Bedrock without leaking records. Covers AgentCore, identity propagation, tool scoping and API limits.","breadcrumb":{"@id":"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/#primaryimage","url":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/salesforce-ai-assistant-amazon-bedrock.png","contentUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/salesforce-ai-assistant-amazon-bedrock.png","width":1200,"height":627,"caption":"Diagram comparing two identity models for a Salesforce AI assistant on Amazon Bedrock: client credentials returning twelve records with seven not permitted, versus per-user three-legged OAuth returning only the five the user is entitled to see."},{"@type":"BreadcrumbList","@id":"https:\/\/john-nessime.com\/blog\/devops\/salesforce-ai-assistant-amazon-bedrock\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/john-nessime.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Building a Salesforce AI Assistant on Amazon Bedrock Without Leaking Your CRM"}]},{"@type":"WebSite","@id":"https:\/\/john-nessime.com\/blog\/#website","url":"https:\/\/john-nessime.com\/blog\/","name":"John Nessime","description":"Cloud, DevOps, Data &amp; AI \u2014 Built, Tested, Explained","publisher":{"@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/john-nessime.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105","name":"John Nessime","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png","url":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png","contentUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png","width":512,"height":512,"caption":"John Nessime"},"logo":{"@id":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png"},"description":"AWS Certified Solutions Architect helping businesses build reliable cloud, data, reporting, and automation solutions. I help startups, agencies, and growing businesses replace manual processes and disconnected data with practical AWS architectures, clean data pipelines, useful dashboards, and maintainable automation.","sameAs":["https:\/\/john-nessime.com\/blog","https:\/\/www.linkedin.com\/in\/john-m-nessime"],"url":"https:\/\/john-nessime.com\/blog\/author\/johnnessime\/"}]}},"_links":{"self":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts\/211","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/comments?post=211"}],"version-history":[{"count":1,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts\/211\/revisions"}],"predecessor-version":[{"id":240,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts\/211\/revisions\/240"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/media\/212"}],"wp:attachment":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/media?parent=211"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/categories?post=211"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/tags?post=211"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}