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:
- A built-in table of released Claude models, keyed by their dated ids. No extra permission, no network call.
- Bedrock itself, via
ListInferenceProfilesthenListFoundationModels, for anything the table does not hold. This is the path that needs the twoListactions 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. - 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 region | Prefix |
|---|---|
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 family | What 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.
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.