[GH-ISSUE #648] Feature: Import subscription pricing from CSV (bulk territory custom prices) #183

Closed
opened 2026-02-26 21:33:55 +03:00 by kerem · 0 comments
Owner

Originally created by @rudrankriyam on GitHub (Feb 18, 2026).
Original GitHub issue: https://github.com/rudrankriyam/App-Store-Connect-CLI/issues/648

Originally assigned to: @rudrankriyam on GitHub.

Problem

Setting per-territory subscription prices is currently a manual loop:

  • look up a subscriptionPricePoint per territory
  • run asc subscriptions prices add once per territory

This makes it hard to apply pricing from a spreadsheet (or from App Store Connect's downloaded Starting Subscription Price.csv) and is a big time sink for large territory sets.

A related pain point: when standard subscription pricing changes, teams often need to update introductory offer pricing in parallel (follow-up section below).

Proposal

Add a bulk import command:

asc subscriptions prices import --id "SUB_ID" --input "./prices.csv" [flags]

Suggested flags:

  • --start-date "YYYY-MM-DD" (optional; applied to rows that don't set start_date)
  • --preserved (optional; sets preserveCurrentPrice=true on created subscriptionPrices)
  • --dry-run (validate + resolve price points; no POSTs)
  • --continue-on-error (default true; consistent with testflight beta-testers import)

Behavior:

  1. Parse CSV (header required)
  2. For each row, resolve the target subscriptionPricePoint:
    • If price_point_id is provided, use it directly
    • Otherwise, fetch price points for the row's territory via:
      • GET /v1/subscriptions/{id}/pricePoints?filter[territory]=<TERRITORY>
      • match attributes.customerPrice to the row's price
  3. Create subscription prices via POST /v1/subscriptionPrices (one per row)
  4. Print a machine-readable summary (JSON default) with per-row failures

Note: This will never be truly atomic because it's many API calls; summary output should make partial application explicit.

Standard CSV import format

CSV is UTF-8, comma-separated, with a header row.

Required columns:

  • territory (App Store Connect territory ID, ISO-3166 alpha-3, e.g. USA, IND, DEU)
  • price (customer price amount as shown in ASC for that territory, e.g. 19.95)

Optional columns:

  • currency_code (ISO 4217; validation only)
  • start_date (YYYY-MM-DD)
  • preserved (true|false) (alias for preserve_current_price)
  • preserve_current_price (true|false)
  • price_point_id (bypass lookup and use directly)

Unknown/extra columns should be ignored (so App Store Connect exports with proceeds columns can be ingested).

territory,currency_code,price,start_date,preserved
USA,USD,19.99,2026-03-01,false
DEU,EUR,19.99,2026-03-01,false
IND,INR,299.00,2026-03-01,true

Compatibility: App Store Connect export

App Store Connect exports typically use headers like:

  • Countries or Regions
  • Currency Code
  • Price
  • plus additional proceeds columns

We should accept these header aliases:

  • countries_or_regions -> territory
  • currency_code (same)

And for Countries or Regions values:

  • if the value already looks like a territory ID (USA), use it
  • otherwise attempt to map common country/region names (e.g. Afghanistan) to the ASC territory ID
  • if a value can't be mapped unambiguously, error with row number and the raw value

Introductory offer pricing (follow-up)

The OpenAPI snapshot shows PATCH /v1/subscriptionIntroductoryOffers/{id} can update endDate only; pricing relationships are not patchable.

So "update intro offer pricing" likely means ending/deleting + recreating offers.

Follow-up feature:

  • a dedicated command to bulk-create (and optionally replace) per-territory introductory offers from CSV, reusing the same territory/price resolution logic.

Acceptance criteria

  • Valid CSV creates prices for all rows (or reports per-row failures)
  • Missing required columns, duplicate known columns, invalid dates, invalid booleans -> usage error (exit code 2)
  • Unknown territory, unmappable territory name, or price not found in price points -> failure includes row number
  • --dry-run does all lookups/validation and prints summary without mutating
  • Tests added with internal/cli/cmdtest + httptest covering:
    • CSV parsing + header aliasing
    • territory name mapping errors
    • price point resolution
    • dry-run behavior
    • partial failure reporting
Originally created by @rudrankriyam on GitHub (Feb 18, 2026). Original GitHub issue: https://github.com/rudrankriyam/App-Store-Connect-CLI/issues/648 Originally assigned to: @rudrankriyam on GitHub. ## Problem Setting per-territory subscription prices is currently a manual loop: - look up a `subscriptionPricePoint` per territory - run `asc subscriptions prices add` once per territory This makes it hard to apply pricing from a spreadsheet (or from App Store Connect's downloaded `Starting Subscription Price.csv`) and is a big time sink for large territory sets. A related pain point: when standard subscription pricing changes, teams often need to update introductory offer pricing in parallel (follow-up section below). ## Proposal Add a bulk import command: ```bash asc subscriptions prices import --id "SUB_ID" --input "./prices.csv" [flags] ``` Suggested flags: - `--start-date "YYYY-MM-DD"` (optional; applied to rows that don't set `start_date`) - `--preserved` (optional; sets `preserveCurrentPrice=true` on created `subscriptionPrices`) - `--dry-run` (validate + resolve price points; no POSTs) - `--continue-on-error` (default true; consistent with `testflight beta-testers import`) Behavior: 1. Parse CSV (header required) 2. For each row, resolve the target `subscriptionPricePoint`: - If `price_point_id` is provided, use it directly - Otherwise, fetch price points for the row's territory via: - `GET /v1/subscriptions/{id}/pricePoints?filter[territory]=<TERRITORY>` - match `attributes.customerPrice` to the row's `price` 3. Create subscription prices via `POST /v1/subscriptionPrices` (one per row) 4. Print a machine-readable summary (JSON default) with per-row failures Note: This will never be truly atomic because it's many API calls; summary output should make partial application explicit. ## Standard CSV import format CSV is UTF-8, comma-separated, with a header row. Required columns: - `territory` (App Store Connect territory ID, ISO-3166 alpha-3, e.g. `USA`, `IND`, `DEU`) - `price` (customer price amount as shown in ASC for that territory, e.g. `19.95`) Optional columns: - `currency_code` (ISO 4217; validation only) - `start_date` (`YYYY-MM-DD`) - `preserved` (`true|false`) (alias for `preserve_current_price`) - `preserve_current_price` (`true|false`) - `price_point_id` (bypass lookup and use directly) Unknown/extra columns should be ignored (so App Store Connect exports with proceeds columns can be ingested). ### Example (recommended) ```csv territory,currency_code,price,start_date,preserved USA,USD,19.99,2026-03-01,false DEU,EUR,19.99,2026-03-01,false IND,INR,299.00,2026-03-01,true ``` ### Compatibility: App Store Connect export App Store Connect exports typically use headers like: - `Countries or Regions` - `Currency Code` - `Price` - plus additional proceeds columns We should accept these header aliases: - `countries_or_regions` -> `territory` - `currency_code` (same) And for `Countries or Regions` values: - if the value already looks like a territory ID (`USA`), use it - otherwise attempt to map common country/region names (e.g. `Afghanistan`) to the ASC territory ID - if a value can't be mapped unambiguously, error with row number and the raw value ## Introductory offer pricing (follow-up) The OpenAPI snapshot shows `PATCH /v1/subscriptionIntroductoryOffers/{id}` can update `endDate` only; pricing relationships are not patchable. So "update intro offer pricing" likely means ending/deleting + recreating offers. Follow-up feature: - a dedicated command to bulk-create (and optionally replace) per-territory introductory offers from CSV, reusing the same territory/price resolution logic. ## Acceptance criteria - Valid CSV creates prices for all rows (or reports per-row failures) - Missing required columns, duplicate known columns, invalid dates, invalid booleans -> usage error (exit code 2) - Unknown territory, unmappable territory name, or price not found in price points -> failure includes row number - `--dry-run` does all lookups/validation and prints summary without mutating - Tests added with `internal/cli/cmdtest` + `httptest` covering: - CSV parsing + header aliasing - territory name mapping errors - price point resolution - dry-run behavior - partial failure reporting
kerem 2026-02-26 21:33:55 +03:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/App-Store-Connect-CLI#183
No description provided.