Skip to main content

Bedrock reference

Depth behind the Bedrock guide.

Which Bedrock API a request uses

Every request goes over the Converse API by default: the OpenAI-shaped /v1/chat/completions lane, every non-Claude model, and Claude on /v1/messages. Converse has no shape for a mid-conversation system turn and folds it into the neighbouring user turn instead.

Claude on the native Anthropic lane (/v1/messages, what Claude Code and the Anthropic SDKs send) can go over InvokeModel with the client's Messages body forwarded as it arrived: mid-conversation system turns, cache_control markers, thinking, tools and metadata all reach the model untouched, and the stream comes back as the same Anthropic events the client would get from Anthropic. The gateway drops model and stream (both are in the URL), sets anthropic_version to bedrock-2023-05-31, and carries the client's anthropic-beta header as the anthropic_beta field.

The InvokeModel lane is off by default in this release. Enable it per deployment by setting the Bedrock provider's Claude over InvokeModel field to true (Console → Providers → bedrock, or anthropicInvokeNative: "true" in the credential bag through PUT /admin/provider-keys). It becomes the default once the paired benchmark (gateway pass-through over InvokeModel against direct Bedrock) passes; the Converse translation measured 22% more on identical bytes in the same benchmark. count_tokens uses Bedrock's own endpoint on either setting.

Model-id resolution

The gateway resolves a Claude id in this order:

  1. A built-in table of released Claude models, keyed by their dated ids. No extra permission, no network call.
  2. Bedrock itself, via ListInferenceProfiles then ListFoundationModels, for anything the table does not hold. This is the path that needs the two List actions in the guide's policy; the answer is cached per region, so it costs one lookup rather than one per request, and it can only ever resolve to a model your account can reach.
  3. Unchanged, when neither resolves it. A Bedrock id sent directly is always forwarded exactly as written.

Bedrock names its newer models without a date, so claude-opus-5 matches anthropic.claude-opus-5 directly. For an older family that Bedrock publishes only as dated snapshots, an undated id names the model rather than a snapshot, and from v1.10.269 it resolves to the newest published snapshot: claude-sonnet-4-5 reaches claude-sonnet-4-5-20250929-v1:0. Newest wins rather than first because AWS returns these listings in no guaranteed order, and first-match would leave the same request served by a different snapshot from one region or account to the next. On earlier releases that form does not resolve, so send the dated id or set a model alias.

The cross-region inference-profile prefix comes from the provider's configured region (Claude 4 and newer are profile-only in most regions, which is what makes the prefix necessary):

Configured regionPrefix
us-*us.
eu-*eu.
ap-*apac.
ca-*ca.

Discovery is not limited to that prefix: where a model is published only under the cross-region global. profile, it returns that instead. Models from other families keep their Bedrock ids, since only Claude has a canonical id to convert from.

Claude 5 availability by region

No undated id is in the built-in table, so claude-opus-5, claude-sonnet-5, and claude-fable-5 resolve only through discovery, and availability differs by region in a way a fixed mapping could not handle:

Region familyWhat discovery finds
us-*All three under the us. prefix
eu-*Opus and sonnet under eu.; fable only as global.anthropic.claude-fable-5
ap-*None carry apac.; all three come from global.

To pin an exact id instead, map the short name with a model alias, which is applied before the request reaches the provider and so takes precedence over all resolution above. To list what your account can use as an alias target:

aws bedrock list-inference-profiles --region <region> \
--query 'inferenceProfileSummaries[].inferenceProfileId' --output text

Static keys and cross-account access

Use a static access-key pair when the gateway has no AWS workload identity (a Cloudflare Worker has no access to AWS workload metadata), or when Bedrock lives in a different AWS account than the gateway, which a workload role cannot reach. Create an IAM user in the account that holds the model access, attach the guide's policy, and mint a key:

aws iam create-user --user-name anyray-bedrock
aws iam attach-user-policy --user-name anyray-bedrock --policy-arn "$POLICY_ARN"
aws iam create-access-key --user-name anyray-bedrock

Paste the pair into the provider config (guide, step 2). The key is long-lived, so it goes on your normal rotation schedule.

A new IAM user needs its own use-case approval

Bedrock evaluates the Anthropic use-case attestation per principal, not per account: a freshly created IAM user gets ResourceNotFoundException asking for the use-case form, even in an account that already invokes Claude successfully. Read the account's form as an admin with bedrock get-use-case-for-model-access, then submit it as the new user with bedrock put-use-case-for-model-access. The BedrockUseCaseAttestation statement in the guide's policy is what permits that; without it the failure arrives later and elsewhere, as AccessDenied on Converse reading "not authorized to perform the required AWS Marketplace actions".

The CloudFormation role

The stack creates the role with the logical id TaskRole. Find its generated name with aws cloudformation describe-stack-resources --stack-name <stack> --logical-resource-id TaskRole, then attach the policy with aws iam attach-role-policy.

Attach a managed policy rather than adding an inline one. The template declares the role's inline policies, so CloudFormation reconciles that list on every stack update and can remove an inline policy you added by hand. A managed-policy attachment is not declared in the template, so it survives; it appears in drift detection, which is expected. One TaskRole backs all three services in the stack, and only the gateway calls Bedrock; scope the policy's Resource if you want to narrow that.

VPC and private subnets

The CloudFormation stack needs no networking change: its tasks run with their own outbound access to AWS, and that holds when the load balancer is internal as well, because the tasks reach Bedrock directly rather than through the load balancer. A gateway in private subnets needs either a NAT gateway or VPC interface endpoints for com.amazonaws.<region>.bedrock-runtime and com.amazonaws.<region>.bedrock. VPC endpoints are regional, so a Bedrock region different from the gateway's own region cannot use one and needs ordinary internet egress.