Skill 61 · AWS Marketplace Metering
Subchapter 61.1
references/architecture.mdMarkdown14 KBView on GitHub
us-east-1 seller region (e.g. us-west-2)
┌─────────────────────┐ ┌──────────────────────────────────┐
│ EventBridge Rule │ │ API Gateway │
Marketplace ──────► │ (aws.agreement- │ │ POST /register │
Agreement + │ marketplace) │ │ │ │
License Events │ │ │ │ ▼ │
│ ▼ │ │ Register Lambda │
│ SQS Queue ──► DLQ │ │ (ResolveCustomer) │
│ │ │ │ │ │
│ ▼ │ read+write │ └──► updates subscribers ──┤
│ Subscription Lambda │◄─────────────┼────────────(cross-region) │
│ │ │ │ │
│ ▼ │ │ EventBridge Schedule (rate 15m) │
│ DynamoDB │ │ │ │
│ (awsmp-subscribers) │◄─────────────┼── Metering pipeline reads subs │
│ PK: licenseArn │ read │ ▼ │
│ SK: customerAWSAcct │ │ discoverer→aggregator→cleanup │
│ productCode, │ │ →submitter (→ BatchMeterUsage) │
│ agreementStatus, │ │ │
│ subscriptionStatus, │ │ DynamoDB (usage) │
│ registeredRegions │ │ PK: licenseArn │
└─────────────────────┘ │ SK: custAcct#dimension#ts │
└──────────────────────────────────┘Unified Subscribers Table — a SINGLE table holding subscription/agreement STATE, PII-FREE. Lives in us-east-1 alongside EventBridge. Populated by EventBridge events and updated by the register Lambda (identity + statuses + registeredRegions only). Buyer registration-form data (PII) is NOT stored here — it lives in the per-Region customer-profile table (below).
For Concurrent Agreements (default, new products): licenseArn is the PARTITION key, customerAWSAccountId is the sort key. A buyer with multiple concurrent agreements has multiple rows (same customerAWSAccountId, different licenseArn), tracked independently.
For legacy (pre-CA) products: key by customerAWSAccountId alone.
| Field | Source | Description |
|---|---|---|
licenseArn (PK, CA) | License Updated event: detail.license.arn | License ARN — partition key for CA products |
customerAWSAccountId (SK) | Agreement events: detail.acceptor.accountId | Buyer’s AWS account ID |
productCode | License Updated event: detail.product.code | Metering product code |
agreementId | Agreement events: detail.agreement.id | Unique agreement identifier |
agreementStatus | Set by subscription Lambda | active (License Updated) / inactive (Purchase Agreement Ended) — the agreement lifecycle |
subscriptionStatus | Set by subscription Lambda (transitions) + deprovision-cleanup Lambda (finalization) | active (License Updated) → deprovisioning (License Deprovisioned — final-usage flush window open) → inactive (deprovision-cleanup Lambda, once deprovisioningExpiry passes). There is no deprovisioned value. |
registeredRegions | Appended by the register Lambda | DynamoDB String Set (SS) of AWS Region names where the buyer was registered (added idempotently via ADD, so no duplicates). Reference-only; NEVER gates metering |
customerIdentifier | ResolveCustomer response (legacy only) | Deprecated; persisted only for legacy (pre-CA) support, NOT for new integrations |
The subscribers table is PII-FREE. It carries only the CA identity (
licenseArn,customerAWSAccountId,productCode,agreementId), the two lifecycle statuses, andregisteredRegions. Buyer registration-form data (PII — contact name, email, company, KYB, seller-defined fields) is NOT stored here; it lives in the per-Regioncustomer-profiletable (below), written in-region by the register Lambda. Because it holds no PII, the subscribers table is safe inus-east-1even when the product/buyers operate in other (opt-in) Regions.
Customer-profile table (per Region) — awsmp-<productCode>-customer-profile, keyed by licenseArn (PK) + customerAWSAccountId (SK), deployed IN EACH metering/registration Region. Holds the seller’s allowlisted registration/KYB fields (buyer PII), with additional seller-requested GSIs on those fields. Written by the in-region register Lambda; kept in-region so buyer PII is never replicated to us-east-1. Not on the metering path.
Mapping a
licenseArnto the seller’s internal SaaS tenant ID is the SELLER’s responsibility — this skill does not own the tenant model. AWS Marketplace identifies a buyer bylicenseArn(+customerAWSAccountId); how that maps to the seller’s own tenant/account/org ID is the seller’s application concern. The skill provides TWO places the seller can anchor that mapping, and the seller CHOOSES which (or both):
- Subscribers table (us-east-1) — carries the
licenseArnfrom theLicense UpdatedEventBridge event (the authoritative, refreshed-on-renewal source; PII-free, so store only a non-PII internal tenant identifier here if you add one).- Customer-profile table (in-Region) — carries the
licenseArncaptured at registration time (when the buyer clicks “Set up your account” andResolveCustomerruns), alongside the buyer’s registration/KYB fields — a natural home if the tenant record is created from the registration form.A row may exist in one before the other (registration and
License Updatedcan arrive in either order), so a seller that needs the mapping available from the earliest moment often writes it in BOTH. The seller adds their owntenantId(or equivalent) attribute — the skill neither invents nor requires a specific tenant-ID scheme — and, if they need to look a tenant up by that ID, defines a GSI on it (on the customer-profile table for a PII/registration-derived ID, or on the subscribers table for a non-PII one).
Usage Table — keyed by licenseArn (PK) + customerAWSAccountId#dimension#timestamp (SK, second precision). Keying by licenseArn (not account) ensures usage is attributed per-agreement, preventing double-billing when a buyer holds multiple concurrent licenses. customerAWSAccountId, dimension, and timestamp are also stored as top-level attributes so the pipeline reads them without parsing the sort key. The seller writes usage here (and sets meteringPending); the metering pipeline reads it and calls BatchMeterUsage.
1. Buyer clicks "Subscribe"
→ EventBridge: Purchase Agreement Created (us-east-1)
→ NOT consumed by the metering path. It carries no licenseArn and is not needed
to meter usage. Sellers MAY add their own rule/target for other use cases
(see "Purchase Agreement Created — seller use cases" below).
2. Shortly after (~seconds)
→ EventBridge: License Updated (us-east-1)
→ Subscription Lambda UPSERTS the subscriber record (us-east-1), keyed by the real
licenseArn (detail.license.arn) + customerAWSAccountId, with productCode
(detail.product.code) and subscriptionStatus=active. This is the row that drives
metering — there is no separate "pending" placeholder row.
3. Buyer clicks "Set up your account"
→ Marketplace redirects the buyer's browser (with x-amzn-marketplace-token) to the
Fulfillment URL = the branded registration PAGE (S3+CloudFront); the page renders,
captures extra buyer/KYB fields, then POSTs the token + fields to the RegistrationUrl
backend (a raw RegistrationUrl Fulfillment URL is a bare-bones fallback only)
→ Register Lambda calls ResolveCustomer (this skill deploys the register Lambda in the
main-stack region as a packaging choice; ResolveCustomer's region is the seller's free
choice and does NOT gate metering or have to match the BatchMeterUsage region)
→ Gets: CustomerAWSAccountId, LicenseArn, ProductCode
→ Writes buyer PII (allowlisted registration/KYB fields) to the IN-REGION
customer-profile table keyed by licenseArn + customerAWSAccountId; upserts the PII-FREE
subscriber row (identity + statuses) and appends the invocation Region to
Status lifecycle: agreementStatus:
active(License Updated) →inactive(Purchase Agreement Ended). subscriptionStatus:active(License Updated) →deprovisioning(License Deprovisioned opens the ~1-hour flush window) →inactive(deprovision-cleanup Lambda, oncedeprovisioningExpirypasses). The two move independently and are never conflated. Metering decisions follow subscriptionStatus / the metering_pending GSI, not agreementStatus or registeredRegions.
The metering path does not consume Purchase Agreement Created, but it is a useful
signal for other seller workflows. Per the AWS Marketplace SaaS EventBridge integration,
this event fires when a new agreement is created, an existing agreement is replaced, or an
existing agreement is renewed, and is delivered to both the manufacturer and proposer
roles. Sellers commonly use it to:
DescribeAgreement to determine whether the new
agreement is a free trial and branch onboarding accordingly.agreement.id (and any prior-agreement linkage) to track renewals and upgrades.resaleAuthorization ID; use this to distinguish direct vs channel-partner resale deals.To act on it, add an EventBridge rule that matches
detail-type: "Purchase Agreement Created - Manufacturer" (and/or - Proposer) with
source: "aws.agreement-marketplace" and route it to your own target — do not add it to the
metering Subscription Lambda. References:
Key insight: The subscribers table lives in
us-east-1alongside EventBridge. When the metering region is a different region, the register Lambda and the metering pipeline (discoverer/submitter) read it cross-region. If the seller’s metering region is alsous-east-1, the events stack and main stack are both inus-east-1and everything is single-region — no cross-region reads at all. us-east-1 is a fully supported metering region forBatchMeterUsage.Multi-region sellers: Each metering region gets its own main stack (usage table + Lambdas). All regions share the single subscribers table in
us-east-1. Deploy the events stack once (us-east-1), then deploy the main stack to each metering region — which MAY include us-east-1 itself.