Flightlinev0.7.0

Docs/Reference/state.yaml schemaview as markdown

Flightline AppState: YAML Reference

TL;DR

A Flightline state file is a YAML document that declares the desired configuration for one app across every App Store Connect surface Flightline manages. You run flightline fetch <bundleId> once to capture live state, edit the YAML, then use flightline plan and flightline apply to preview and write the diff. The schema is embedded in the binary; your editor autocompletes via the yaml-language-server directive at the top of every fetched file.

Every top-level child of spec is optional. Omitting a section tells Flightline "leave this surface alone." You don't have to manage everything, start with spec.metadata and spec.version, add sections when you need them.

The schema URL is https://flightline.dev/schemas/v1alpha1/state.schema.json. Fields not listed in this document are not part of the v1alpha1 contract; unrecognized keys cause a LoadState error.

This reference covers v1alpha1. The apiVersion constant locks that. If a future release bumps to v1beta1, the diff will be explicit and documented.

See the state-as-code guide for a 5-minute walkthrough.


File anatomy

A minimal state file:

# yaml-language-server: $schema=https://flightline.dev/schemas/v1alpha1/state.schema.json

apiVersion: flightline.dev/v1alpha1
kind: AppState

metadata:
  bundleId: com.example.myapp
  version: "1.2.0"

spec:
  version:
    releaseType: AFTER_APPROVAL

The four top-level keys are required: apiVersion, kind, metadata, spec.

Key Type Required Notes
apiVersion string const yes Must be exactly flightline.dev/v1alpha1
kind string const yes Must be exactly AppState
metadata object yes Bundle identity, see metadata
spec object yes Desired state, all children optional

Editor setup. The # yaml-language-server: $schema=... directive at the top of every flightline fetch output activates autocomplete and inline validation in VS Code, Neovim (with yaml-language-server), and any editor that supports the LSP YAML extension. The schema is hosted at https://flightline.dev/schemas/v1alpha1/state.schema.json and also embedded in the flightline binary.


metadata

Identifies the app and the version being described. These three fields are also the keys Flightline uses when resolving the live ASC resource to diff against.

metadata:
  bundleId: app.tideterm.ios
  version: "1.0.1"
  platform: IOS
Field Type Required Default Constraint Gotcha
bundleId string yes , ^[a-zA-Z0-9._-]+$, min 3 chars Must already exist in ASC. Flightline does not create apps.
version string yes , ^[0-9]+(\.[0-9]+)*$ Marketing version (CFBundleShortVersionString). Leading zeros rejected, 01.0 fails schema. Quote it: "1.0".
platform enum no IOS IOS, MAC_OS, TV_OS, VISION_OS Defaults to IOS when absent. Must match the platform your build targets.

Authoring loop. metadata.bundleId + metadata.version are the identity keys Flightline uses at every step: fetch → lint → plan → apply → preflight.


spec.version

Controls how and when the version releases once approved by Apple Review.

spec:
  version:
    releaseType: AFTER_APPROVAL
    copyright: "© 2026 Tideterm Labs"
Field Type Required Default Constraint Gotcha
releaseType enum no , MANUAL, AFTER_APPROVAL, SCHEDULED If absent, Flightline leaves the current ASC setting unchanged. SCHEDULED requires earliestReleaseDate.
earliestReleaseDate string no , ISO 8601 datetime Required when releaseType=SCHEDULED. Apple rejects times in the past.
copyright string no , maxLength 100 Displayed on the App Store listing. Apple shows this under the app name.
downloadable boolean no , , Rarely needed; controls whether the build is downloadable from TestFlight or the App Store.

Release timing. Version release type is set before submission. MANUAL gives you a hold button after approval; AFTER_APPROVAL releases automatically; SCHEDULED releases at the specified time.


spec.build

Attaches a build (already uploaded via Xcode / altool) to this version. Flightline looks up the build by version + number and attaches it by its ASC resource ID.

spec:
  build:
    number: "42"
