Amba

Monetization Control Plane

Author, plan, apply, and drift-detect your RevenueCat subscription config as Infrastructure-as-Code.

The monetization control plane lets you manage your app's subscription configuration — entitlements, products, offerings, packages, and paywalls — as Infrastructure-as-Code, on top of your existing RevenueCat project. New to this? Walk the whole flow first in the Monetization Quickstart — this page is the full reference.

Additive changes apply automatically; destructive ones require explicit confirmation. Apply creates entitlements, offerings, packages, and paywall drafts and attaches products with no confirmation. Destructive changes — detaching a product, archiving an entitlement or offering, or removing a package — wait until you confirm with --confirm (or confirm set to the plan_hash); without it they are reported but not run. Applies are durable and pollable: you get an operation handle, writes are paced against the provider's rate limits and idempotency-keyed, an interrupted apply resumes safely, and apply reads back every write, stops on the first failure without rolling back, and never archives the offering customers currently see or detaches a product a live customer resolves (unless you explicitly allow it).

Two changes stay in the RevenueCat dashboard because RevenueCat has no API for them: choosing the default (current) offering ("Make Default") and publishing a paywall live to an offering. Amba authors the paywall draft and tells you the exact dashboard step for the rest — it never silently skips them.

Store products

Apply can now provision your store products through RevenueCat. Declare a product (its store, store identifier, type, and optional provisioning metadata) and a single apply:

  1. Registers it with RevenueCat (the product record, attached to the right app) for any store.
  2. Creates the real product in App Store Connect through RevenueCat's store connection — declare store_metadata.push_to_store: true on an app_store product, plus duration (ONE_WEEK | ONE_MONTH | TWO_MONTHS | THREE_MONTHS | SIX_MONTHS | ONE_YEAR) and subscription_group_name for subscriptions. One-time purchase types need no extra metadata.
// amba.config.json → monetization.products[]
{
  "key": "monthly",
  "store": "app_store",
  "store_identifier": "com.yourapp.monthly",
  "type": "subscription",
  "display_name": "Monthly",
  "store_metadata": {
    "push_to_store": true,
    "duration": "ONE_MONTH",
    "subscription_group_name": "Premium",
    "price": "9.99 USD/month", // documentation for the checklist — no API sets prices
  },
}

Store catalog writes never run off an up-front confirm. A plan with store-product steps always starts in a waiting state — even if you passed --confirm — and only runs after you explicitly approve the started apply (re-run amba monetization apply --confirm). Creating real store products is the one place a second deliberate look is structurally required.

The human-floor checklist

Some store-side steps have no API — anywhere, for anyone. The plan surfaces them as a human_floor checklist with the exact console location for each, so you (or your agent) know precisely what remains:

  • Store SKUs outside the App Store — RevenueCat's store push covers App Store Connect only. Google Play / Stripe / Amazon products are registered with RevenueCat by apply, but the SKU itself is created in the store console (the checklist carries your declared price/duration so it's a copy-paste job).
  • Pricing — no API sets store prices. After a product exists, set its price (and intro/trial offers) in the store console. Your declared store_metadata.price is echoed in the checklist.
  • Agreements, banking, and tax — the Paid Applications Agreement (App Store Connect → Business) and the payments profile (Play Console → Setup) are one-time account-level steps.
  • App review — newly created App Store in-app purchases ship with your next app review submission.

Blocking gaps — a product referenced but never declared, or a store with no connected RevenueCat app — fail the apply up front (422) with the exact fix.

What you get

  • Plan — a three-way diff between the config you've declared in Amba, the baseline you last adopted, and the live config in RevenueCat. It shows the operations an apply would perform (create / update / attach / archive / set-current — including store-product creation steps), a store-floor preflight (blocking gaps the apply can't create), a human-floor checklist (store-side steps no API performs, with exact console locations), and any drift. Store products are managed objects like everything else — out-of-band edits to them surface as drift.
  • Apply — push your declared config to RevenueCat as a durable, pollable operation: starting an apply returns an operation_id you poll to completion, writes are paced against RevenueCat's API rate limits, every write carries an idempotency key (an interrupted apply resumes safely, never double-creating), and the run survives restarts. Additive ops (create entitlements / offerings / packages / paywall drafts; attach products) apply automatically. Destructive ops (detach, archive, remove a package) are gated: they apply only when you confirm the exact plan_hash — pending applies wait for your confirmation. Pass the plan_hash from a fresh plan; apply refuses if RevenueCat drifted since then. Setting the default offering and publishing a paywall live are surfaced as dashboard steps (no RevenueCat API).
  • Drift detection — surfaces RevenueCat objects under Amba's management that changed out-of-band (someone edited them directly in the RevenueCat dashboard). A daily background sweep records drift automatically.
  • Export — snapshot your live RevenueCat config into a declarative bundle you can review and version-control.
  • Adopt — record the live config as Amba's managed baseline so the next plan is a clean no-op and future out-of-band edits surface as drift. Adopt writes only Amba state; it never changes RevenueCat. It's idempotent.

