Skip to Content

Marketplace Offers — Trust Policy

The trust policy on the FlowStateMarketplaceConnector IAM role controls who can assume the role and under what conditions.

The trust policy

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::<FLOWSTATE_AWS_ACCOUNT>:role/flowstate-<stage>-backend", "arn:aws:iam::<FLOWSTATE_AWS_ACCOUNT>:role/flowstate-<stage>-mcp-execution" ] }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "<YOUR_EXTERNAL_ID>" } } } ] }

Fill in your FlowState account id, stage, and ExternalId — your FlowState contact provides all three, or use the values shown in Getting started.

Two principals — both required

PrincipalWhy it needs to assume your role
flowstate-<stage>-backendServes the REST API — the FlowState web app and the HubSpot card.
flowstate-<stage>-mcp-executionServes the MCP tools.

Include both. A trust policy listing only the backend role works through the web app and the card, then fails with AccessDenied the moment anyone uses Marketplace Offers through an MCP client.

The CloudFormation parameter and OpenTofu variable are both lists (FlowstatePrincipalArns / flowstate_principal_arns), so add both ARNs to the list.

sts:ExternalId

The ExternalId is a secret, unique to your tenant, generated by FlowState. It must match exactly what Getting started shows you — no leading or trailing spaces — or the AssumeRole call is denied. It protects against the confused-deputy problem : without it, another FlowState tenant could not trick FlowState into assuming your role, because they don’t have your ExternalId.


The permissions policy

This role’s permissions are an inline policy — attach it directly, there is no AWS-managed policy to reference.

{ "Version": "2012-10-17", "Statement": [ { "Sid": "MarketplaceCatalogList", "Effect": "Allow", "Action": ["aws-marketplace:ListEntities"], "Resource": "*" }, { "Sid": "MarketplaceCatalogManage", "Effect": "Allow", "Action": [ "aws-marketplace:DescribeEntity", "aws-marketplace:StartChangeSet", "aws-marketplace:DescribeChangeSet" ], "Resource": [ "arn:aws:aws-marketplace:us-east-1:<YOUR_ACCOUNT_ID>:AWSMarketplace/SaaSProduct/*", "arn:aws:aws-marketplace:us-east-1:<YOUR_ACCOUNT_ID>:AWSMarketplace/Offer/*", "arn:aws:aws-marketplace:us-east-1:<YOUR_ACCOUNT_ID>:AWSMarketplace/ChangeSet/*", "arn:aws:aws-marketplace:us-east-1:<YOUR_ACCOUNT_ID>:AWSMarketplace/Seller/*" ] }, { "Sid": "MarketplaceAgreementRead", "Effect": "Allow", "Action": ["aws-marketplace:SearchAgreements"], "Resource": "*", "Condition": { "ForAllValues:StringEquals": { "aws-marketplace:AgreementType": ["PurchaseAgreement"] }, "StringEquals": { "aws-marketplace:PartyType": "Proposer" } } } ] }

Replace <YOUR_ACCOUNT_ID> with your AWS account id.

Note the action prefix: it’s aws-marketplace:, not marketplace-catalog:, even though the API and SDK client are both named marketplace-catalog.

Region: the AWS Marketplace Catalog API exists in us-east-1 only, regardless of which region your seller account otherwise operates in. Keep the resource ARNs above pinned to us-east-1.

What each statement is for:

  • MarketplaceCatalogList — find your SaaS products and offers.
  • MarketplaceCatalogManage — read a product or offer, create/clone/release/expire offers (all three actions are needed for every one of those operations), and read your seller profile’s provisioned currencies so the offer form can show only currencies you’re actually able to be paid in.
  • MarketplaceAgreementRead — list the purchase agreements your offers have produced.

This is a read/write grant on your own Marketplace catalog and a read-only grant on your agreements and seller profile. Nothing outside AWS Marketplace is touched.