Field Type Required Default Constraint Gotcha
number string no , , CFBundleVersion. Quote it: "42" not 42. Bare integers are YAML numbers, not strings, and Flightline's strict loader rejects the type mismatch.

Builds must reach VALID state in ASC before Flightline can attach them. Upload is done by Xcode, altool, or xcrun notarytool, not by Flightline. If the build isn't processed yet, flightline apply will surface a typed error.


spec.metadata

Per-locale store listing content. Keys under spec.metadata.locales are Apple locale codes (en-US, es-MX, fr-FR, ja, etc.). You can manage any subset of locales, locales not listed here are left alone.

spec:
  metadata:
    locales:
      en-US:
        name: ".PassDMV: California"
        subtitle: "Pass the test, first try"
        description: |
          California DMV practice tests with the latest official questions.
        keywords: "DMV,driver,test,license,California,permit,practice"
        whatsNew: "v1.0.1, bug fixes, faster question loading."
        promotionalText: "Free updates as the DMV question bank evolves."
        marketingUrl: "https://tideterm.app"
        supportUrl: "https://tideterm.app/support"
        privacyPolicyUrl: "https://tideterm.app/privacy"
      es-MX:
        name: ".PassDMV: California"
        subtitle: "Aprueba a la primera"

Locale key format

Locale codes follow the pattern ^[a-z]{2}(-[A-Z]{2})?$, two-letter language code, optionally followed by a dash and two-letter region code. Examples: en-US, fr-FR, ja, zh-Hans, pt-BR.

Per-locale fields

Field Type Required maxLength Gotcha
name string baseline 30 App name on the store listing. Apple enforces 30 chars; plan/apply will reject strings over this limit before hitting the wire. Applies to appInfoLocalization.
subtitle string no 30 One-line subtitle beneath the app name. Applies to appInfoLocalization.
description string baseline 4000 Long body text on the listing page. Applies to appStoreVersionLocalization.
keywords string no 100 Comma-separated, no spaces around commas. Goes to appStoreVersionLocalization. Not shown to users but affects search indexing.
whatsNew string no 4000 "What's New in this Version" text. Version-scoped: this is for the current metadata.version.
promotionalText string no 170 Promotional text block. Can be updated without resubmission, the only metadata field you can change on a live, approved version. Use it for time-sensitive copy.
marketingUrl URI no , Full URL.
supportUrl URI baseline , Full URL.
privacyPolicyUrl URI no , Full URL.

Cross-resource routing. name and subtitle live on appInfoLocalization; everything else lives on appStoreVersionLocalization. Flightline handles the routing, you author a single locale map and Flightline dispatches to the correct ASC resource per field.

Per-locale completeness. Every locale you declare must be complete enough for Apple to accept. The localizations.completeness rule checks Flightline's submission baseline (name, description, and supportUrl) even when metadata is the only localized surface, and reports locale coverage gaps across other managed surfaces. Other fields remain lifecycle-dependent; check the Apple documentation for Apple's current requirements.


spec.screenshots

Per-locale, per-device-class screenshot sets. Flightline computes an MD5 of each local file and skips slots whose sourceFileChecksum already matches, making repeated applies no-ops when screenshots haven't changed.

spec:
  screenshots:
    locales:
      en-US:
        APP_IPHONE_69:
          - path: ./screenshots/iphone69-1.png
          - path: ./screenshots/iphone69-2.png
        APP_IPHONE_67:
          - path: ./screenshots/iphone67-1.png

Device class keys

