Integration
One Claude model, four on-ramps. The same doctor sees you at four hospitals — direct API, AWS Bedrock, Google Vertex AI, and Microsoft Azure. Six concepts on what actually changes: the gateway, the auth, and the bill — not the model.
The 6 concepts of provider integration
Tap any concept to jump to its 5-card cluster.
- 1The Four Ways to Call ClaudeSame model, different plumbing
- 2AWS Bedrock Deep DiveIAM auth, model-ID format, Guardrails
- 3Google Vertex AI Deep DiveADC auth, date-pinned model IDs
- 4Microsoft Azure AI Deep DiveDifferent SDK, chat-completions shape
- 5Provider-Agnostic WrapperOne env var swaps the whole cloud
- 6Decision FrameworkWhich provider fits your team
Same model, four different front doors
Claude always runs on Anthropic's own infrastructure. Whether you call it directly or through AWS Bedrock, Google Vertex AI, or Microsoft Azure AI, the same model does the work. What changes is only the gateway, authentication, and billing your request passes through on the way in.
Your request hits the cloud provider's endpoint first, its identity system checks your credentials, and it forwards the payload to Anthropic's fleet. In code, three of the four paths are a one-line constructor swap; the fourth (Azure) uses a different SDK and response shape. Picking a provider is picking who handles the paperwork — not which model you get.
One specialist, four hospital networks
BEFORE: Dr. Claude is a specialist you can see at four networks: her own private clinic (direct API), General Hospital (AWS), City Medical (GCP), and Metro Health (Azure). Same doctor, same expertise, same consultation quality at every one.
PAIN: Your company's insurance only covered one network. Seeing Dr. Claude anywhere else meant out-of-network paperwork, a special exception, and legal reviewing every single appointment — a months-long tax before you could even book.
MAPPING: Now she operates in all four. If your existing cloud contract already covers a network, you book through it — no new vendor, no new legal review, the bill goes to the account you already have. Choosing your provider is choosing which network handles billing and paperwork, not which doctor you see.
What actually differs between the paths
- The SDK classDirect uses
Anthropic(); BedrockAnthropicBedrock(); VertexAnthropicVertex(); Azure a separate chat-completions client. - The authDirect uses an API key; Bedrock uses AWS IAM; Vertex uses Google ADC; Azure uses a key or Entra ID.
- The model ID + billEach path names the model differently and routes the charge to that cloud's invoice — per-token pricing stays the same.
- New-model lagDirect gets new models day 0; the clouds follow days to weeks later. Data residency shifts to your own cloud account.
Which door do I walk through?
# Question: how should I reach Claude? IF you need the newest model on day 0 OR you're just prototyping: USE the direct API # one key, no cloud setup ELIF procurement already signed a cloud contract (AWS/GCP/Azure): USE that cloud's gateway # no new vendor, bill on existing account ELSE: START direct, wrap it (Concept 5) # migrate later with one env var
The token price is identical everywhere. The real difference is organizational velocity, not cost per token.
Misconceptions
Picking a provider is picking the plumbing, not the model. Gateway, auth, and bill differ; Claude is the same everywhere.
Choose the path that matches contracts you already have — or start direct and stay portable.
A managed inference gateway inside AWS
AWS Bedrock is not a place to run your agent code — it's a managed gateway that sits in front of Claude. Your app runs wherever you put it and sends calls to Bedrock's regional endpoint. Bedrock validates your IAM credentials, logs to CloudTrail, applies any Guardrails, forwards to Claude, and streams the answer back.
Think of it as "API Gateway for AI." You inherit AWS's regions, compliance certifications (SOC 2, HIPAA BAA, FedRAMP), and the ability to keep data in one region — without running any new servers. Two things change vs. direct: auth is AWS IAM, and the model ID has a special format.
The corporate mailroom that stamps every package
BEFORE: In a big company, you don't hand parcels straight to the courier. You drop them at the mailroom, which weighs, logs, and stamps each one before it leaves the building.
PAIN: Skip the mailroom and nobody can prove what left, who sent it, or whether it broke policy. For a regulated company, an unlogged package leaving the building is an audit failure waiting to happen.
MAPPING: Bedrock is that mailroom for Claude calls. Every request is checked against your IAM badge, stamped into CloudTrail, and optionally screened by Guardrails before it reaches the model — all under the AWS contract you already signed. You get the courier (Claude) plus a full paper trail.
From setup to first call
- Request model accessIn the Bedrock console, enable Claude under Anthropic. Usually instant for Haiku and Sonnet.
- Grant IAM permissionYour role needs
bedrock:InvokeModel. No key in code — the instance role provides credentials in production. - Use the Bedrock model IDNot
claude-sonnet-4-6but a prefixed ID likeus.anthropic.claude-...-v1:0for cross-region routing. - Call and signThe client signs the request with SigV4 automatically and returns the same response shape as the direct API.
Pseudocode
# AUTH: AWS IAM via the standard credential chain # env vars -> ~/.aws -> instance role client = BEDROCK_CLIENT(region = "us-west-2") # MODEL ID: cross-region profile, NOT direct ID model = "us.anthropic.claude-sonnet-4-6-...-v1:0" TRY: reply = client.CREATE MESSAGE( model = model, max_tokens = 1024, # REQUIRED on Bedrock messages = [user("Explain Bedrock.")]) PRINT reply.text CATCH api_error AS e: # 403 = IAM missing InvokeModel # 404 = wrong model-ID format PRINT "Bedrock error", e.status
Misconceptions
claude-sonnet-4-6 becomes anthropic.claude-...-v1:0 (or a us.-prefixed cross-region profile). The direct ID returns a ValidationException.Bedrock = AWS IAM auth + a prefixed model ID + a mandatory max_tokens. Everything else matches the direct API.
The payoff: Claude under your existing AWS contract, with CloudTrail logging and optional Guardrails outside your code.
Claude from the GCP Model Garden
On Google Cloud, Claude lives in the Vertex AI Model Garden — GCP's catalog of foundation models. Enable Claude once per project, and Google exposes it as a serverless endpoint that accepts Anthropic-format calls. Your code uses the AnthropicVertex client.
The signature move is auth: Vertex uses Application Default Credentials (ADC). The SDK discovers a short-lived token from your gcloud login locally, or from the metadata server on Cloud Run / GKE — the same code, no key file, in every environment. Model IDs are date-pinned, like claude-sonnet-4-6@20250514.
The building's keycard vs. carrying a key
BEFORE: To enter a locked office you could carry a physical key — copy it, hand it out, hope nobody loses it. That's an API key: a secret you have to manage, store, and rotate yourself.
PAIN: Physical keys get copied, dropped, and forgotten in code. A key committed to a repo is a breach; a key that expired at 2am is an outage. Someone has to babysit every copy of it.
MAPPING: ADC is the building's keycard system. Your identity (your gcloud login, or the machine's attached service account) is recognized automatically, and a short-lived badge is issued on the spot. No key travels with your code — the environment vouches for you, and the badge expires on its own.
Enable, authenticate, call
- Enable the API + accept termsTurn on the Vertex AI API and enable Claude in Model Garden — once per GCP project.
- Let ADC handle authLocally:
gcloud auth application-default login. In production: attach a service account withaiplatform.user— no key file. - Pin the model by dateUse a date-stamped ID like
claude-sonnet-4-6@20250514so behavior doesn't shift under you. - Call in a supported regionClaude is only in specific regions (e.g.
us-east5). System prompts pass as a separatesystemparam, same as direct.
Pseudocode
# AUTH: ADC resolves a short-lived token # gcloud login OR metadata server client = VERTEX_CLIENT( project = env("GCP_PROJECT"), region = "us-east5") # Claude-enabled region # MODEL ID: pin the date in production model = "claude-sonnet-4-6@20250514" TRY: reply = client.CREATE MESSAGE( model = model, max_tokens = 1024, system = "You are concise.", messages = [user("What is Vertex AI?")]) PRINT reply.text CATCH api_error AS e: # 403 = SA missing aiplatform.user # 404 = model not enabled in this region PRINT "Vertex error", e.status
Misconceptions
us-east5), and you should pin the date-stamped ID so a model update can't silently change behavior.Vertex = ADC auth + date-pinned model IDs + a Claude-enabled region. The call shape matches the direct API exactly.
ADC means the same code runs on your laptop and on Cloud Run with zero key files to manage.
The one that uses a different SDK
Azure AI Foundry is Microsoft's unified AI platform. You deploy Claude from its model catalog as a serverless deployment, get an endpoint URL and key, and call it. But unlike Bedrock and Vertex, Azure does not use the Anthropic SDK.
Instead you use Microsoft's azure-ai-inference package, which speaks the chat-completions format (the OpenAI-style shape). You call complete() instead of messages.create(), and the answer comes back at choices[0].message.content — not content[0].text. Same model, genuinely different client and response shape.
Same power, different wall socket
BEFORE: Your laptop charger delivers the same electricity in any country. Plug it into the wall and it just works — at home.
PAIN: Fly abroad and the wall socket has a different shape. The electricity behind it is identical, but your plug physically won't fit. You need an adapter, or nothing charges — and the adapter's prongs sit in a different arrangement than you're used to.
MAPPING: Azure is that foreign socket. The power (Claude) is the same, but the plug shape (the SDK and JSON format) differs. You reach for the azure-ai-inference adapter, call complete(), and read the answer from a differently-named field. Once the adapter's in, it charges exactly the same.
Deploy, then call the Azure way
- Create + deployMake an Azure AI Foundry resource, deploy Claude as a "Serverless API," and name the deployment.
- Grab endpoint + keyCopy the endpoint URL and key into env vars — or use Entra Managed Identity in production (no key at all).
- Use the Azure clientBuild a
ChatCompletionsClientand callcomplete()— the model name is your deployment name, not the Anthropic ID. - Read the OpenAI shapeThe answer is at
choices[0].message.content. Native Anthropic tool-use isn't available here.
Pseudocode
# AUTH: API key (dev) or Managed Identity (prod) client = CHAT_COMPLETIONS_CLIENT( endpoint = env("AZURE_AI_ENDPOINT"), credential = key(env("AZURE_AI_KEY"))) # NOTE: different method + message shape TRY: reply = client.COMPLETE( model = "my-claude-deployment", # deployment name messages = [ system("Be concise."), user("What is Azure AI?") ], max_tokens = 512) # response is OpenAI-shaped: PRINT reply.choices[0].message.content CATCH http_error AS e: # 401 = bad key 404 = wrong deployment name PRINT "Azure error", e.status
Misconceptions
azure-ai-inference), a different method (complete()), and returns the OpenAI shape (choices[0].message.content).claude-sonnet-4-6. A mismatch returns a 404.Azure is the odd one out: different SDK, complete() not messages.create(), and a chat-completions response shape.
In production, Entra Managed Identity removes API keys entirely — the runtime vouches for your app.
Hide all four behind one method
You've seen four clients with three auth systems and two response shapes. If your agent code calls those clients directly, it's welded to one cloud forever. The fix is the adapter pattern: one wrapper class whose chat() method always returns a plain string.
Internally the wrapper reads an environment variable (CLAUDE_PROVIDER), builds the right backend client, and normalizes the response — including Azure's different field path. Every other file in your codebase just calls client.chat(message). Switching clouds becomes a one-line config change, not a rewrite.
The universal travel adapter
BEFORE: Every country has its own socket shape. Without help you'd pack four different plugs and swap them at each border, hoping you grabbed the right one.
PAIN: Wiring four plug types into every device is brittle. Change destination and you're rewiring gear — and if you hardcode one country's plug, that device simply won't work anywhere else.
MAPPING: The wrapper is a universal travel adapter with one face your device plugs into. Flip a switch on the back (the env var) and it reshapes to whatever the wall needs. Your device (agent code) only ever sees the one familiar socket; the adapter absorbs every country's quirks.
One class, four backends
- Read the providerThe constructor reads
CLAUDE_PROVIDER(direct / bedrock / vertex / azure), defaulting to direct. - Build the right clientA switch picks the matching backend and its auth — the only place in the codebase that knows the clouds exist.
- Resolve the model IDEach provider maps to its own model-ID format via env vars, so the caller never sees the difference.
- Normalize the response
chat()returns a plain string — converting Azure'schoices[0]...path so every path looks identical.
Pseudocode
CLASS ClaudeClient: INIT(provider = env("CLAUDE_PROVIDER", "direct")): self.backend = BUILD_BACKEND(provider) FUNCTION chat(message, system?): model = MODEL_ID_FOR(self.provider) IF provider == "azure": r = backend.complete(model, msgs) RETURN r.choices[0].message.content ELSE: # direct / bedrock / vertex r = backend.create_message(model, msgs) RETURN r.content[0].text # every other file just does: answer = ClaudeClient().chat("Hello!")
Misconceptions
choices[0].message.content while the others use content[0].text. The adapter must normalize both to a string.One env var, CLAUDE_PROVIDER, swaps the entire cloud with zero code changes. The wrapper is the only file that knows which cloud you're on.
Start direct for speed, wrap from day one, and migrate to a cloud later as a config change — not a rewrite.
Pick the cloud you're already standing in
Because Claude is identical everywhere, the choice isn't about the model — it's about your existing contracts, identity system, and data-residency rules. The right provider is almost always the one your organization is already committed to.
Need the newest model on day 0, or just prototyping? Direct API. Already deep in AWS, GCP, or Azure? Use that cloud so there's no new vendor review and the bill lands on an account you already have. Not sure yet? Start direct behind the wrapper and let the decision come later — painlessly.
Which bank branch to open an account at
BEFORE: The money (Claude) is the same currency at every branch. What differs is which bank you already have a relationship with, where your paychecks land, and which ATMs are free for you.
PAIN: Open an account at a brand-new bank and you re-do every check: identity verification, new paperwork, new logins, reconnecting your direct deposits. All that friction for the exact same dollars.
MAPPING: If your salary already deposits at one bank, you bank there — the accounts, the trust, and the compliance are already in place. Choosing a cloud provider works the same way: go where your contracts, identity, and data already live, and the same Claude costs you far less friction.
The migration path most teams take
- Stage 1 — prototypeDirect API. One key, zero cloud setup, fastest to ship.
- Stage 2 — scalingDirect API behind the provider-agnostic wrapper. Same speed, but portable.
- Stage 3 — enterpriseMove to Bedrock / Vertex / Azure when procurement or data residency requires it — a one-line switch.
- Match your stackAWS-native → Bedrock; GCP → Vertex; Microsoft-heavy → Azure; none → direct.
Which provider, in one glance
# Which cloud should host my Claude calls? IF need newest model day-0 OR prototyping: → Direct API # fastest, day-0 models IF AWS-native (EA/BAA, S3, EC2): → AWS Bedrock # + Guardrails, Knowledge Bases IF on GCP (BigQuery, Cloud Run, GKE): → Vertex AI # ADC auth, CUD discounts IF Microsoft-heavy (Entra, M365, Azure): → Azure AI # Managed Identity, no keys ELSE: → Direct + wrapper # migrate later, 1 env var
Misconceptions
Choose the cloud you already live in. Existing contracts, identity, and data residency decide it — not model quality, which is the same everywhere.
Start direct behind the wrapper; graduate to Bedrock, Vertex, or Azure as a config change when procurement unlocks.
Test the core insight
One question per concept. Tap to reveal the answer.
claude-sonnet-4-6 fail on AWS Bedrock?anthropic.claude-...-v1:0, or a us.-prefixed cross-region profile. Passing the direct ID returns a ValidationException — one of the most common beginner errors. max_tokens is also required.gcloud login locally or the metadata server in production — the same code, no key file, in every environment. Model IDs should be date-pinned (e.g. @20250514).azure-ai-inference, calling complete() instead of messages.create(), with the answer at choices[0].message.content (OpenAI shape). The model field is your deployment name.ClaudeClient reads CLAUDE_PROVIDER, builds the right backend, and normalizes the response (including Azure's different field) so every other file just calls chat(). It's the only file that knows which cloud you're on.Open the full module on desktop
The desktop version is the full build — complete Python and Node clients for Bedrock, Vertex, and Azure with real auth and error handling, Bedrock Guardrails and Knowledge Bases, Entra Managed Identity, the full provider-agnostic wrapper class, and a hands-on lab that calls Claude across all four providers.
Open M21B on Desktop → Full cloud build · Bedrock + Vertex + Azure + wrapper · ~75 minModule 21B of 30+ · Track 7: Production Deployment