Skip to content

IAM Permissions

Every AWS call stdapi.ai makes is signed with the credentials of the role it runs under. This page lists, feature by feature, the exact IAM statements that role needs, and ends with complete policy examples.

Who this page is for. If you deploy with the official Terraform module, it already provisions the ECS task role with every statement below — you need this page only to audit what that role can do, or to restrict it further. Read it in full when you build the role yourself: a manual ECS or EC2 deployment, a container running outside AWS, or an IAM user for local development.

Building your policy

Combine the statements below based on the features you enable. At minimum, you need the Bedrock permissions; add S3, TTS, STT and the others only as your deployment requires them (least privilege).

A customer managed policy is capped at 6,144 characters, a limit AWS does not raise through Service Quotas, and whitespace does not count toward it. Enabling most of the features on this page exceeds that in a single document — CreatePolicy then fails with LimitExceeded: Cannot exceed quota for PolicySize: 6144, naming no statement. Attach several policies to the role instead of widening actions to save characters: splitting by service keeps each one readable and reviewable, the Terraform module ships two for exactly this reason (one for Amazon Bedrock, one for the supporting services), and a role takes up to 10 managed policies by default.


Which Sections Do You Need?

Each row below is one section of this page. Find the features your deployment enables, then read the sections they point to.

Feature Required Permissions Configuration
Bedrock bedrock:CountTokens
bedrock:InvokeGuardrailChecks
bedrock:InvokeModel
bedrock:InvokeModelWithBidirectionalStream
bedrock:InvokeModelWithResponseStream
bedrock:InvokeTool (the Amazon Nova grounding server tool)
bedrock:Rerank
bedrock:GetAsyncInvoke and bedrock:TagResource (on arn:aws:bedrock:*:*:async-invoke/*) for async-invoke models (video, TwelveLabs Marengo embeddings)
For model discovery: bedrock:ListFoundationModels
bedrock:GetFoundationModelAvailability
bedrock:ListProvisionedModelThroughputs
bedrock:ListInferenceProfiles
The Realtime API (POST /v1/realtime/client_secrets, WS /v1/realtime) needs no additional action: bedrock:InvokeModelWithBidirectionalStream covers it
Always required
Bedrock Marketplace Auto-Subscribe aws-marketplace:Subscribe
aws-marketplace:ViewSubscriptions
AWS_BEDROCK_MARKETPLACE_AUTO_SUBSCRIBE=true (default)
Bedrock Marketplace Model Endpoints bedrock:ListMarketplaceModelEndpoints
bedrock:GetMarketplaceModelEndpoint
sagemaker:InvokeEndpoint
sagemaker:InvokeEndpointWithResponseStream (on arn:aws:sagemaker:*:*:endpoint/*, conditioned on aws:CalledViaLast: bedrock.amazonaws.com)
AWS_BEDROCK_MARKETPLACE_ENDPOINTS_ENABLED=true
SageMaker AI Endpoints sagemaker:CallWithBearerToken (on *, no resource-level support)
sagemaker:InvokeEndpoint (on the endpoint ARNs you serve)
AWS_SAGEMAKER_ENDPOINTS configured
AWS Marketplace Metering aws-marketplace:RegisterUsage AWS Marketplace image only (always active); not required for the community image
Bedrock Inference Profiles, Prompt Routers and Prompt Management bedrock:GetInferenceProfile
bedrock:GetPromptRouter
bedrock:GetPrompt and bedrock:RenderPrompt (on arn:aws:bedrock:*:*:prompt/*) for Prompt Management prompts
AWS_BEDROCK_ALLOW_*_ARN=true or AWS_BEDROCK_MODEL_ARN_MAPPING configured
Bedrock Guardrails bedrock:ApplyGuardrail AWS_BEDROCK_GUARDRAIL_IDENTIFIER
Bedrock Session Storage Bedrock session permissions (bedrock:CreateSession, bedrock:GetSession, bedrock:*Invocation*, bedrock:ListSessions, bedrock:EndSession, bedrock:DeleteSession, bedrock:TagResource, bedrock:ListTagsForResource on sessions) store=true requests and stored-completion listings
Bedrock Mantle bedrock-mantle:CreateInference
bedrock-mantle:GetInference
bedrock-mantle:DeleteInference
bedrock-mantle:ListModels
bedrock-mantle:GetModel
bedrock-mantle:CancelInference
bedrock-mantle:CountTokens (on arn:aws:bedrock-mantle:*:*:project/*)
bedrock-mantle:CallWithBearerToken
AWS_BEDROCK_MANTLE_ENABLED=true
Web Search bedrock-websearch:InvokeSearch
bedrock-websearch:InvokeFetch
bedrock-websearch:ExternalWebAccess only when external web access is enabled
web_search requests on the OpenAI GPT-5.x family
S3 File Storage s3:PutObject
s3:PutObjectTagging
s3:GetObject
s3:DeleteObject
s3:AbortMultipartUpload
s3:ListMultipartUploadParts
s3:ListBucket
s3:ListBucketMultipartUploads
on every bucket, including each AWS_S3_REGIONAL_BUCKETS entry
kms:Decrypt and kms:GenerateDataKey, with a kms:ViaService condition, when the buckets use KMS encryption
AWS_S3_BUCKET
AWS_S3_REGIONAL_BUCKETS
If S3 buckets use KMS encryption
S3 Accepted Input Buckets s3:GetObject on the objects of each declared bucket (no s3:ListBucket)
kms:Decrypt, with a kms:ViaService condition, when those buckets use KMS encryption
AWS_S3_ACCEPTED_BUCKETS
If those buckets use KMS encryption
Vector Stores s3vectors:CreateIndex
s3vectors:DeleteIndex
s3vectors:PutVectors
s3vectors:GetVectors
s3vectors:QueryVectors
s3vectors:DeleteVectors (on the vector bucket and its indexes)
File Storage S3 permissions on AWS_S3_BUCKET for the stores' records
kms:Decrypt and kms:GenerateDataKey, with a kms:ViaService condition, when the vector bucket uses KMS encryption
AWS_S3_VECTORS_BUCKET
AWS_S3_VECTORS_REGION
Durable Vector Store Indexing sqs:SendMessage
sqs:ReceiveMessage
sqs:DeleteMessage
sqs:ChangeMessageVisibility
sqs:GetQueueAttributes (on the queue ARN only)
kms:Decrypt and kms:GenerateDataKey, with a kms:ViaService condition, when the queue uses SSE-KMS with your own key
AWS_SQS_VECTOR_STORE_QUEUE_URL
Shared Table dynamodb:GetItem
dynamodb:PutItem
dynamodb:DeleteItem
dynamodb:Query
dynamodb:DescribeTable
dynamodb:DescribeTimeToLive (on the table ARN; no dynamodb:Scan, no index ARN)
AWS_DYNAMODB_TABLE
AWS_DYNAMODB_REGION
MODEL_CACHE_SHARED
Tenant API Key Delivery ssm:PutParameter
ssm:GetParameter (on the delivery prefix)
kms:Encrypt, kms:Decrypt, kms:GenerateDataKey on the key, with a kms:ViaService condition, when TENANT_KEY_SSM_KMS_KEY_ID names a key of your own
plus the Shared Table permissions above
TENANT_API_KEYS
TENANT_KEY_SSM_PARAMETER_PREFIX
TENANT_KEY_SSM_KMS_KEY_ID
Tenant AWS Credentials sts:AssumeRole on the tenant role ARNs, conditioned on sts:ExternalId TENANT_AWS_CREDENTIALS
Knowledge Base Vector Stores bedrock:GetKnowledgeBase
bedrock:Retrieve
bedrock:ListDataSources
bedrock:IngestKnowledgeBaseDocuments
bedrock:ListKnowledgeBaseDocuments
bedrock:GetKnowledgeBaseDocuments
bedrock:DeleteKnowledgeBaseDocuments (on each allowlisted knowledge base ARN; no bedrock:ListKnowledgeBases)
AWS_BEDROCK_KNOWLEDGE_BASE_IDS
Video Generation Core Bedrock invoke permissions (incl. bedrock:GetAsyncInvoke, bedrock:TagResource)
bedrock:ListAsyncInvokes and bedrock:ListTagsForResource (on arn:aws:bedrock:*:*:async-invoke/*) for job listing
File Storage S3 permissions on each regional bucket
AWS_S3_REGIONAL_BUCKETS
Batch Inference bedrock:CreateModelInvocationJob
bedrock:GetModelInvocationJob
bedrock:StopModelInvocationJob (on arn:aws:bedrock:*:*:model-invocation-job/*)
iam:PassRole on the batch service role, scoped with iam:PassedToService: bedrock.amazonaws.com
File Storage S3 permissions on each bucket a batch uses, plus the service role's own policy
AWS_BEDROCK_BATCH_ROLE_ARN
Text-to-Speech polly:SynthesizeSpeech
polly:DescribeVoices
polly:StartSpeechSynthesisStream for generative voices above 3,000 characters
polly:StartSpeechSynthesisTask, polly:GetSpeechSynthesisTask and S3 PutObject/GetObject/DeleteObject on each bucket serving a Polly region, for the other voices above 3,000 characters
AWS_POLLY_REGION
AWS_S3_BUCKET
AWS_S3_REGIONAL_BUCKETS
Speech-to-Text transcribe:StartTranscriptionJob
transcribe:GetTranscriptionJob
transcribe:DeleteTranscriptionJob
transcribe:StartStreamTranscription
transcribe:TagResource (on arn:aws:transcribe:*:*:transcription-job/*)
File Storage S3 permissions on every bucket serving a candidate region
kms:GenerateDataKey, kms:Decrypt on the output encryption key, when one is configured
AWS_TRANSCRIBE_REGION
AWS_TRANSCRIBE_S3_BUCKET
AWS_S3_REGIONAL_BUCKETS
AWS_TRANSCRIBE_STREAM_LANGUAGES
AWS_TRANSCRIBE_OUTPUT_ENCRYPTION_KEY_ARN
Language Detection comprehend:DetectDominantLanguage AWS_COMPREHEND_REGION
Comprehend Moderation comprehend:DetectToxicContent Moderations API without a configured guardrail
Text Translation translate:TranslateText
translate:ListLanguages (optional; validates the language pair before transcribing)
AWS_TRANSLATE_REGION
Cost Tracking pricing:GetProducts COST_TRACKING=true (opt-in; false by default)
Usage API cloudwatch:GetMetricData
cloudwatch:ListMetrics (on *CloudWatch metric actions take no resource ARN); GetMetricData is billed per metric read and is outside the free tier
USAGE_API=true (opt-in; false by default), with CLOUDWATCH_METRICS=true
Per-User Cost Attribution sts:AssumeRole and sts:TagSession on the end user role, matched by that role's trust policy; on the end user role itself, bedrock:InvokeModel, bedrock:InvokeModelWithResponseStream on every model ARN form the deployment allows, plus bedrock:ApplyGuardrail when a guardrail is configured and s3:GetObject (with kms:Decrypt via S3) on every bucket an invocation can reference by URI AWS_BEDROCK_USER_ROLE_ARN
API Key Authentication Parameter Store: ssm:GetParameter
kms:Decrypt (if encrypted)
Secrets Manager: secretsmanager:GetSecretValue
API_KEY_SSM_PARAMETER
API_KEY_SECRETSMANAGER_SECRET

Multi-Region Failover and Region-Scoped Policies

By default, Amazon Bedrock and the other AWS AI services (Polly, Transcribe, Comprehend, Translate) are called in every region listed in AWS_BEDROCK_REGIONS, failing over from one to the next on throttling, quota, or availability errors. See Other AWS Services Failover.

The statements on this page use region-agnostic resources, so they work as-is. However, any aws:RequestedRegion condition — in the policy itself, a permissions boundary, or a service control policy — must allow all configured regions, otherwise failover silently fails and requests error out once the first region is unavailable.


Bedrock (Required)

Environment Variables: Always required

These permissions are mandatory for stdapi.ai to discover and invoke Amazon Bedrock models:

Bedrock IAM Policy Statements
{
  "Sid": "BedrockModelInvoke",
  "Effect": "Allow",
  "Action": [
    "bedrock:CountTokens",
    "bedrock:GetAsyncInvoke",
    "bedrock:InvokeGuardrailChecks",
    "bedrock:InvokeModel",
    "bedrock:InvokeModelWithBidirectionalStream",
    "bedrock:InvokeModelWithResponseStream",
    "bedrock:InvokeTool",
    "bedrock:Rerank"
  ],
  "Resource": "*"
},
{
  "Sid": "BedrockAsyncInvokeTagging",
  "Effect": "Allow",
  "Action": [
    "bedrock:TagResource"
  ],
  "Resource": "arn:aws:bedrock:*:*:async-invoke/*"
},
{
  "Sid": "BedrockModelDiscovery",
  "Effect": "Allow",
  "Action": [
    "bedrock:ListFoundationModels",
    "bedrock:GetFoundationModelAvailability",
    "bedrock:ListProvisionedModelThroughputs",
    "bedrock:ListInferenceProfiles"
  ],
  "Resource": "*"
}

Asynchronous Invocations

bedrock:GetAsyncInvoke and bedrock:TagResource (on arn:aws:bedrock:*:*:async-invoke/*) serve Bedrock asynchronous invocations, used by video generation models and asynchronous embedding models such as TwelveLabs Marengo (twelvelabs.marengo-embed-*). They can be dropped if your deployment uses none of these models. bedrock:ListAsyncInvokes and bedrock:ListTagsForResource are not part of this core set — they are only needed for video job listing (see Video Generation).

Bidirectional Streaming

bedrock:InvokeModelWithBidirectionalStream serves any model invoked over a persistent, two-way connection: live audio transcription with Amazon Nova Sonic (see Speech-to-Text) and the Realtime API (POST /v1/realtime/client_secrets, WS /v1/realtime) alike. No route-specific action exists for either — it is already part of the core Bedrock policy above.


Bedrock Marketplace Auto-Subscribe (Optional)

Environment Variables: AWS_BEDROCK_MARKETPLACE_AUTO_SUBSCRIBE

Required only if you want models sold as third-party AWS Marketplace listings to be usable without subscribing to each one by hand (AWS_BEDROCK_MARKETPLACE_AUTO_SUBSCRIBE=true, which is the default). The server never calls Subscribe itself: it keeps a listing with no agreement in the catalogue, and AWS creates the subscription under this role on the first invocation. It applies to whichever models AWS sells that way — see Which Models Are Which; models billed as ordinary Amazon Bedrock usage need none of these permissions.

Bedrock Marketplace Auto-Subscribe IAM Policy Statement
{
  "Sid": "BedrockMarketplaceAutoSubscribe",
  "Effect": "Allow",
  "Action": [
    "aws-marketplace:Subscribe",
    "aws-marketplace:ViewSubscriptions"
  ],
  "Resource": "*"
}

Cost Consideration

Automatic marketplace subscriptions may incur costs. Review AWS Marketplace pricing for individual models before enabling this feature, or set AWS_BEDROCK_MARKETPLACE_AUTO_SUBSCRIBE=false to require manual marketplace subscription.


Bedrock Marketplace Model Endpoints (Optional)

Environment Variables: AWS_BEDROCK_MARKETPLACE_ENDPOINTS_ENABLED

Required only if you want stdapi.ai to publish and serve the Amazon Bedrock Marketplace model endpoints already deployed in this account. The server only discovers and invokes them — it never creates, updates or deletes an endpoint, so it is granted no action that does.

Bedrock Marketplace Model Endpoints IAM Policy Statements
{
  "Sid": "BedrockMarketplaceEndpointDiscovery",
  "Effect": "Allow",
  "Action": [
    "bedrock:ListMarketplaceModelEndpoints",
    "bedrock:GetMarketplaceModelEndpoint"
  ],
  "Resource": "*"
},
{
  "Sid": "BedrockMarketplaceEndpointInvoke",
  "Effect": "Allow",
  "Action": [
    "sagemaker:InvokeEndpoint",
    "sagemaker:InvokeEndpointWithResponseStream"
  ],
  "Resource": "arn:aws:sagemaker:*:<account-id>:endpoint/*",
  "Condition": {
    "StringEquals": {
      "aws:CalledViaLast": "bedrock.amazonaws.com"
    }
  }
}

Neither ListMarketplaceModelEndpoints nor GetMarketplaceModelEndpoint takes a resource-level scope, hence Resource: "*" on discovery. sagemaker:InvokeEndpoint is what Amazon Bedrock calls on the server's behalf when it forwards an invocation to the endpoint, which is why it is conditioned on aws:CalledViaLast: bedrock.amazonaws.com — under this policy, the role can never call a SageMaker endpoint directly, only through Bedrock.

See Set up model access for Amazon Bedrock Marketplace.


SageMaker AI Endpoints (Optional)

Environment Variables: AWS_SAGEMAKER_ENDPOINTS

Required only if you name Amazon SageMaker AI endpoints for stdapi.ai to serve. The server only invokes the endpoints you named — it never creates, updates, scales or deletes one, so it is granted no action that does.

SageMaker AI Endpoints IAM Policy Statements
{
  "Sid": "SageMakerBearerToken",
  "Effect": "Allow",
  "Action": "sagemaker:CallWithBearerToken",
  "Resource": "*"
},
{
  "Sid": "SageMakerEndpointInvoke",
  "Effect": "Allow",
  "Action": "sagemaker:InvokeEndpoint",
  "Resource": [
    "arn:aws:sagemaker:<region>:<account-id>:endpoint/<endpoint-name>"
  ]
}

The OpenAI-compatible route authenticates with a short-term API key the server derives locally from its own credentials, which is what sagemaker:CallWithBearerToken authorizes; the action supports no resource-level scope, hence Resource: "*". That key carries the same authority as the role, so sagemaker:InvokeEndpoint is what bounds it — list the endpoint ARNs you actually serve rather than *, and a leaked key can still reach nothing else.

See OpenAI-compatible endpoints.


AWS Marketplace Metering (AWS Marketplace Image Only)

Environment Variables: none (always active on the AWS Marketplace image)

Required only for the AWS Marketplace image — not the community image. At startup it registers hourly usage with AWS Marketplace Metering on ECS, EKS, and Fargate; an AccessDenied error aborts startup.

AWS Marketplace Metering IAM Policy Statement
{
  "Sid": "MarketplaceRegisterUsage",
  "Effect": "Allow",
  "Action": [
    "aws-marketplace:RegisterUsage"
  ],
  "Resource": "*"
}

Bedrock Inference Profiles, Prompt Routers and Prompt Management (Optional)

Environment Variables: AWS_BEDROCK_ALLOW_CROSS_REGION_INFERENCE_PROFILE_ARN, AWS_BEDROCK_ALLOW_APPLICATION_INFERENCE_PROFILE_ARN, AWS_BEDROCK_ALLOW_PROMPT_ROUTER_ARN, AWS_BEDROCK_ALLOW_PROMPT_ARN, AWS_BEDROCK_MODEL_ARN_MAPPING

Required only if you enable ARN-based routing features that allow users to pass inference profile, prompt router or Prompt Management prompt ARNs directly, or if you configure server-side ARN mappings.

Bedrock Inference Profiles, Prompt Routers and Prompt Management IAM Policy Statements
{
  "Sid": "BedrockInferenceProfilesAndPromptRouters",
  "Effect": "Allow",
  "Action": [
    "bedrock:GetInferenceProfile",
    "bedrock:GetPromptRouter"
  ],
  "Resource": "*"
},
{
  "Sid": "BedrockPromptManagement",
  "Effect": "Allow",
  "Action": [
    "bedrock:GetPrompt",
    "bedrock:RenderPrompt"
  ],
  "Resource": "arn:aws:bedrock:*:*:prompt/*"
}

bedrock:GetPrompt resolves the model bound to the prompt variant; bedrock:RenderPrompt is required because the prompt ARN is then sent to Bedrock as the invocation modelId.

When to Include

Add these permissions when:

  • AWS_BEDROCK_ALLOW_CROSS_REGION_INFERENCE_PROFILE_ARN=true
  • AWS_BEDROCK_ALLOW_APPLICATION_INFERENCE_PROFILE_ARN=true
  • AWS_BEDROCK_ALLOW_PROMPT_ROUTER_ARN=true
  • AWS_BEDROCK_ALLOW_PROMPT_ARN=true (bedrock:GetPrompt and bedrock:RenderPrompt only)
  • AWS_BEDROCK_MODEL_ARN_MAPPING is configured with any mappings

Bedrock Guardrails (Optional)

Environment Variables: AWS_BEDROCK_GUARDRAIL_IDENTIFIER, AWS_BEDROCK_GUARDRAIL_VERSION

Required if you configure Bedrock Guardrails for content filtering, use the moderation request parameter, or select a guardrail on the Moderations API (without a guardrail, that API falls back to Comprehend toxicity moderation). See the Bedrock Guardrails configuration section.

Bedrock Guardrails IAM Policy Statement
{
  "Sid": "BedrockGuardrails",
  "Effect": "Allow",
  "Action": [
    "bedrock:ApplyGuardrail"
  ],
  "Resource": "arn:aws:bedrock:*:*:guardrail/*"
}

Bedrock Session Storage (Optional)

Environment Variables: none (enabled by the store=true request parameter; see Bedrock Session Storage configuration)

Required if clients use store=true on the Responses or Chat Completions APIs, or the Conversations API, all of which persist state in Amazon Bedrock sessions.

Bedrock Session Storage IAM Policy Statement
{
  "Sid": "BedrockSessionStorage",
  "Effect": "Allow",
  "Action": [
    "bedrock:CreateSession",
    "bedrock:GetSession",
    "bedrock:UpdateSession",
    "bedrock:CreateInvocation",
    "bedrock:PutInvocationStep",
    "bedrock:ListInvocations",
    "bedrock:ListInvocationSteps",
    "bedrock:GetInvocationStep",
    "bedrock:EndSession",
    "bedrock:DeleteSession",
    "bedrock:TagResource",
    "bedrock:ListTagsForResource"
  ],
  "Resource": "arn:aws:bedrock:*:*:session/*"
},
{
  "Sid": "BedrockSessionListing",
  "Effect": "Allow",
  "Action": "bedrock:ListSessions",
  "Resource": "*"
}

bedrock:ListSessions serves the stored chat completions listing endpoint (GET /v1/chat/completions); the account-level ListSessions action does not support resource scoping. bedrock:GetSession is used on deletion and bedrock:ListTagsForResource on both deletion and listing, to check that a stored object belongs to the API it is requested from. bedrock:UpdateSession serves the conversation metadata update (POST /v1/conversations/{conversation_id}) only.

Add kms:Encrypt, kms:Decrypt, kms:GenerateDataKey and kms:DescribeKey on the key when AWS_BEDROCK_SESSION_ENCRYPTION_KEY_ARN is configured — the set AWS documents for session encryption. The Terraform module grants a slightly different set, adding kms:CreateGrant and leaving out kms:Encrypt.


Bedrock Mantle (Optional)

Environment Variables: AWS_BEDROCK_MANTLE_ENABLED

Required for AWS_BEDROCK_MANTLE_ENABLED (enabled by default), which exposes models served by the Amazon Bedrock Mantle endpoint (OpenAI GPT, xAI Grok, Google Gemma, and more). Without these permissions the server still starts normally: Mantle models are not listed and a warning is logged.

Bedrock Mantle IAM Policy Statements
{
  "Sid": "BedrockMantleInference",
  "Effect": "Allow",
  "Action": [
    "bedrock-mantle:CreateInference",
    "bedrock-mantle:GetInference",
    "bedrock-mantle:DeleteInference",
    "bedrock-mantle:ListModels",
    "bedrock-mantle:GetModel",
    "bedrock-mantle:CancelInference",
    "bedrock-mantle:CountTokens"
  ],
  "Resource": "arn:aws:bedrock-mantle:*:*:project/*"
},
{
  "Sid": "BedrockMantleBearerToken",
  "Effect": "Allow",
  "Action": "bedrock-mantle:CallWithBearerToken",
  "Resource": "*"
}

bedrock-mantle:CallWithBearerToken authorizes the short-term bearer tokens the server derives from its AWS credential chain; it does not support resource scoping.

bedrock-mantle:CountTokens serves /anthropic/v1/messages/count_tokens for a Mantle-served model: Amazon Bedrock's own CountTokens accepts Anthropic models only, so the count is proxied to the Mantle endpoint. Without it that route answers 500.


Web Search (Optional)

Environment Variables: none (enabled by a web_search tool in a request)

Required for the built-in web search tool on the OpenAI GPT-5.x family, whichever settings are in use. Each action is authorized only when the model actually attempts that call, and a denied call does not fail the request: AWS documents the model continuing with the information it already has and telling you it could not retrieve enough current information (Identity and access management for Web Search).

A missing web search permission produces no error and no server log entry

The denial is handled inside the model call, so the request succeeds with a normal answer: the server sees nothing to report, and the response is indistinguishable from the model deciding it did not need to search. When answers never cite a source, check these permissions (and the Region the call was served in) before suspecting the model. AWS CloudTrail records the denied bedrock-websearch calls.

Add bedrock-websearch:ExternalWebAccess on top when a request can reach external web access — that is, when AWS_BEDROCK_EXTERNAL_WEB_ACCESS is enabled, or when AWS_BEDROCK_ALLOW_EXTERNAL_WEB_ACCESS_OVERRIDE lets a client ask for it per request. Leaving it out is what keeps every search inside the AWS boundary.

Web Search IAM Policy Statement
{
  "Sid": "BedrockWebSearch",
  "Effect": "Allow",
  "Action": [
    "bedrock-websearch:InvokeSearch",
    "bedrock-websearch:InvokeFetch"
  ],
  "Resource": "*"
}

InvokeSearch and InvokeFetch are authorized on AWS-owned tool resources (arn:aws:bedrock-websearch:<region>:aws:tool/<name>), which "*" covers; the AWS managed policies instead scope them to arn:aws:bedrock-websearch:*:*:*, and either form grants the same searches. ExternalWebAccess is a permission-only action with no resource of its own, so grant it with "*". Web search runs in the Region that served the model call, in the Regions where the tool is offered; scope the statement with an aws:RequestedRegion condition to pin which Regions may run searches. See Identity and access management for Web Search and Actions, resources, and condition keys.


S3 File Storage (Optional)

Environment Variables: AWS_S3_BUCKET, AWS_S3_REGIONAL_BUCKETS

Required for storing generated images, audio files, documents, and videos. See Storage Configuration for bucket setup details.

S3 File Storage IAM Policy Statements
{
  "Sid": "S3FileStorage",
  "Effect": "Allow",
  "Action": [
    "s3:PutObject",
    "s3:PutObjectTagging",
    "s3:GetObject",
    "s3:DeleteObject",
    "s3:AbortMultipartUpload",
    "s3:ListMultipartUploadParts"
  ],
  "Resource": "arn:aws:s3:::AWS_S3_BUCKET_VALUE/*"
},
{
  "Sid": "S3FileStorageList",
  "Effect": "Allow",
  "Action": [
    "s3:ListBucket",
    "s3:ListBucketMultipartUploads"
  ],
  "Resource": "arn:aws:s3:::AWS_S3_BUCKET_VALUE"
}

Replace Bucket Name

Replace AWS_S3_BUCKET_VALUE with the value of your AWS_S3_BUCKET environment variable. Repeat both statements for each AWS_S3_REGIONAL_BUCKETS bucket — they serve video generation, asynchronous embeddings (TwelveLabs Marengo, Amazon Nova), large attachments on any multimodal route, and Speech-to-Text failover, and the Files API looks up objects across every configured bucket.

Multipart Uploads

Large files are uploaded and copied with the multipart API. Its CreateMultipartUpload, UploadPart, UploadPartCopy, and CompleteMultipartUpload operations have no dedicated IAM actions — they are authorized by s3:PutObject — which is why only the abort and listing actions appear above.

Cross-Region Access

Bucket ARNs are region-agnostic, so one statement per bucket covers every region. When a request fails over to another region, the server copies the object server-side through the destination region's S3 endpoint, reading the source bucket from there. Grant these actions on the source and destination buckets alike, and on the KMS key of each encrypted bucket.

If your S3 bucket uses KMS encryption, also add:

{
  "Sid": "KMSEncryptedBucket",
  "Effect": "Allow",
  "Action": [
    "kms:Decrypt",
    "kms:GenerateDataKey"
  ],
  "Resource": "arn:aws:kms:REGION:ACCOUNT_ID:key/YOUR_KMS_KEY_ID",
  "Condition": {
    "StringEquals": {
      "kms:ViaService": "s3.REGION.amazonaws.com"
    }
  }
}

KMS Security

The kms:ViaService condition restricts KMS key usage to S3 service calls only, following AWS security best practices. Because the condition pins a single region, add one statement per region — with that region's key ARN and s3.REGION.amazonaws.com — when you use regional buckets with per-region keys.


S3 Accepted Input Buckets (Optional)

Environment Variables: AWS_S3_ACCEPTED_BUCKETS

Required when clients send S3 URIs — or S3 HTTP URLs, including presigned ones — that point at buckets this deployment does not own. The server reads those objects with its own role: a bucket that is not declared is refused before any AWS call, and a declared bucket the role cannot read answers 400 naming that input.

S3 Accepted Input Buckets IAM Policy Statements
{
  "Sid": "S3AcceptedBuckets",
  "Effect": "Allow",
  "Action": "s3:GetObject",
  "Resource": [
    "arn:aws:s3:::ACCEPTED_BUCKET_NAME/*"
  ]
}

Replace the Bucket Names

Replace ACCEPTED_BUCKET_NAME with a bucket name declared in AWS_S3_ACCEPTED_BUCKETS, and list one arn:aws:s3:::BUCKET/* resource per entry of that map. Grant it on the objects only — the server reads the object a request names and never lists these buckets, so no s3:ListBucket is needed.

Reads, Heads, and Copies

s3:GetObject covers all three ways the server touches these objects: reading the bytes, a HeadObject for the size and content type, and the server-side copy that moves an input into the region a model runs in. The destination of that copy is one of the deployment's own buckets, already covered by S3 File Storage.

The bucket owner grants the other half

These buckets are not the deployment's, so their own bucket policy must also allow this role to s3:GetObject — cross-account access needs both sides. A denial that survives this statement is the caller's object to fix.

If an accepted bucket uses KMS encryption, also add:

{
  "Sid": "KMSAcceptedBuckets",
  "Effect": "Allow",
  "Action": "kms:Decrypt",
  "Resource": [
    "arn:aws:kms:REGION:ACCOUNT_ID:key/YOUR_KMS_KEY_ID"
  ],
  "Condition": {
    "StringLike": {
      "kms:ViaService": "s3.*.amazonaws.com"
    }
  }
}

KMS Security

Only kms:Decrypt is granted — the server never writes to these buckets, so it never needs kms:GenerateDataKey on their keys. The kms:ViaService condition restricts the key to S3 service calls; it is a StringLike because accepted buckets may span regions. When every key lives in one region, tighten it to StringEquals on s3.REGION.amazonaws.com. List one key ARN per encrypted bucket, and, for a key in another account, its key policy must grant this role as well.


Vector Stores (Optional)

Environment Variables: AWS_S3_VECTORS_BUCKET, AWS_S3_VECTORS_REGION, AWS_S3_BUCKET

Required by the Vector Stores API. The indexed content lives in an Amazon S3 vector bucket you create; the stores' own records live in the general purpose bucket under AWS_S3_VECTOR_STORES_PREFIX and are covered by the S3 File Storage statements above.

Vector Stores IAM Policy Statements
{
  "Sid": "VectorStoreIndexes",
  "Effect": "Allow",
  "Action": [
    "s3vectors:CreateIndex",
    "s3vectors:DeleteIndex",
    "s3vectors:PutVectors",
    "s3vectors:GetVectors",
    "s3vectors:QueryVectors",
    "s3vectors:DeleteVectors"
  ],
  "Resource": [
    "arn:aws:s3vectors:REGION:ACCOUNT_ID:bucket/AWS_S3_VECTORS_BUCKET_VALUE",
    "arn:aws:s3vectors:REGION:ACCOUNT_ID:bucket/AWS_S3_VECTORS_BUCKET_VALUE/index/*"
  ]
}

Replace the Placeholders

Replace AWS_S3_VECTORS_BUCKET_VALUE with your AWS_S3_VECTORS_BUCKET value, REGION with AWS_S3_VECTORS_REGION, and ACCOUNT_ID with your account. The bucket ARN itself is needed for the index actions; the index ARN pattern covers the per-store indexes the gateway creates and deletes.

The bucket is yours to create

The gateway never creates or deletes the vector bucket, only the indexes inside it, so no bucket-level create or delete action is granted.

Records live in the general purpose bucket

Grant the S3 File Storage statements on AWS_S3_BUCKET as well: the stores, their attached files and their batches are JSON objects there.

If your vector bucket uses KMS encryption, also add:

{
  "Sid": "KMSVectorBucket",
  "Effect": "Allow",
  "Action": [
    "kms:Decrypt",
    "kms:GenerateDataKey"
  ],
  "Resource": "arn:aws:kms:REGION:ACCOUNT_ID:key/YOUR_KMS_KEY_ID",
  "Condition": {
    "StringEquals": {
      "kms:ViaService": "s3vectors.REGION.amazonaws.com"
    }
  }
}

KMS Security

REGION is AWS_S3_VECTORS_REGION — the region the vector bucket lives in — in the key ARN and in the kms:ViaService value alike. That condition restricts the key to Amazon S3 Vectors calls, so the role cannot use it anywhere else. Add this statement for a bucket you encrypted with a customer managed key of your own.


Durable Vector Store Indexing (Optional)

Environment Variables: AWS_SQS_VECTOR_STORE_QUEUE_URL

Required to keep indexing a vector store file when the server that accepted it stops. The gateway both writes the work to the Amazon SQS queue you create and reads it back, so it needs the producer and the consumer actions on that one queue.

Durable Vector Store Indexing IAM Policy Statements
{
  "Sid": "VectorStoreIndexingQueue",
  "Effect": "Allow",
  "Action": [
    "sqs:SendMessage",
    "sqs:ReceiveMessage",
    "sqs:DeleteMessage",
    "sqs:ChangeMessageVisibility",
    "sqs:GetQueueAttributes"
  ],
  "Resource": "arn:aws:sqs:REGION:ACCOUNT_ID:QUEUE_NAME"
}

Replace the Placeholders

REGION, ACCOUNT_ID and QUEUE_NAME are the three parts of your AWS_SQS_VECTOR_STORE_QUEUE_URL. Grant this on the queue ARN itself — never on *.

The queues are yours to create

The gateway never creates, deletes or reconfigures a queue, so no sqs:CreateQueue, sqs:DeleteQueue or sqs:SetQueueAttributes is granted. sqs:GetQueueAttributes is read-only and is what lets the gateway honour your dead-letter queue's redrive policy.

The dead-letter queue needs nothing

Amazon SQS moves an exhausted message itself; the gateway never reads the dead-letter queue, so grant it nothing.

If your queue uses SSE-KMS with a key of your own, also add:

{
  "Sid": "KMSVectorStoreIndexingQueue",
  "Effect": "Allow",
  "Action": [
    "kms:Decrypt",
    "kms:GenerateDataKey"
  ],
  "Resource": "arn:aws:kms:REGION:ACCOUNT_ID:key/YOUR_KMS_KEY_ID",
  "Condition": {
    "StringEquals": {
      "kms:ViaService": "sqs.REGION.amazonaws.com"
    }
  }
}

KMS Security

Amazon SQS calls AWS KMS under the gateway's own identity, so the grant is conditioned on the call arriving through Amazon SQS in the region the queue lives in — the REGION of your AWS_SQS_VECTOR_STORE_QUEUE_URL, in the key ARN and in the kms:ViaService value alike. Both actions are needed: kms:GenerateDataKey to send a message, kms:Decrypt to receive one. A queue encrypted with the Amazon SQS managed key needs no statement.


Shared Table (Optional)

Environment Variables: AWS_DYNAMODB_TABLE, AWS_DYNAMODB_REGION, MODEL_CACHE_SHARED

Required by the features whose records every instance of a deployment reads and writes, on the one Amazon DynamoDB table you create. Grant it on that table's ARN. Model list sharing is the feature that uses it today; a server missing these permissions reports it at WARNING and keeps discovering the model list itself.

Shared Table IAM Policy Statements
{
  "Sid": "SharedTable",
  "Effect": "Allow",
  "Action": [
    "dynamodb:GetItem",
    "dynamodb:PutItem",
    "dynamodb:DeleteItem",
    "dynamodb:Query",
    "dynamodb:DescribeTable",
    "dynamodb:DescribeTimeToLive"
  ],
  "Resource": "arn:aws:dynamodb:REGION:ACCOUNT_ID:table/TABLE_NAME"
}

Replace the Placeholders

REGION is AWS_DYNAMODB_REGION, ACCOUNT_ID your AWS account ID, and TABLE_NAME your AWS_DYNAMODB_TABLE. Grant this on the table ARN itself — never on *.

The table is yours to create

The gateway never creates, deletes or reconfigures a table, so no dynamodb:CreateTable, dynamodb:DeleteTable or dynamodb:UpdateTable is granted. dynamodb:DescribeTable and dynamodb:DescribeTimeToLive are read-only and are what let the gateway report a table whose key schema or expiration is not what the features need.

No index and no scan

The table has no secondary index, so no index ARN is needed, and every read is addressed by key — dynamodb:Scan is deliberately not granted.

If the table uses a customer managed key

A table encrypted with a customer managed AWS KMS key needs no kms:* permission here: Amazon DynamoDB creates the grants it uses on your behalf when the table is created. Encryption at rest is always on, and the default AWS owned key is free.

Write access to the table is control of what the gateway serves

The published model list carries the routing state the gateway invokes with — inference profiles and Amazon Bedrock Marketplace and Amazon SageMaker AI endpoint ARNs — so anything that can write to this table can decide where the gateway sends inference traffic. Treat dynamodb:PutItem on it as equivalent to the gateway's own inference permissions: grant it to the gateway's task role only, keep the table dedicated to the gateway, and scope the inference permissions above to the endpoint ARNs you actually deploy.


Tenant API Key Delivery (Optional)

Environment Variables: TENANT_API_KEYS, TENANT_KEY_SSM_PARAMETER_PREFIX, TENANT_KEY_SSM_KMS_KEY_ID

Required, together with the shared table permissions, when tenant API keys are enabled. The gateway writes each minted key exactly once (PutParameter refuses to overwrite), and reads a parameter back only to recover a mint that crashed between delivery and recording. Grant it on the delivery prefix and nothing wider.

Tenant Key Delivery IAM Policy Statement
{
  "Sid": "TenantKeyDelivery",
  "Effect": "Allow",
  "Action": [
    "ssm:PutParameter",
    "ssm:GetParameter"
  ],
  "Resource": "arn:aws:ssm:REGION:ACCOUNT_ID:parameter/PREFIX/*"
}

Replace the Placeholders

REGION is the deployment's own Region, ACCOUNT_ID your AWS account ID, and /PREFIX your TENANT_KEY_SSM_PARAMETER_PREFIX — the parameter ARN concatenates parameter and the prefix's leading slash.

One prefix per deployment

Any principal allowed to read under the prefix can read every tenant's key, and the gateway role itself can read them back — unless TENANT_KEY_SSM_KMS_KEY_ID names a key of your own, which also takes kms:Decrypt on it. Keep the prefix private to one deployment, and delete each parameter once its key is delivered — the gateway never needs it again.

Customer Managed Key Statement (with TENANT_KEY_SSM_KMS_KEY_ID)

Add this statement when the delivery parameters are encrypted with a key of your own instead of the AWS-managed alias/aws/ssm key. kms:Encrypt covers writing a standard SecureString, kms:Decrypt covers reading one back, and kms:GenerateDataKey is only needed if the account's default parameter tier creates advanced parameters. The kms:ViaService condition keeps the grant usable through Parameter Store alone.

{
  "Sid": "TenantKeyDeliveryKms",
  "Effect": "Allow",
  "Action": [
    "kms:Encrypt",
    "kms:Decrypt",
    "kms:GenerateDataKey"
  ],
  "Resource": "arn:aws:kms:REGION:ACCOUNT_ID:key/KEY_ID",
  "Condition": {
    "StringEquals": {
      "kms:ViaService": "ssm.REGION.amazonaws.com"
    }
  }
}

Replace the Placeholders

REGION is the deployment's own Region, ACCOUNT_ID your AWS account ID, and KEY_ID the key's own identifier. Resource is always the key ARN, even where TENANT_KEY_SSM_KMS_KEY_ID names the key by alias: KMS does not accept an alias ARN as the resource of a cryptographic action.


Tenant AWS Credentials (Optional)

Environment Variables: TENANT_AWS_CREDENTIALS

Required when tenant AWS credentials are enabled: the gateway assumes each tenant's registered cross-account role to run that tenant's model invocations under the tenant's own account. Grant sts:AssumeRole on the tenant roles alone — list them, or constrain a pattern — never on *, and require an ExternalId to be presented so no code path can ever assume a role without the confused-deputy check.

Tenant Role Assumption IAM Policy Statement
{
  "Sid": "TenantRoleAssumption",
  "Effect": "Allow",
  "Action": "sts:AssumeRole",
  "Resource": [
    "arn:aws:iam::TENANT_ACCOUNT_ID:role/TENANT_ROLE_NAME"
  ],
  "Condition": {
    "StringLike": { "sts:ExternalId": "?*" }
  }
}

The other half lives in the tenant's account

Each tenant role's trust policy must allow this deployment's account (or role) to call sts:AssumeRole on it, conditioned on the ExternalId the gateway minted for that tenant — the shape is shown in Tenant AWS credentials. The role's permission policy is the tenant's to scope: the Bedrock invocation actions on the models it wants to serve.


Knowledge Base Vector Stores (Optional)

Environment Variables: AWS_BEDROCK_KNOWLEDGE_BASE_IDS

Required to serve an Amazon Bedrock knowledge base you created as a vector store. Grant one statement per allowlisted knowledge base, on its own ARN.

Knowledge Base Vector Stores IAM Policy Statements
{
  "Sid": "BedrockKnowledgeBaseVectorStores",
  "Effect": "Allow",
  "Action": [
    "bedrock:GetKnowledgeBase",
    "bedrock:Retrieve",
    "bedrock:ListDataSources",
    "bedrock:IngestKnowledgeBaseDocuments",
    "bedrock:ListKnowledgeBaseDocuments",
    "bedrock:GetKnowledgeBaseDocuments",
    "bedrock:DeleteKnowledgeBaseDocuments"
  ],
  "Resource": "arn:aws:bedrock:REGION:ACCOUNT_ID:knowledge-base/KNOWLEDGE_BASE_ID"
}

Replace the Placeholders

Replace REGION with the first AWS_BEDROCK_REGIONS entry, ACCOUNT_ID with your account, and KNOWLEDGE_BASE_ID with the knowledge base identifier — one ARN per entry of AWS_BEDROCK_KNOWLEDGE_BASE_IDS.

No discovery action, deliberately

bedrock:ListKnowledgeBases is not granted, and is not needed: the server never discovers knowledge bases it was not given. Only the identifiers in the allowlist are ever addressed, and any other one is answered as an unknown vector store.

The knowledge base is yours to create

The gateway never creates or deletes a knowledge base, so no create, update or delete action on the knowledge base itself is granted — only the documents of its data source.

Checked once at startup

bedrock:GetKnowledgeBase — already needed to serve a store — is also called on every allowlisted entry at startup, to confirm each one is a kind the server can serve. No extra action is required for that check, and a role missing the action never stops the server: it starts with one warning per entry instead.


Video Generation (Optional)

Environment Variables: AWS_S3_REGIONAL_BUCKETS

Video generation itself runs on the core Bedrock asynchronous invocation permissions (bedrock:InvokeModel, bedrock:GetAsyncInvoke, bedrock:TagResource) plus S3 File Storage permissions on each regional bucket. The video job listing endpoint (GET /v1/videos) additionally requires:

Video Job Listing IAM Policy Statements
{
  "Sid": "BedrockVideoJobListing",
  "Effect": "Allow",
  "Action": [
    "bedrock:ListAsyncInvokes"
  ],
  "Resource": "*"
},
{
  "Sid": "BedrockVideoJobTags",
  "Effect": "Allow",
  "Action": [
    "bedrock:ListTagsForResource"
  ],
  "Resource": "arn:aws:bedrock:*:*:async-invoke/*"
}

The account-level ListAsyncInvokes action does not support resource scoping; ListTagsForResource reads the job metadata tags used to attribute listed jobs.


Batch Inference (Optional)

Environment Variables: AWS_BEDROCK_BATCH_ROLE_ARN, AWS_S3_BATCHES_PREFIX, AWS_S3_BUCKET, AWS_S3_REGIONAL_BUCKETS

The Batch API and the Message Batches API run on Amazon Bedrock batch inference, which needs two policies: the server's own, and a service role Amazon Bedrock assumes to read the requests and write the results.

The Server's Policy

Batch Inference IAM Policy Statements
{
  "Sid": "BedrockBatchJobs",
  "Effect": "Allow",
  "Action": [
    "bedrock:CreateModelInvocationJob",
    "bedrock:GetModelInvocationJob",
    "bedrock:StopModelInvocationJob"
  ],
  "Resource": "arn:aws:bedrock:*:<account-id>:model-invocation-job/*"
},
{
  "Sid": "BedrockBatchPassRole",
  "Effect": "Allow",
  "Action": "iam:PassRole",
  "Resource": "arn:aws:iam::<account-id>:role/stdapi-ai-batch",
  "Condition": {
    "StringEquals": {"iam:PassedToService": "bedrock.amazonaws.com"}
  }
}

Substitute <account-id> with your AWS account ID, and the role ARN with the value of AWS_BEDROCK_BATCH_ROLE_ARN. The server also needs the S3 File Storage permissions on every bucket a batch may use.

The Service Role

Create a role named by AWS_BEDROCK_BATCH_ROLE_ARN whose trust policy lets Amazon Bedrock assume it, scoped to your account and to batch jobs so it cannot be used by another account's jobs:

Batch Service Role Trust Policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {"Service": "bedrock.amazonaws.com"},
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {"aws:SourceAccount": "<account-id>"},
        "ArnEquals": {
          "aws:SourceArn": "arn:aws:bedrock:*:<account-id>:model-invocation-job/*"
        }
      }
    }
  ]
}
Batch Service Role Permissions Policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "BatchDataAccess",
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::<bucket-name>/batches/*"
    },
    {
      "Sid": "BatchDataListing",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::<bucket-name>",
      "Condition": {"StringLike": {"s3:prefix": "batches/*"}}
    },
    {
      "Sid": "BatchModelInvocation",
      "Effect": "Allow",
      "Action": "bedrock:InvokeModel",
      "Resource": [
        "arn:aws:bedrock:*::foundation-model/*",
        "arn:aws:bedrock:*:<account-id>:inference-profile/*"
      ]
    }
  ]
}

Repeat the S3 statements for each AWS_S3_REGIONAL_BUCKETS bucket, and substitute batches/ with your AWS_S3_BATCHES_PREFIX. A cross-region inference profile needs bedrock:InvokeModel on both the profile and the foundation models behind it — see Inference Profiles.


Text-to-Speech (Optional)

Environment Variables: AWS_POLLY_REGION, DEFAULT_TTS_MODEL, DEFAULT_TTS_LANGUAGE, AWS_S3_BUCKET, AWS_S3_REGIONAL_BUCKETS

Required for generating speech from text using Amazon Polly. See the Audio and Text-to-Speech configuration section.

Optimize Performance

Set DEFAULT_TTS_LANGUAGE to skip language detection and avoid Amazon Comprehend API calls, improving response times and reducing costs.

Polly Text-to-Speech IAM Policy Statements
{
  "Sid": "PollyTextToSpeech",
  "Effect": "Allow",
  "Action": [
    "polly:SynthesizeSpeech",
    "polly:StartSpeechSynthesisStream",
    "polly:DescribeVoices",
    "polly:StartSpeechSynthesisTask",
    "polly:GetSpeechSynthesisTask"
  ],
  "Resource": "*"
},
{
  "Sid": "PollyS3Storage",
  "Effect": "Allow",
  "Action": [
    "s3:PutObject",
    "s3:GetObject",
    "s3:DeleteObject"
  ],
  "Resource": "arn:aws:s3:::AWS_S3_BUCKET_VALUE/*"
}

Only for Long Input

The two task actions and the S3 statement serve input above 3,000 characters, which Amazon Polly synthesizes into a bucket co-located with the serving region.

Replace AWS_S3_BUCKET_VALUE with the value of your AWS_S3_BUCKET environment variable, and repeat the statement for each AWS_S3_REGIONAL_BUCKETS bucket serving a Polly region. Amazon Polly writes the audio object with the identity that started the synthesis, which is why s3:PutObject belongs to this policy; the audio is then read back and deleted once the response has been sent.

Granting the permissions is not what enables long input

The 3,000-character limit is decided by the configuration, not by this policy: text-to-speech stays capped at 3,000 characters per request — and requests above it are rejected with that limit — as long as no bucket is configured for the Polly regions (AWS_S3_BUCKET, AWS_S3_REGIONAL_BUCKETS).

With a bucket configured but these actions missing, long requests are accepted and then fail on a permission error instead. Grant the whole set, or leave the bucket unconfigured.

If your S3 buckets use KMS encryption, also add the KMS permissions for each bucket's key, with that region's kms:ViaService value.


Speech-to-Text (Optional)

Environment Variables: AWS_TRANSCRIBE_REGION, AWS_TRANSCRIBE_S3_BUCKET, AWS_S3_REGIONAL_BUCKETS, AWS_TRANSCRIBE_OUTPUT_ENCRYPTION_KEY_ARN

Required for transcribing audio files using Amazon Transcribe. Each transcription job stages its audio in a bucket co-located with the Transcribe endpoint, so the S3 statement must cover every bucket that serves a candidate region.

Transcribe Speech-to-Text IAM Policy Statements
{
  "Sid": "TranscribeSpeechToText",
  "Effect": "Allow",
  "Action": [
    "transcribe:StartTranscriptionJob",
    "transcribe:GetTranscriptionJob",
    "transcribe:DeleteTranscriptionJob",
    "transcribe:StartStreamTranscription"
  ],
  "Resource": "*"
},
{
  "Sid": "TranscribeTagging",
  "Effect": "Allow",
  "Action": [
    "transcribe:TagResource"
  ],
  "Resource": "arn:aws:transcribe:*:*:transcription-job/*"
},
{
  "Sid": "TranscribeS3Storage",
  "Effect": "Allow",
  "Action": [
    "s3:PutObject",
    "s3:PutObjectTagging",
    "s3:GetObject",
    "s3:DeleteObject",
    "s3:AbortMultipartUpload",
    "s3:ListMultipartUploadParts"
  ],
  "Resource": "arn:aws:s3:::AWS_TRANSCRIBE_S3_BUCKET_VALUE/*"
}

Replace Bucket Name

Replace AWS_TRANSCRIBE_S3_BUCKET_VALUE with the value of your AWS_TRANSCRIBE_S3_BUCKET environment variable (or AWS_S3_BUCKET if using the same bucket).

One Bucket per Candidate Region

With the default multi-region behavior (AWS_TRANSCRIBE_REGION unset), Transcribe fails over across the AWS_BEDROCK_REGIONS that have a co-located bucket: the primary region uses the bucket above, the others their AWS_S3_REGIONAL_BUCKETS entry. Repeat the TranscribeS3Storage statement for each of those buckets.

On failover the audio is server-side copied from the previous candidate's bucket to the next one, which is why the copy and multipart actions are required. Set AWS_TRANSCRIBE_REGION to pin a single region and keep a single bucket.

StartStreamTranscription needs no bucket

transcribe:StartStreamTranscription serves stream=true requests, which send their audio to Transcribe directly instead of staging it. A deployment with no bucket at all still serves those, and only those — the TranscribeS3Storage statement above is what the other requests need.

If your transcribe S3 buckets use KMS encryption, also add the KMS permissions for each bucket's key, with that region's kms:ViaService value.

Encrypting the transcription output with your own key (AWS_TRANSCRIBE_OUTPUT_ENCRYPTION_KEY_ARN) additionally requires:

Transcribe Output Encryption IAM Policy Statement
{
  "Sid": "TranscribeOutputEncryption",
  "Effect": "Allow",
  "Action": [
    "kms:GenerateDataKey",
    "kms:Decrypt"
  ],
  "Resource": "AWS_TRANSCRIBE_OUTPUT_ENCRYPTION_KEY_ARN_VALUE"
}

Replace Key ARN

Replace AWS_TRANSCRIBE_OUTPUT_ENCRYPTION_KEY_ARN_VALUE with the value of your AWS_TRANSCRIBE_OUTPUT_ENCRYPTION_KEY_ARN environment variable. The key policy must allow the same actions for this role; kms:Decrypt is what lets the finished transcript be read back.


Language Detection (Optional)

Environment Variables: AWS_COMPREHEND_REGION

Required for automatic language detection (used by TTS for voice selection).

Comprehend Language Detection IAM Policy Statement
{
  "Sid": "ComprehendLanguageDetection",
  "Effect": "Allow",
  "Action": [
    "comprehend:DetectDominantLanguage"
  ],
  "Resource": "*"
}

Comprehend Moderation (Optional)

Environment Variables: AWS_COMPREHEND_REGION

Required for the Moderations API toxicity backend — the default backend when no Bedrock guardrail is configured, and always available as the amazon.comprehend-toxicity model.

Comprehend Moderation IAM Policy Statement
{
  "Sid": "ComprehendModeration",
  "Effect": "Allow",
  "Action": [
    "comprehend:DetectToxicContent"
  ],
  "Resource": "*"
}

Text Translation (Optional)

Environment Variables: AWS_TRANSLATE_REGION

Required for text translation features.

Translate Text Translation IAM Policy Statement
{
  "Sid": "TranslateTextTranslation",
  "Effect": "Allow",
  "Action": [
    "translate:TranslateText",
    "translate:ListLanguages"
  ],
  "Resource": "*"
}

translate:ListLanguages

Read once at startup, to refuse a language Amazon Translate does not support before the audio is transcribed. Without it translation still works: an unsupported language is then reported once the translation call itself fails.


Cost Tracking (Optional)

Environment Variables: COST_TRACKING

Required for COST_TRACKING (disabled by default), which prices requests from the AWS Price List API. Without this permission the catalog stays empty and request logs carry no cost data.

Cost Tracking IAM Policy Statement
{
  "Sid": "PricingCatalog",
  "Effect": "Allow",
  "Action": [
    "pricing:GetProducts"
  ],
  "Resource": "*"
}

Usage API (Optional)

Environment Variables: USAGE_API

Required only when USAGE_API is enabled (disabled by default), which serves the organization usage and costs endpoints by reading the Amazon CloudWatch metrics the deployment publishes. Without these permissions those endpoints cannot answer; nothing else in the deployment is affected.

Usage API IAM Policy Statement
{
  "Sid": "UsageApiMetricRead",
  "Effect": "Allow",
  "Action": [
    "cloudwatch:GetMetricData",
    "cloudwatch:ListMetrics"
  ],
  "Resource": "*"
}

Why the resource is *

CloudWatch metric actions do not support resource-level permissions: there is no metric ARN to name, so cloudwatch:GetMetricData and cloudwatch:ListMetrics can only be granted on *. The cloudwatch:namespace condition key is documented for PutMetricData and OTLP ingest, not for reads, so it cannot narrow these two either. See Actions, resources, and condition keys for Amazon CloudWatch.

cloudwatch:GetMetricData is billed per metric read

It is also excluded from the CloudWatch free tier. Price the endpoints before you expose them — Usage API Query Cost.


Per-User Cost Attribution (Optional)

Environment Variables: AWS_BEDROCK_USER_ROLE_ARN

Required to run each end user's model calls under a role session of their own, so AWS reports their spend separately. Three policies are involved: the server's own role must be allowed to open the sessions, the end user role must trust it to do so, and the end user role must be allowed to invoke models.

1. On the server's role — allow it to open sessions of the end user role, and of that role only:

Server Role IAM Policy Statement
{
  "Sid": "EndUserRoleSessions",
  "Effect": "Allow",
  "Action": [
    "sts:AssumeRole",
    "sts:TagSession"
  ],
  "Resource": "arn:aws:iam::ACCOUNT_ID:role/stdapi-ai-end-user"
}

2. Trust policy of the end user role — allow the server's role, and nothing else, to assume it and to tag the session. sts:TagSession is a separate action: without it, every session that carries the end user tag is denied.

End User Role Trust Policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::ACCOUNT_ID:role/stdapi-ai-task-role"
      },
      "Action": [
        "sts:AssumeRole",
        "sts:TagSession"
      ]
    }
  ]
}

3. Permission policy of the end user role — everything AWS authorizes against the caller of a model invocation: the invocation actions on the models the deployment serves, the guardrail the invocation carries, and the S3 objects it references:

End User Role IAM Policy Statements
{
  "Sid": "EndUserModelInvoke",
  "Effect": "Allow",
  "Action": [
    "bedrock:InvokeModel",
    "bedrock:InvokeModelWithResponseStream"
  ],
  "Resource": [
    "arn:aws:bedrock:*:ACCOUNT_ID:inference-profile/*",
    "arn:aws:bedrock:*:ACCOUNT_ID:application-inference-profile/*",
    "arn:aws:bedrock:*:ACCOUNT_ID:default-prompt-router/*",
    "arn:aws:bedrock:*::foundation-model/*",
    "arn:aws:bedrock:*:ACCOUNT_ID:marketplace/model-endpoint/*"
  ]
},
{
  "Sid": "EndUserApplyGuardrail",
  "Effect": "Allow",
  "Action": [
    "bedrock:ApplyGuardrail"
  ],
  "Resource": "arn:aws:bedrock:*:ACCOUNT_ID:guardrail/*"
},
{
  "Sid": "EndUserS3ReferencedMedia",
  "Effect": "Allow",
  "Action": [
    "s3:GetObject"
  ],
  "Resource": "arn:aws:s3:::AWS_S3_BUCKET_VALUE/*"
}

If the buckets it reads use KMS encryption, also add:

{
  "Sid": "EndUserS3ReferencedMediaKMS",
  "Effect": "Allow",
  "Action": [
    "kms:Decrypt"
  ],
  "Resource": "arn:aws:kms:REGION:ACCOUNT_ID:key/YOUR_KMS_KEY_ID",
  "Condition": {
    "StringEquals": {
      "kms:ViaService": "s3.REGION.amazonaws.com"
    }
  }
}

Replace ACCOUNT_ID with your AWS account ID, stdapi-ai-task-role with the role the server runs as, and AWS_S3_BUCKET_VALUE with each bucket an invocation can reference by URI (see the S3 warning below).

An inference profile needs the foundation models behind it

A cross-region inference profile routes to a foundation model in each of its Regions, and AWS authorizes both the profile ARN and every foundation model ARN it reaches. A policy naming only inference-profile/... fails with an access-denied error naming foundation-model/... in a Region you never configured. Keep arn:aws:bedrock:*::foundation-model/... alongside the profile, or the call is denied.

A Marketplace model endpoint is authorized against a different resource shape

bedrock:InvokeModel on a Marketplace model endpoint is authorized against arn:aws:bedrock:*:ACCOUNT_ID:marketplace/model-endpoint/*not against the SageMaker endpoint ARN the request names. A policy that only lists foundation-model/* and the inference-profile forms above denies every Marketplace endpoint invocation with an access-denied error, even though the same statement already covers ordinary models. Keep this resource whenever AWS_BEDROCK_MARKETPLACE_ENDPOINTS_ENABLED or AWS_BEDROCK_ALLOW_MARKETPLACE_ENDPOINT_ARN can put an endpoint in front of a request under this role.

Media referenced by S3 URI is read as the end user

An invocation may carry its media as an s3Location rather than inline — large attachments the server uploads to a regional bucket, a Files API file_id, or an S3 URI the client sent from an accepted bucket — and Amazon Bedrock then reads that object with the session that signed the invocation, not with the server's role: the assumed role must have the s3:GetObject permission to the Amazon S3 URI. Without the EndUserS3ReferencedMedia statement, those requests fail with an access-denied error the caller sees as a 403, while the same request without per-user attribution succeeds. List every bucket a URI can name: AWS_S3_BUCKET, each AWS_S3_REGIONAL_BUCKETS bucket, and each AWS_S3_ACCEPTED_BUCKETS bucket — with the KMS statement for every encrypted one. The role needs no write action: the upload the server performs before the invocation keeps the server's own identity.

A configured guardrail is authorized against the end user

A guardrail applied during an invocation — AWS_BEDROCK_GUARDRAIL_IDENTIFIER, a model alias carrying one, or a request-level moderation parameter — is evaluated as part of that invocation, so AWS requires bedrock:ApplyGuardrail from the identity making the call. Without the EndUserApplyGuardrail statement, every model request fails with an access-denied error as soon as per-user attribution is enabled. See Set up permissions to use Amazon Bedrock Guardrails. Narrow the resource to your guardrail ARN if you prefer.

Name every model ARN form the deployment allows

The Resource list must cover every ARN a request can resolve to. Add arn:aws:bedrock:*:ACCOUNT_ID:prompt/* when AWS_BEDROCK_ALLOW_PROMPT_ARN is enabled, and keep the application inference profile, prompt router and marketplace model endpoint entries above whenever AWS_BEDROCK_ALLOW_APPLICATION_INFERENCE_PROFILE_ARN, AWS_BEDROCK_ALLOW_PROMPT_ROUTER_ARN, AWS_BEDROCK_MARKETPLACE_ENDPOINTS_ENABLED, AWS_BEDROCK_ALLOW_MARKETPLACE_ENDPOINT_ARN or AWS_BEDROCK_MODEL_ARN_MAPPING can put one in front of a model. An ARN form the end user role does not name is denied under it while it still works on the server's role.

Add the Web Search actions to this role as well if you serve web_search requests: AWS evaluates them when the model actually runs a search, which happens inside the invocation the end user signed. A denied search does not fail the request, it degrades the answer.

A session tag is an access boundary only when the identity is verified

The end user identity is taken from the authenticated caller only under Amazon Cognito authentication. With an API key, or with no authentication, it is whatever the client declared in the request body (safety_identifier, user, metadata.user_id) — so any caller holding the key can send another user's identifier and obtain that user's session tag.

Write policies conditioned on aws:PrincipalTag/<key> only when AUTHENTICATION_MODE is cognito, which is the configuration where every request carries an identity the gateway verified. Anywhere else, treat the tag as cost metadata, never as an authorization input.

Restricting a role per end user

Where the identity is verified, the session tag makes it testable in a policy: compare it to something on the resource side, so each session reaches only its own data — "StringEquals": {"aws:ResourceTag/user": "${aws:PrincipalTag/user}"}, an s3:prefix condition, or a Resource ARN embedding ${aws:PrincipalTag/user}. A condition comparing the tag to itself always matches and restricts nothing. A Deny on any tag value the deployment does not expect is the other half of the same pattern. Set AWS_BEDROCK_USER_ROLE_TAG_KEY to the key the policy tests.

Scope

Only Bedrock model invocations run under the end user role, together with the guardrail applied during them and the S3 read Amazon Bedrock performs for an s3Location they carry. Standalone guardrail evaluations (the Moderations API), reranking, video generation and its output files, speech, transcription and translation keep the server's own role, so the end user role needs none of their permissions — and the server's role still needs all of them.


API Key Authentication (Optional)

Required if you configure API authentication. See the Authentication configuration section.

SSM Parameter Store

Environment Variables: API_KEY_SSM_PARAMETER

SSM Parameter Store IAM Policy Statements
{
  "Sid": "SSMParameterAccess",
  "Effect": "Allow",
  "Action": [
    "ssm:GetParameter"
  ],
  "Resource": "arn:aws:ssm:REGION:ACCOUNT_ID:parameter/API_KEY_SSM_PARAMETER_VALUE"
}

Replace Parameter Path

Replace API_KEY_SSM_PARAMETER_VALUE with the value of your API_KEY_SSM_PARAMETER environment variable (e.g., /stdapi/prod/api-key), and REGION with the server's own region (AWS_REGION) — parameters are read there, not in the Bedrock regions.

If using encrypted SSM parameters, also add:

{
  "Sid": "KMSDecryptionForSSM",
  "Effect": "Allow",
  "Action": [
    "kms:Decrypt"
  ],
  "Resource": "arn:aws:kms:REGION:ACCOUNT_ID:key/YOUR_KMS_KEY_ID",
  "Condition": {
    "StringEquals": {
      "kms:ViaService": "ssm.REGION.amazonaws.com"
    }
  }
}

KMS Security

The kms:ViaService condition restricts KMS key usage to SSM service calls only.

Secrets Manager

Environment Variables: API_KEY_SECRETSMANAGER_SECRET

Secrets Manager IAM Policy Statement
{
  "Sid": "SecretsManagerAccess",
  "Effect": "Allow",
  "Action": [
    "secretsmanager:GetSecretValue"
  ],
  "Resource": "arn:aws:secretsmanager:REGION:ACCOUNT_ID:secret:API_KEY_SECRETSMANAGER_SECRET_VALUE"
}

Replace Secret Name

Replace API_KEY_SECRETSMANAGER_SECRET_VALUE with the value of your API_KEY_SECRETSMANAGER_SECRET environment variable (e.g., stdapi-api-key), and REGION with the server's own region (AWS_REGION) — secrets are read there, not in the Bedrock regions.


Complete Policy Examples

Minimal Policy (Bedrock Only)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "BedrockModelInvoke",
      "Effect": "Allow",
      "Action": [
        "bedrock:CountTokens",
        "bedrock:GetAsyncInvoke",
        "bedrock:InvokeGuardrailChecks",
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithBidirectionalStream",
        "bedrock:InvokeModelWithResponseStream",
        "bedrock:InvokeTool",
        "bedrock:Rerank"
      ],
      "Resource": "*"
    },
    {
      "Sid": "BedrockAsyncInvokeTagging",
      "Effect": "Allow",
      "Action": [
        "bedrock:TagResource"
      ],
      "Resource": "arn:aws:bedrock:*:*:async-invoke/*"
    },
    {
      "Sid": "BedrockModelDiscovery",
      "Effect": "Allow",
      "Action": [
        "bedrock:ListFoundationModels",
        "bedrock:GetFoundationModelAvailability",
        "bedrock:ListProvisionedModelThroughputs",
        "bedrock:ListInferenceProfiles"
      ],
      "Resource": "*"
    },
    {
      "Sid": "BedrockMarketplaceAutoSubscribe",
      "Effect": "Allow",
      "Action": [
        "aws-marketplace:Subscribe",
        "aws-marketplace:ViewSubscriptions"
      ],
      "Resource": "*"
    },
    {
      "Sid": "MarketplaceRegisterUsage",
      "Effect": "Allow",
      "Action": [
        "aws-marketplace:RegisterUsage"
      ],
      "Resource": "*"
    }
  ]
}

Marketplace Auto-Subscribe (Default Enabled)

The marketplace permissions are included because AWS_BEDROCK_MARKETPLACE_AUTO_SUBSCRIBE defaults to true. If you set it to false, you can remove the BedrockMarketplaceAutoSubscribe statement.

Marketplace RegisterUsage (AWS Marketplace Image Only)

MarketplaceRegisterUsage is only needed on the AWS Marketplace image; remove it when deploying the community image.

Production Policy (Bedrock + S3 + Authentication)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "BedrockModelInvoke",
      "Effect": "Allow",
      "Action": [
        "bedrock:CountTokens",
        "bedrock:GetAsyncInvoke",
        "bedrock:InvokeGuardrailChecks",
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithBidirectionalStream",
        "bedrock:InvokeModelWithResponseStream",
        "bedrock:InvokeTool",
        "bedrock:Rerank"
      ],
      "Resource": "*"
    },
    {
      "Sid": "BedrockAsyncInvokeTagging",
      "Effect": "Allow",
      "Action": [
        "bedrock:TagResource"
      ],
      "Resource": "arn:aws:bedrock:*:*:async-invoke/*"
    },
    {
      "Sid": "BedrockModelDiscovery",
      "Effect": "Allow",
      "Action": [
        "bedrock:ListFoundationModels",
        "bedrock:GetFoundationModelAvailability",
        "bedrock:ListProvisionedModelThroughputs",
        "bedrock:ListInferenceProfiles"
      ],
      "Resource": "*"
    },
    {
      "Sid": "BedrockMarketplaceAutoSubscribe",
      "Effect": "Allow",
      "Action": [
        "aws-marketplace:Subscribe",
        "aws-marketplace:ViewSubscriptions"
      ],
      "Resource": "*"
    },
    {
      "Sid": "MarketplaceRegisterUsage",
      "Effect": "Allow",
      "Action": [
        "aws-marketplace:RegisterUsage"
      ],
      "Resource": "*"
    },
    {
      "Sid": "S3FileStorage",
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:PutObjectTagging",
        "s3:GetObject",
        "s3:DeleteObject",
        "s3:AbortMultipartUpload",
        "s3:ListMultipartUploadParts"
      ],
      "Resource": "arn:aws:s3:::my-stdapi-bucket/*"
    },
    {
      "Sid": "S3FileStorageList",
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:ListBucketMultipartUploads"
      ],
      "Resource": "arn:aws:s3:::my-stdapi-bucket"
    },
    {
      "Sid": "SSMParameterAccess",
      "Effect": "Allow",
      "Action": [
        "ssm:GetParameter"
      ],
      "Resource": "arn:aws:ssm:us-east-1:123456789012:parameter/stdapi/prod/api-key"
    },
    {
      "Sid": "PricingCatalog",
      "Effect": "Allow",
      "Action": [
        "pricing:GetProducts"
      ],
      "Resource": "*"
    },
    {
      "Sid": "UsageApiMetricRead",
      "Effect": "Allow",
      "Action": [
        "cloudwatch:GetMetricData",
        "cloudwatch:ListMetrics"
      ],
      "Resource": "*"
    }
  ]
}

Marketplace Auto-Subscribe (Default Enabled)

The marketplace permissions are included because AWS_BEDROCK_MARKETPLACE_AUTO_SUBSCRIBE defaults to true. If you set it to false, you can remove the BedrockMarketplaceAutoSubscribe statement to follow the principle of least privilege.

Marketplace RegisterUsage (AWS Marketplace Image Only)

MarketplaceRegisterUsage is only needed on the AWS Marketplace image; remove it when deploying the community image.

Cost Tracking (Opt-In)

PricingCatalog is only needed when COST_TRACKING is set to true; remove it otherwise.

Usage API (Opt-In)

UsageApiMetricRead is only needed when USAGE_API is set to true; remove it otherwise. See Usage API for why the resource cannot be narrowed, and Usage API Query Cost for what the reads are billed at.


IAM Role vs. IAM User

stdapi.ai supports both IAM roles and IAM users:

  • IAM Role (Recommended): Use when running on EC2, ECS, Lambda, or other AWS compute services. Attach the policy to the instance/task role.
  • IAM User: Use when running outside AWS or for development. Create an IAM user with the required permissions and configure AWS credentials via environment variables or AWS CLI configuration.

Best Practice: Use IAM Roles

When deploying on AWS infrastructure, always prefer IAM roles over IAM users with access keys. IAM roles provide automatic credential rotation and better security.


AWS Tag Policies

If your AWS organization enforces a tag policy, the following tag keys must be allowed on the relevant resource types.

Tag key Value Applied to
stdapi-ai.expires "true" S3 objects (Files API expiry)
stdapi-ai.request_id request UUID Bedrock async jobs, Transcribe jobs
stdapi-ai.server_id server instance name Bedrock async jobs, Transcribe jobs
stdapi-ai.user_id user identifier Bedrock async jobs, Transcribe jobs (when user identity is known)
aws-apn-id pc:<product-code> All AWS resources created at runtime and by the Terraform module. This is a standard AWS Marketplace attribution tag required by any AWS Marketplace product — allowing it benefits all such products deployed in your organization, not only stdapi.ai.

Next Steps