Key Device Pixel dimensions
APP_IPHONE_69 iPhone 16 Pro Max (6.9") 1320×2868 or 2868×1320
APP_IPHONE_67 iPhone 14 Plus / 15 Plus (6.7") 1290×2796 or 2796×1290
APP_IPHONE_65 iPhone 11 Pro Max / XS Max (6.5") 1242×2688 or 2688×1242
APP_IPHONE_61 iPhone 11 / XR (6.1") 828×1792 or 1792×828
APP_IPHONE_55 iPhone 8 Plus (5.5") 1242×2208 or 2208×1242
APP_IPAD_PRO_3GEN_129 iPad Pro 12.9" (3rd gen+) 2048×2732 or 2732×2048
APP_IPAD_PRO_3GEN_11 iPad Pro 11" (1st gen+) 1668×2388 or 2388×1668
APP_APPLE_TV Apple TV 1920×1080 or 3840×2160
APP_APPLE_WATCH Apple Watch varies by generation
APP_APPLE_VISION_PRO Apple Vision Pro 2732×2048

screenshotFile fields

Field Type Required Notes
path string yes Relative paths resolve against the directory containing the state file. PNG or JPG.
alt string no Reserved. Apple does not currently surface alt text on the store listing.

Each device slot accepts 1 to 10 screenshots (minItems: 1, maxItems: 10).

Required devices for new submissions. Apple requires at least one supported large-iPhone screenshot tier for new iOS app submissions. The L3 preflight rule screenshots.requiredDevices catches missing coverage offline.

flightline apply resolves these paths relative to the state file, compares local MD5 checksums with Apple's live checksums, uploads changed files, and deletes live files omitted from the managed device set. Use --resume to continue an interrupted multipart upload. The dedicated flightline screenshots upload command remains available for one-off uploads outside state management.


spec.iap

In-app purchases keyed by productId. This section covers consumable, non-consumable, and non-renewing IAPs. Auto-renewable subscriptions live under spec.testflight (the subscription group surface), they are not represented here.

spec:
  iap:
    products:
      app.tideterm.ios.lifetime:
        type: NON_CONSUMABLE
        name: "Lifetime Access"
        familySharable: false
        contentHosting: NON_HOSTED
        reviewNote: |
          Tap "Unlock Full Access" on the home screen.
        reviewScreenshot:
          path: ./review-screenshots/lifetime.png
        localizations:
          en-US:
            name: "Lifetime Access"
            description: "Unlock all DMV practice tests forever."

iapProduct fields

Field Type Required Constraint Gotcha
type enum yes CONSUMABLE, NON_CONSUMABLE, NON_RENEWING_SUBSCRIPTION AUTO_RENEWABLE_SUBSCRIPTION is not valid here, auto-renewing subs live in ASC's subscription group surface.
name string no , Apple-facing reference name in ASC. Not shown to customers.
familySharable boolean no , Whether Family Sharing is enabled for this IAP.
contentHosting enum no HOSTED, NON_HOSTED Hosted: Apple hosts the downloadable content. Most IAPs are NON_HOSTED.
reviewNote string no maxLength 4000 Instructions for the App Review team to exercise this IAP. Important for IAPs with non-obvious unlock paths.
reviewScreenshot object no , Path to a screenshot showing the IAP unlock screen. See note below.
localizations map no , Locale-keyed {name, description} pairs. See below.

reviewScreenshot

reviewScreenshot:
  path: ./review-screenshots/lifetime.png
Field Type Required Gotcha
path string yes Relative to state file directory. The L3 preflight rule iap.reviewScreenshot.exists checks this is populated. Missing review screenshots are a common rejection cause.

Current limitation. Like app screenshots, the reviewScreenshot binary upload is not driven by flightline apply. Use flightline iap review-screenshot upload <bundleId> --product <productId> --file <path> directly.

iapLocalization fields

Field Type Required maxLength Gotcha
name string no 30 Customer-visible IAP name.
description string no 45 Customer-visible IAP description. Note the 45-char ceiling, much shorter than app metadata.

spec.ageRating

Apple's age-rating questionnaire. Each field answers one content-category question. All fields are optional individually, answering only the categories relevant to your app is fine. However, Apple requires the questionnaire to be complete before you can submit; the L3 preflight rule version.ageRating.answered verifies this.

spec:
  ageRating:
    cartoonOrFantasyViolence: NONE
    realisticViolence: NONE
    profanityOrCrudeHumor: NONE
    matureSuggestiveThemes: NONE
    horrorOrFearThemes: NONE
    medicalOrTreatmentInformation: NONE
    alcoholTobaccoOrDrugUseOrReferences: NONE
    contestsAndGambling: NONE
    sexualContentOrNudity: NONE
    sexualContentGraphicAndNudity: NONE
    gambling: false
    unrestrictedWebAccess: false

Frequency fields (enum)

These fields accept NONE, INFREQUENT_OR_MILD, or FREQUENT_OR_INTENSE:

Field Flightline name Apple API field
cartoonOrFantasyViolence cartoon or fantasy violence violenceCartoonOrFantasy
realisticViolence realistic violence violenceRealistic
profanityOrCrudeHumor profanity or crude humor profanityOrCrudeHumor
matureSuggestiveThemes mature or suggestive themes matureOrSuggestiveThemes
horrorOrFearThemes horror or fear themes horrorOrFearThemes
medicalOrTreatmentInformation medical or treatment information medicalOrTreatmentInformation
alcoholTobaccoOrDrugUseOrReferences alcohol, tobacco, or drug use alcoholTobaccoOrDrugUseOrReferences
contestsAndGambling contests and gambling contests (Apple wire name)
sexualContentOrNudity sexual content or nudity sexualContentOrNudity
sexualContentGraphicAndNudity graphic sexual content and nudity sexualContentGraphicAndNudity

Boolean fields

Field Type Gotcha
prolongedGraphicSadisticRealisticViolence boolean Apple's API uses a frequency enum for this field; Flightline maps any non-NONE value fetched from Apple to true. Set true for apps with prolonged, graphic, sadistic violence.
gambling boolean Gambling features (not just references, actual gambling mechanics).
unrestrictedWebAccess boolean App provides unrestricted internet access (e.g., a web browser).
kidsAgeBand enum or null FIVE_AND_UNDER, SIX_TO_EIGHT, NINE_TO_ELEVEN, or null. Set only for Kids category apps.
seventeenPlus boolean Read-only. Apple derives the 17+ rating from your answers, you cannot set this field directly. flightline apply returns a typed error if you include this in a change set. You may include it in the file for documentation, but it has no write effect.

spec.exportCompliance

Per-build encryption declaration. Every build submitted to Apple must have export compliance answered. Most apps that only use system TLS set usesNonExemptEncryption: false and are done.

spec:
  exportCompliance:
    usesNonExemptEncryption: false
Field Type Required Gotcha
usesNonExemptEncryption boolean no false for apps that use only standard system TLS/HTTPS. true triggers the ECCN classification block below.
declaration object no Required only when usesNonExemptEncryption: true and the app needs full ECCN classification (rare).

declaration (ECCN classification)

Only populate this if your app implements proprietary cryptographic algorithms beyond TLS.

Field Type Gotcha
containsProprietaryCryptography boolean Your own crypto implementation.
containsThirdPartyCryptography boolean Third-party crypto beyond the OS.
availableOnFrenchStore boolean French regulatory requirement.
usesEncryption boolean Any encryption usage.
exempt boolean Meets an EAR exemption.
eccn string ECCN classification string (e.g. 5D002).
documentName string Name of the encryption documentation.
documentUrl URI URL to the documentation.

spec.reviewerDemo

Login credentials for App Review. Apple requires demo credentials for any app that has a sign-in wall. Flightline never logs or echoes passwords.

spec:
  reviewerDemo:
    username: "demo@tideterm.app"
    passwordRef: env:PASSDMV_DEMO_PASSWORD
    notes: |
      Tap any practice test to start. The IAP unlock screen appears
      after the second free question.
    contactName: "Joel Nuts"
    contactEmail: "devteam@corelift.io"
    contactPhone: "+1-555-0100"
Field Type Required Constraint Gotcha
username string no , Demo account username.
passwordRef string no ^env:[A-Z_][A-Z0-9_]*$ Env var reference, e.g. env:DEMO_PASSWORD. Flightline resolves the variable at apply time. Never put the password directly in the YAML file, it will end up in git.
passwordFile string no , Path to a file containing the password. Trailing newline is trimmed. Alternative to passwordRef.
notes string no maxLength 4000 Instructions for the reviewer. Include where to find any non-obvious flows (IAP, restricted features, login-wall bypass).
contactName string no , Your name or the developer's name.
contactEmail string no email format Contact for Review team questions.
contactPhone string no , International format recommended.

Password field constraint. Exactly one of passwordRef or passwordFile may be present, or neither. Both together is a schema validation error (oneOf). If neither is provided, Flightline applies the other reviewer-demo fields without a password update.


spec.categories

App Store category assignment. Category IDs correspond to Apple's category taxonomy, use flightline categories list to enumerate valid IDs.

spec:
  categories:
    primary: EDUCATION
    secondary: REFERENCE
    primarySubcategories:
      - EDUCATION_TUTORIALS
Field Type Required Constraint Gotcha
primary string no , Primary category ID (e.g. BUSINESS, GAMES, EDUCATION). See /v1/appCategories in the ASC API.
secondary string no , Secondary category ID.
primarySubcategories array of string no maxItems 2 Subcategory IDs under primary. Only valid for categories that have subcategories (notably GAMES).
secondarySubcategories array of string no maxItems 2 Subcategory IDs under secondary.

spec.pricing

Single base-territory price point. Per-territory overrides are a future L2 extension.

spec:
  pricing:
    baseTerritory: USA
    appPricePointId: "FREE"
Field Type Required Gotcha
baseTerritory string no ISO 3166-1 alpha-3 territory code (e.g. USA, GBR, JPN, AUS). Not alpha-2.
appPricePointId string no Apple's appPricePoint resource ID. Use flightline price-points list to enumerate. "FREE" is the free tier. Quote the value, bare FREE is not a YAML string.

spec.testflight

TestFlight beta distribution configuration. Only groups listed here are managed by Flightline. Groups absent from this section are left alone.

spec:
  testflight:
    groups:
      friends-and-family:
        isInternal: false
        publicLink: false
        testers:
          - email: tester1@example.com
            firstName: Test
            lastName: One
      internal-team:
        isInternal: true

Group key format

Group keys match ^[A-Za-z0-9 _-]+$. They are the human-readable names you assign, Flightline resolves the ASC resource ID by matching the name against the live group list.

testflightGroup fields

Field Type Required Constraint Gotcha
isInternal boolean no , Internal groups are your App Store Connect team members. External groups are outside testers.
publicLink boolean no , Enables Apple's public invite link for this group. Valid only for external groups.
publicLinkLimit integer no 1 to 10000 Maximum testers via the public link. Requires publicLink: true.
testers array no , Explicit tester list. Each entry requires at least email.

testflightTester fields

Field Type Required
email string (email format) yes
firstName string no
lastName string no

spec.customProductPages

Custom Product Pages (CPPs), alternate store listings with different screenshots and promotional text, used for ad-driven traffic. Keys are page identifiers (slugs).

spec:
  customProductPages:
    summer-discovery-2026:
      visible: true
      localizations:
        en-US:
          promotionalText: "Pass your DMV test before the road trip."
          screenshots:
            APP_IPHONE_69:
              - path: ./screenshots/cpp-summer-iphone69-1.png

customProductPage fields

Field Type Required Gotcha
visible boolean no Controls whether the CPP is visible/active.
localizations map no Locale-keyed content.

CPP localization fields

Field Type Required maxLength Gotcha
promotionalText string no 170 CPP-specific promotional text, overriding the main listing.
screenshots map no , Device-class screenshot sets, same format as spec.screenshots.

Device classes for CPPs. CPPs support a subset of device classes: APP_IPHONE_67, APP_IPHONE_69, APP_IPHONE_65, APP_IPHONE_61, APP_IPHONE_55, APP_IPAD_PRO_3GEN_129, APP_IPAD_PRO_3GEN_11. TV, Watch, and Vision Pro device classes are not supported on CPPs.

Current limitation. CPP screenshot binary uploads are not driven by flightline apply, the same as main screenshots. Use the dedicated Custom Product Page screenshot upload command directly.


What Flightline does NOT manage

Privacy nutrition labels

spec.privacyLabels does not exist in the v1alpha1 schema and is not planned for v1. Apple's App Store Connect API v4.3 does not expose the appPrivacyDetails resource, there are no read or write endpoints for privacy nutrition labels in the public API.

Flightline ships a flightline privacy-labels get <bundleId> stub that returns a typed diagnostic explaining the gap. The JSON contract has supported: false and a pointer to the portal.

Manage privacy labels in the App Store Connect web UI.

Asset upload paths (screenshots, IAP review screenshots, CPP screenshots)

The flightline apply orchestrator does not yet drive multipart binary uploads. The sections for spec.screenshots, spec.iap.products[*].reviewScreenshot, and spec.customProductPages.<name>.localizations.<locale>.screenshots are fully supported in flightline fetch and flightline plan (the diff engine compares checksums), but flightline apply returns a typed error for these change paths and directs you to the L1 upload verbs:

flightline screenshots upload <bundleId> --version <v> --locale <l> --device-set <d> <path>
flightline iap review-screenshot upload <bundleId> --product <productId> --file <path>

Asset uploads remain explicit L1 operations and are not dispatched by flightline apply.


Common gotchas

YAML coercion

Flightline uses yaml.v3 with KnownFields(true) and strict type matching. These are the most common coercion traps:

Problem Bad Good
Boolean string yes, no, on, off, true, false unquoted in a string field Quote strings: "false"
Bare boolean in an enum field releaseType: true releaseType: AFTER_APPROVAL
Integer version version: 1.0 (YAML float) version: "1.0" (quoted string)
Integer build number number: 42 (YAML int) number: "42" (quoted string)
Leading zeros number: "042" fails the pattern ^[0-9]+(.[0-9]+)*$ in version Use "42" not "042"
Scientific notation 1e3 parsed as float in some YAML parsers Always quote version and build number strings

The strict loader surfaces these as file:line:col diagnostics before schema validation runs, so you see the specific location.

Per-locale completeness

Every locale you declare in spec.metadata.locales is managed. Fill the baseline name, description, and supportUrl fields for each locale or omit that locale entirely. Flightline's localizations.completeness rule checks those fields and reports locale coverage gaps across the managed localization surfaces.

Cross-resource field routing

name and subtitle live on appInfoLocalization in the ASC API; description, keywords, whatsNew, promotionalText, marketingUrl, supportUrl, and privacyPolicyUrl live on appStoreVersionLocalization. Flightline handles the dispatch, you do not need to know which resource owns which field. But if you see a diff that looks like a no-op update, check whether you have the same locale in two separate ASC resource states.

seventeenPlus is read-only

The seventeenPlus boolean in spec.ageRating reflects Apple's computed rating from your questionnaire answers. You cannot set it directly, Flightline returns a typed error if this field appears in a change set. You may include it in the YAML as documentation of the current state (as written by flightline fetch), but changes to it are ignored with an error, not silently applied.

contestsAndGambling maps to Apple's "contests" field

The schema uses contestsAndGambling for the frequency question about contests and gambling features. On Apple's wire API, this field is called contests. Flightline translates in both directions; you always use contestsAndGambling in your YAML.

Omitted spec sections are not managed

If you omit spec.screenshots entirely, Flightline will not touch your screenshots, not delete them, not diff them, nothing. This is intentional: partial state files let you manage only the surfaces you care about. If you want Flightline to own a surface, fetch the full state first (flightline fetch), then edit.

Version must be in editable state

flightline plan and flightline apply require the version identified by metadata.version to be in an editable state in ASC (e.g., PREPARE_FOR_SUBMISSION, DEVELOPER_REJECTED). If the version is READY_FOR_SALE or under review, writes will fail with a 422 from Apple's API.


See also

  • State-as-code guide, 5-minute fetch → edit → plan → apply walkthrough
  • Schema source, the JSON Schema 2020-12 contract
  • Preflight in CI, run live release checks as a deployment gate
  • flightline --help, flightline fetch --help, flightline plan --help, flightline apply --help