Prerequisites

Connect RevenueCat as an integration with a secret API key. Plan, drift, and export work with a read-scoped key (project_configuration:*:read). To apply, supply a read-write key (project_configuration:*:read_write) as secret_api_key_write on the integration — or use one full-access key for both. Amba resolves keys server-side; they are never returned to a client.

To push products into App Store Connect, connect your App Store Connect API key to RevenueCat (RevenueCat dashboard → your app's configuration) — apply creates the products through that connection. What no API can do — prices, agreements/banking/tax, app review, and store SKUs outside the App Store — stays on the plan's human-floor checklist with the exact console location for each.

Plan

amba monetization plan

Returns the op-list, the store-floor preflight, and drift. Nothing is applied — this is a preview.

{
  "clean": false,
  "ops": [
    { "op": "create", "object_type": "product", "identifier": "monthly", "status": "pending" },
    { "op": "create", "object_type": "entitlement", "identifier": "premium", "status": "pending" }
  ],
  "store_floor": [],
  "human_floor": [
    {
      "code": "store_pricing_required",
      "product_key": "monthly",
      "store": "app_store",
      "title": "Set the price for \"com.yourapp.monthly\"",
      "location": "App Store Connect → My Apps → your app → Monetization (In-App Purchases / Subscriptions)"
    }
  ],
  "drift": []
}

Apply

amba monetization apply

Pushes your declared config to RevenueCat. It computes a fresh plan first, then starts a durable apply and returns an operation_id — the CLI polls it to completion for you; over the API, poll GET /v1/admin/projects/:projectId/operations/:operationId until succeeded | failed. The apply paces its writes against RevenueCat's API rate limits (a large plan takes a few minutes), and every write carries an idempotency key, so an interrupted apply resumes exactly where it left off without double-creating anything (a re-attempted App Store push that already landed resolves cleanly). If a blocking store-floor gap exists it stops up front and tells you exactly what to fix.

If the plan includes destructive ops (detach a product, archive an entitlement/offering/product, remove a package) or store-product creation steps, the apply reports them and waits for your confirmation without running them — review (including the human-floor checklist), then confirm:

amba monetization apply --confirm

Confirmation approves the exact plan_hash you reviewed. If the plan changed between plan and confirm, the confirmation is rejected and nothing destructive runs — re-plan and apply again. An unconfirmed apply expires on its own after 24 hours.

--confirm executes the gated ops in a safe order (store-product creates first — before anything that references them — then detach → remove package → archive entitlement → archive product → archive offering, dead-last), and:

  • it refuses to archive the offering customers currently see (RevenueCat has no API to move the default pointer — make a different offering the default in the dashboard first);
  • it refuses to detach the last product a live customer resolves unless you add --allow-detach-live to accept the impact.

For configuration that drifted out-of-band, choose how to reconcile:

amba monetization apply --confirm --reconcile adopt   # pull live changes into your config
amba monetization apply --confirm --reconcile amba    # revert RevenueCat to your config (default)

Sandbox-first (optional). If you configure a sandbox RevenueCat project on the integration, the gated apply runs against the sandbox first plus a synthetic purchase→entitlement-resolve check, and only proceeds to production if it passes.

Drift

amba monetization drift

Shows only the Amba-managed objects whose live RevenueCat state has changed since you last adopted them.

The control plane is deliberately leaky: the RevenueCat dashboard stays fully usable, and out-of-band edits are never blocked or silently reverted — they're observed (every plan plus a daily background sweep records them as drift) and you decide per change whether to keep it (--reconcile adopt) or converge back to your declared config (--reconcile amba, the default).

Export

amba monetization export --file monetization.json

Writes the live config to a declarative bundle. Omit --file to print to stdout.

Adopt

amba monetization adopt --all

Records the live config as your managed baseline. After adopting, the next amba monetization plan is a clean no-op, and any later dashboard edit shows up as drift.

Declarative authoring with promotion bundles

The monetization section is part of the promotion bundle, so you can author your subscription config once and promote it between projects with amba export / amba apply. Importing writes Amba's declared definitions only — it doesn't touch RevenueCat, and it leaves the adopted baseline empty so the next plan shows your imported config as pending create ops until you adopt.

For agents (MCP)

ToolWhat it does
amba_monetization_planPreview the three-way diff + store-floor + human-floor checklist + drift; returns plan_hash
amba_monetization_applyApply the declared config (additive auto; destructive gated behind confirm; store steps need an explicit confirm)
amba_monetization_driftOut-of-band changes to managed objects (read-only)
amba_monetization_exportSnapshot the live config to a declarative bundle (read-only)
amba_monetization_definitions_listList the declared definitions (read-only)
amba_monetization_adoptRecord the live config as the managed baseline (writes Amba state only)

On this page