diff --git a/bun.lock b/bun.lock
index a379f8c0d..a5f2c9a5c 100644
--- a/bun.lock
+++ b/bun.lock
@@ -145,7 +145,7 @@
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^15.15.0",
- "jsdom": "^30",
+ "jsdom": "29.1.1",
"lint-staged": "^16.1.5",
"npm-run-all": "^4.1.5",
"postcss": "~8.5.26",
@@ -213,9 +213,13 @@
"@ardatan/relay-compiler": ["@ardatan/relay-compiler@13.0.2", "", { "dependencies": { "@babel/runtime": "^8.0.0", "immutable": "^5.1.9", "invariant": "^2.2.4" }, "peerDependencies": { "graphql": "*" } }, "sha512-VFpv9UP820SiwDUPYtq7PmD3jifzZlevkQ26bhbSzFeruSTys0eHzQCZyKg+IhgmZzwPI9AFjPe26ABNjGeIKg=="],
- "@asamuzakjp/css-color": ["@asamuzakjp/css-color@6.0.7", "", { "dependencies": { "@csstools/css-calc": "^3.3.0", "@csstools/css-color-parser": "^4.1.10", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "lru-cache": "^11.5.2" } }, "sha512-vC/bk1Lz7Tn/EfU9/apOTBk80/8dyGyWMowPoV1tJ52muDGsDqt2HPT2klrFUiY60MQmQv9q8yIht15JnBgDGw=="],
+ "@asamuzakjp/css-color": ["@asamuzakjp/css-color@5.1.11", "", { "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", "@csstools/css-calc": "^3.2.0", "@csstools/css-color-parser": "^4.1.0", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg=="],
- "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@8.3.2", "", { "dependencies": { "bidi-js": "^1.0.3", "css-tree": "^3.2.1", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.5.2" } }, "sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q=="],
+ "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@7.1.1", "", { "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.2.1", "is-potential-custom-element-name": "^1.0.1" } }, "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ=="],
+
+ "@asamuzakjp/generational-cache": ["@asamuzakjp/generational-cache@1.0.1", "", {}, "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg=="],
+
+ "@asamuzakjp/nwsapi": ["@asamuzakjp/nwsapi@2.3.9", "", {}, "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q=="],
"@auth/core": ["@auth/core@0.41.3", "", { "dependencies": { "@panva/hkdf": "^1.2.1", "jose": "^6.0.6", "oauth4webapi": "^3.3.0", "preact": "10.24.3", "preact-render-to-string": "6.5.11" }, "peerDependencies": { "@simplewebauthn/browser": "^9.0.1", "@simplewebauthn/server": "^9.0.2", "nodemailer": "^7.0.7 || ^8.0.5" }, "optionalPeers": ["@simplewebauthn/browser", "@simplewebauthn/server", "nodemailer"] }, "sha512-sJ3JMHHkXMD3aOjopv7mOBTO1Ocw4b0fAEXJBz6k7YHLpYQI6C40jCUPc5fNvUKxXRXNE1/sRISA15UrwWJBTw=="],
@@ -1667,7 +1671,7 @@
"js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="],
- "jsdom": ["jsdom@30.0.1", "", { "dependencies": { "@asamuzakjp/css-color": "^6.0.5", "@asamuzakjp/dom-selector": "^8.3.0", "@bramus/specificity": "^2.4.2", "@csstools/css-syntax-patches-for-csstree": "^1.1.7", "@exodus/bytes": "^1.15.1", "css-tree": "^3.2.1", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.5.2", "parse5": "^8.0.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.2", "undici": "^8.9.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", "whatwg-url": "^17.1.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.2.3" }, "optionalPeers": ["canvas"] }, "sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA=="],
+ "jsdom": ["jsdom@29.1.1", "", { "dependencies": { "@asamuzakjp/css-color": "^5.1.11", "@asamuzakjp/dom-selector": "^7.1.1", "@bramus/specificity": "^2.4.2", "@csstools/css-syntax-patches-for-csstree": "^1.1.3", "@exodus/bytes": "^1.15.0", "css-tree": "^3.2.1", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.3.5", "parse5": "^8.0.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.1", "undici": "^7.25.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", "whatwg-url": "^16.0.1", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q=="],
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
@@ -2361,7 +2365,7 @@
"unc-path-regex": ["unc-path-regex@0.1.2", "", {}, "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg=="],
- "undici": ["undici@8.10.0", "", {}, "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ=="],
+ "undici": ["undici@7.29.0", "", {}, "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw=="],
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
@@ -2419,7 +2423,7 @@
"whatwg-mimetype": ["whatwg-mimetype@5.0.0", "", {}, "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw=="],
- "whatwg-url": ["whatwg-url@17.1.0", "", { "dependencies": { "@exodus/bytes": "^1.15.1", "tr46": "^6.0.0", "webidl-conversions": "^8.0.1" } }, "sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw=="],
+ "whatwg-url": ["whatwg-url@16.0.1", "", { "dependencies": { "@exodus/bytes": "^1.11.0", "tr46": "^6.0.0", "webidl-conversions": "^8.0.1" } }, "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
@@ -2585,8 +2589,6 @@
"convex/esbuild": ["esbuild@0.27.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.0", "@esbuild/android-arm": "0.27.0", "@esbuild/android-arm64": "0.27.0", "@esbuild/android-x64": "0.27.0", "@esbuild/darwin-arm64": "0.27.0", "@esbuild/darwin-x64": "0.27.0", "@esbuild/freebsd-arm64": "0.27.0", "@esbuild/freebsd-x64": "0.27.0", "@esbuild/linux-arm": "0.27.0", "@esbuild/linux-arm64": "0.27.0", "@esbuild/linux-ia32": "0.27.0", "@esbuild/linux-loong64": "0.27.0", "@esbuild/linux-mips64el": "0.27.0", "@esbuild/linux-ppc64": "0.27.0", "@esbuild/linux-riscv64": "0.27.0", "@esbuild/linux-s390x": "0.27.0", "@esbuild/linux-x64": "0.27.0", "@esbuild/netbsd-arm64": "0.27.0", "@esbuild/netbsd-x64": "0.27.0", "@esbuild/openbsd-arm64": "0.27.0", "@esbuild/openbsd-x64": "0.27.0", "@esbuild/openharmony-arm64": "0.27.0", "@esbuild/sunos-x64": "0.27.0", "@esbuild/win32-arm64": "0.27.0", "@esbuild/win32-ia32": "0.27.0", "@esbuild/win32-x64": "0.27.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA=="],
- "data-urls/whatwg-url": ["whatwg-url@16.0.1", "", { "dependencies": { "@exodus/bytes": "^1.11.0", "tr46": "^6.0.0", "webidl-conversions": "^8.0.1" } }, "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw=="],
-
"dir-glob/path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="],
"dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
diff --git a/gql/.vscode/settings.json b/gql/.vscode/settings.json
deleted file mode 100644
index c1659144e..000000000
--- a/gql/.vscode/settings.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "cSpell.words": [
- "apollographql",
- "codegen",
- "gradlew",
- "openiap",
- "preorder",
- "pubspec",
- "skus"
- ]
-}
\ No newline at end of file
diff --git a/knowledge/_claude-context/context.md b/knowledge/_claude-context/context.md
index cd1f62154..303dfcc5a 100644
--- a/knowledge/_claude-context/context.md
+++ b/knowledge/_claude-context/context.md
@@ -1,7 +1,7 @@
# OpenIAP Project Context
> **Auto-generated for Claude Code**
-> Last updated: 2026-08-13T01:31:14.251Z
+> Last updated: 2026-08-18T16:58:40.514Z
>
> Usage: `claude --context knowledge/_claude-context/context.md`
@@ -287,11 +287,12 @@ const IsSubscription: boolean; // No PascalCase for variables
```
openiap/
├── packages/
+│ ├── conformance/ # Behavioral conformance spec, runner, and reports
│ ├── docs/ # Documentation (React/Vite/Vercel)
│ ├── gql/ # GraphQL schema & type generation
│ ├── google/ # Android library (Kotlin)
│ ├── apple/ # iOS/macOS library (Swift)
-│ ├── kit/ # Hosted receipt-validation SaaS (Fly.io app)
+│ ├── kit/ # Purchase validation + entitlement infrastructure (Fly.io app)
│ └── mcp-server/ # IAPKit MCP server (hosted at kit.openiap.dev/mcp)
├── plugins/
│ └── openiap/ # Codex + Claude Code plugin (skills + MCP config)
@@ -313,6 +314,31 @@ openiap/
Libraries reference local `packages/apple` and `packages/google` source directly (not published CocoaPods/Maven artifacts), enabling immediate development without waiting for native releases.
+## Directory Ownership Guardrail
+
+Keep each project surface under its canonical owner:
+
+| Content | Canonical location |
+| ------------------------------------------------ | ----------------------- |
+| Deployable packages and native implementations | `packages//` |
+| Framework SDKs | `libraries//` |
+| Agent integrations distributed to users | `plugins//` |
+| Behavioral conformance spec, runner, and reports | `packages/conformance/` |
+| Repository knowledge | `knowledge/` |
+| Repository-wide automation | `scripts/` |
+| Shared editor settings | `.vscode/` |
+
+- Never create a root directory that duplicates a child of `packages/`,
+ `libraries/`, or `plugins/`. For example, use `packages/docs/` and
+ `packages/gql/`, never root `docs/` or `gql/`.
+- Before adding a top-level directory, search for an existing owner and extend
+ it. Add a new root only when no canonical owner fits, and document that owner
+ in this section in the same change.
+- Keep shared editor settings in root `.vscode/`. Package-specific settings are
+ allowed only when they apply exclusively to that package's toolchain.
+- Run `bun run audit:layout` after directory changes. Pre-commit and CI enforce
+ the same audit; do not weaken it to permit a duplicate owner.
+
## Package Responsibilities
### packages/gql
diff --git a/knowledge/internal/02-architecture.md b/knowledge/internal/02-architecture.md
index af4ca15e3..06e597bc0 100644
--- a/knowledge/internal/02-architecture.md
+++ b/knowledge/internal/02-architecture.md
@@ -8,11 +8,12 @@
```
openiap/
├── packages/
+│ ├── conformance/ # Behavioral conformance spec, runner, and reports
│ ├── docs/ # Documentation (React/Vite/Vercel)
│ ├── gql/ # GraphQL schema & type generation
│ ├── google/ # Android library (Kotlin)
│ ├── apple/ # iOS/macOS library (Swift)
-│ ├── kit/ # Hosted receipt-validation SaaS (Fly.io app)
+│ ├── kit/ # Purchase validation + entitlement infrastructure (Fly.io app)
│ └── mcp-server/ # IAPKit MCP server (hosted at kit.openiap.dev/mcp)
├── plugins/
│ └── openiap/ # Codex + Claude Code plugin (skills + MCP config)
@@ -34,6 +35,31 @@ openiap/
Libraries reference local `packages/apple` and `packages/google` source directly (not published CocoaPods/Maven artifacts), enabling immediate development without waiting for native releases.
+## Directory Ownership Guardrail
+
+Keep each project surface under its canonical owner:
+
+| Content | Canonical location |
+| ------------------------------------------------ | ----------------------- |
+| Deployable packages and native implementations | `packages//` |
+| Framework SDKs | `libraries//` |
+| Agent integrations distributed to users | `plugins//` |
+| Behavioral conformance spec, runner, and reports | `packages/conformance/` |
+| Repository knowledge | `knowledge/` |
+| Repository-wide automation | `scripts/` |
+| Shared editor settings | `.vscode/` |
+
+- Never create a root directory that duplicates a child of `packages/`,
+ `libraries/`, or `plugins/`. For example, use `packages/docs/` and
+ `packages/gql/`, never root `docs/` or `gql/`.
+- Before adding a top-level directory, search for an existing owner and extend
+ it. Add a new root only when no canonical owner fits, and document that owner
+ in this section in the same change.
+- Keep shared editor settings in root `.vscode/`. Package-specific settings are
+ allowed only when they apply exclusively to that package's toolchain.
+- Run `bun run audit:layout` after directory changes. Pre-commit and CI enforce
+ the same audit; do not weaken it to permit a duplicate owner.
+
## Package Responsibilities
### packages/gql
diff --git a/package.json b/package.json
index 4725662d9..9659db3a6 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"lint": "bun run --filter '*' lint",
"e2e:web": "node scripts/e2e-web-sites.mjs",
"audit:deprecations": "node --test scripts/audit-deprecation-schedule.test.mjs && node scripts/audit-deprecation-schedule.mjs",
+ "audit:layout": "node --test scripts/audit-repo-layout.test.mjs && node scripts/audit-repo-layout.mjs",
"audit:parity": "node scripts/audit-non-godot-parity.mjs",
"audit:kit-contract": "node --test scripts/audit-kit-spec-contract.test.mjs && node scripts/audit-kit-spec-contract.mjs",
"audit:docs": "bun run scripts/audit-docs.ts",
diff --git a/docs/conformance-audit.md b/packages/conformance/CONFORMANCE_AUDIT.md
similarity index 100%
rename from docs/conformance-audit.md
rename to packages/conformance/CONFORMANCE_AUDIT.md
diff --git a/packages/conformance/README.md b/packages/conformance/README.md
index c5d5c9dad..fdc1f7f8e 100644
--- a/packages/conformance/README.md
+++ b/packages/conformance/README.md
@@ -14,6 +14,10 @@ packages/gql/src/capability-matrix.mjs which stores must implement what
packages/conformance/src/spec/ what each behavior must do <- you are here
```
+See the [Conformance Testing Audit](https://github.com/hyodotdev/openiap/blob/main/packages/conformance/CONFORMANCE_AUDIT.md)
+for the repository assessment that led to this suite and the remediation
+history.
+
## Versioning
A report states two versions, and neither is optional:
diff --git a/packages/docs/README.md b/packages/docs/README.md
index 218849d5c..25918f118 100644
--- a/packages/docs/README.md
+++ b/packages/docs/README.md
@@ -20,6 +20,13 @@ Visit [openiap.dev](https://openiap.dev) for full documentation.
+
+
+
+
+
+
+
-Thank you to our sponsors for supporting the OpenIAP initiative. [Become a sponsor](https://openiap.dev/sponsors)
\ No newline at end of file
+Thank you to our sponsors for supporting the OpenIAP initiative. [Become a sponsor](https://openiap.dev/sponsors)
diff --git a/packages/docs/SHOWCASE.md b/packages/docs/SHOWCASE.md
index d4dcbb7a5..0f82352b5 100644
--- a/packages/docs/SHOWCASE.md
+++ b/packages/docs/SHOWCASE.md
@@ -63,7 +63,7 @@ Leave `ratings` and `installs` out of your PR — the script fills them in.
| `logo` | ✅ | Path under `packages/docs/public` (e.g. `/showcase/your-app.webp`) or a full https URL. |
| `ratings` / `installs` | — | Maintainer-managed ordering metrics. Leave these out. |
| `library` | ✅ | One of `expo-iap`, `react-native-iap`, `flutter_inapp_purchase`, `kmp-iap`, `maui-iap`, `godot-iap`. |
-| `iapkit` | — | Set to `true` only when the app uses IAPKit receipt validation. Omit it otherwise. |
+| `iapkit` | — | Set to `true` only when the app uses IAPKit. Omit it otherwise. |
| `ios` | — | App Store URL. |
| `android` | — | Google Play URL. |
| `web` | — | Website or other store, shown as "Website". |
@@ -75,7 +75,7 @@ are skipped at render time.
Reply to [discussion #350](https://github.com/hyodotdev/openiap/discussions/350) or
email **hyo@hyo.dev** with your app name, one-liner, logo, store links, and which
-library you use. Also tell us whether you use IAPKit for receipt validation.
+library you use. Also tell us whether you use IAPKit.
## Removal and updates
diff --git a/packages/docs/public/llms-full.txt b/packages/docs/public/llms-full.txt
index 84c345cd5..d7a0d2f58 100644
--- a/packages/docs/public/llms-full.txt
+++ b/packages/docs/public/llms-full.txt
@@ -3,7 +3,7 @@
> OpenIAP: Unified in-app purchase specification for iOS & Android
> Documentation: https://openiap.dev
> Quick Reference: https://openiap.dev/llms.txt
-> Generated: 2026-08-17T10:30:38.945Z
+> Generated: 2026-08-17T23:14:29.027Z
## Table of Contents
1. Installation
@@ -2053,7 +2053,8 @@ transient or malformed responses.
# IAPKit
-> Receipt-validation SaaS managed by OpenIAP. Hosted at https://kit.openiap.dev.
+> Open-source purchase validation and entitlement infrastructure for the
+> OpenIAP ecosystem, managed by OpenIAP. Hosted at https://kit.openiap.dev.
> One Bearer-authed endpoint for Apple / Google / Horizon / Amazon;
> harmonized response shape with `{ store, isValid, state, productId?, environment? }` so your backend has a single code path for
> entitlement + refund detection.
@@ -2174,6 +2175,25 @@ Harmonized `state` values (truthy `isValid`): `ENTITLED`,
- A matching IAPKit catalog row must exist before a payload write. After creating a product directly in App Store Connect or Play Console, run a pull sync and wait for its job to succeed, or create the row with secret-authenticated `POST /v1/products`; early writes return `PRODUCT_NOT_FOUND`.
- Secret-key `GET /v1/products/client-payload/{productId}?platform=...` returns the current payload plus durable `expectedVersion`, including the revision after deletion. MCP exposes it as `iapkit_get_client_payload`.
+## Dashboard order lookup
+
+The authenticated project dashboard includes a read-only Orders tab for live
+Apple and Google order lookup. It reuses the project's store credentials,
+returns the order plus best-effort subscription status, and never stores or
+logs the order ID or result. Apple lookup is production-only and needs the App
+Apple ID; Google requires the Play service account's View financial data
+permission. This operator tool does not accept API keys and is not part of
+`/v1`.
+
+## Version compatibility
+
+Hosted `/v1` responses evolve additively. Existing fields keep their meaning,
+new response data is optional or request-gated, and a breaking contract would
+use `/v2` while `/v1` keeps serving. SDKs report their compile-time spec in
+`X-OpenIAP-Spec` for rollout measurement only; the header never changes receipt
+verification. Gate on `isValid`, require the store-verified `productId`, and
+allow optional metadata to be absent or unknown.
+
## Status codes
- `200` — verification ran; require `isValid`, an operation-appropriate `state`, and an exact store-verified `productId` match
@@ -2213,10 +2233,14 @@ assuming DAU implies safe request volume.
- [/docs](https://kit.openiap.dev/docs) — full in-dashboard documentation
- [/docs/quickstart](https://kit.openiap.dev/docs/quickstart) — signup → project → API key → first verify
- [/docs/products](https://kit.openiap.dev/docs/products) — catalog sync, client payload editing, retrieval, caching, and limits
+- [/docs/orders](https://kit.openiap.dev/docs/orders) — read-only Apple and Google order support tooling
- [/docs/verification/apple](https://kit.openiap.dev/docs/verification/apple) — bundle ID, Issuer ID, Key ID, .p8
- [/docs/verification/google](https://kit.openiap.dev/docs/verification/google) — package name, service account JSON
- [/docs/verification/horizon](https://kit.openiap.dev/docs/verification/horizon) — App ID + App Secret (write-only)
+- [/docs/verification/amazon](https://kit.openiap.dev/docs/verification/amazon) — RVS shared secret, App Tester sandbox, and background rechecks
- [/docs/api](https://kit.openiap.dev/docs/api) — request shapes, responses, errors, headers, and Amazon RVS payloads
+- [/docs/compatibility](https://kit.openiap.dev/docs/compatibility) — additive `/v1` policy and older-SDK behavior
+- [/docs/webhooks](https://kit.openiap.dev/docs/webhooks) — inbound Apple ASN v2 and Google RTDN setup
- [/docs/operations](https://kit.openiap.dev/docs/operations) — fair use, capacity, rate and concurrency limits, logs, `/health`, graceful shutdown
- [openiap.dev/docs/webhooks](https://openiap.dev/docs/webhooks) — operator setup steps for inbound Apple ASN v2 and Google RTDN lifecycle delivery
- [/docs/ai-assistants](https://kit.openiap.dev/docs/ai-assistants) — how to point Codex / Claude / Cursor / etc. at this file
diff --git a/packages/docs/public/llms.txt b/packages/docs/public/llms.txt
index 5811b9328..d9fe35eaf 100644
--- a/packages/docs/public/llms.txt
+++ b/packages/docs/public/llms.txt
@@ -3,7 +3,7 @@
> OpenIAP: Unified in-app purchase specification for iOS & Android
> Documentation: https://openiap.dev
> Full Reference: https://openiap.dev/llms-full.txt
-> Generated: 2026-08-17T10:30:38.945Z
+> Generated: 2026-08-17T23:14:29.027Z
## Installation
diff --git a/packages/docs/public/sponsors/amazon-dark.webp b/packages/docs/public/sponsors/amazon-dark.webp
new file mode 100644
index 0000000000000000000000000000000000000000..e7337c894c1d1ce25cfeac39a55ae15dfe990576
GIT binary patch
literal 15858
zcmV
z3Qa?m`9^3Gq1767Vhc`c(Y3k;h7X$ydC1J!fn#W?|FCVRI^6H-f7`ZKPV)cb|Np1w
z2SSo{pVz(C>QmQy07)k^<_z60C4~@
zmW^fvhh{)R(MXISL?F_}5}FqZ1at%;G{`~EH(?3uJ@z%&2;p=-G3cZ%-$UH$QO
zp`_!=9q$DetTR{3T8pk5SU8O}W6J|(&9&lL$-5qW9dv9&n&d(!0r!%|c<~sR^aLLl
z%7VZPagoCy7YLU!nq`Q6z9}$0JwzVj`JaCZ0059qtb6ySTw5*Mw%uJ`oiE$9W!v7)
z+z*F^v#o5~_}B9C5Ox#5B;o)L$E9&g+zU^{@%Ur>6W)rC;EVVszK{P)jMMOKd1|auh`o&b$`?iw{17Kf-ga!F6!9W^$FyI1HD-&2V=-9xt1Nf52Nt
z;S2Z%zE>S3#Oe4>D?W*LJ%iWdxAAm50Jp^zaF%9D@iuqi^0*_8#Y^xfzrp+Px&Qe~
zR1rVicF*VVKlpR}2A+Ul!L@PDW^((@I1)F-gFEqBya6A@SMh_W;(+Hy;lFVbUW5n!
z2uC%O!#3kwxZ&Xt|BBDnMP}nii!a5muRCM2o5FeU3rFXzkCKZYFZ;=t@5E2r%K`j6
ze(g-08YQ>x$M{3s8y9JI1I;))u7~?~;2*z^Ga|E}`ak>!ehUxz)7a+J%pY)zcP<{a
zK1x1*g3o^yFPMP~XWSYN;>t~U-Qwyfxz(S+U*NG5afnlVWC>;JVDo%o}&VS{4)OTr31Et
z%ixhmgD?ofAaNKZ4uc>F!k}UpghApkNE`-{FbIMm3@U~}
z`_CDb>0Jx;j~P7W_?P5ZBt4>7xFT
zIj=ZI>6PjMq4Gl)4wEqZpCb48K@1fcuCw{JK>u#qxwiCiK$P8+G3$QUCqZm|>m#(>
zU8_iJ6{max7qx@t!I8oj%r>Rs?Y2NB^vnbBe%4t;4&IbD_A14aGWzON70s{JP4G6ivQb@>s+5Z{d4J|4~@0j4sEEd>?>y^8^
zy5PRJguWT4bA{a8j+@YM-u12`ZZ-j~aorv+H2wvD@QU4hDk|gae=xyuojEDEfgf{-
zaP4dD6OylezZ4CRj+A~gleGBpwgBh4?)a4o{+l4k3l4f{n{j(UNKSZdAl9>yySxJD
zcU}5Y=zMu#b5cCZl!_ioUg_69fv$3fg1cRBeL{Ad+Z?P0wW@!Xth0j!y!By)_O2H6
z+nW=%X+%iK?*Ew|X{F2f1g*hkq4ng0enNtx_Wem)+)&U{Uk##VACD8#&yCSXggYha
z!(xHIu}hf-!v%lti&UjObyY~;{gZnjc7#vJb$y$mt3%0h9ZMDhw>O7!+AkC8lkW9s
zV|a4v8q-8{
zIOVV|K9!WwAt5m|S_S_~TWE~yOwjCoqC?k*ZC3Bmb3*l<>$Mc&M#*T#lWifg|9TXU
z>t>SZaoqiyXw5{z=ztLE+i6_l5g{|shZ^U$x}wLr-&s1RLbA{uSF=z@I4&7WtA)(y
zXE6?M7j0hs)6qioO2qo53Yos0M#Y~KLVbN`Nm*x$E<+D{tXoP8p*?@7hQ=iUPm1g`
z_jQB@<3ywOM!y#DixFy&E`&byWpi@63#oxWDVcGn==7!xt2Se
))}
+
+
+
+
+ See more apps built with OpenIAP
+
+
diff --git a/packages/docs/src/pages/docs/ecosystem.tsx b/packages/docs/src/pages/docs/ecosystem.tsx
index 1538bf3ac..71a787c13 100644
--- a/packages/docs/src/pages/docs/ecosystem.tsx
+++ b/packages/docs/src/pages/docs/ecosystem.tsx
@@ -10,14 +10,16 @@ function Ecosystem() {
Ecosystem
- Here is the big picture of OpenIAP ecosystem. If you are interested in
- joining the ecosystem, please contact{' '}
+ OpenIAP defines the shared purchase contract and ships native and
+ framework SDKs. IAPKit adds optional hosted purchase verification,
+ entitlements, store notifications, product sync, and MCP operations. If
+ you are interested in joining the ecosystem, please contact{' '}
hyo@hyo.dev.
@@ -158,6 +160,19 @@ function Ecosystem() {
+
+
Optional infrastructure
+
+
+ IAPKit
+ {' '}
+ is the optional hosted layer for teams that need server-side purchase
+ verification, entitlement state, App Store and Google Play lifecycle
+ notifications, product synchronization, revenue visibility, and MCP
+ operations without building a separate IAP backend.
+
+
+
Maintaining open source libraries requires significant time and effort.
If you find OpenIAP helpful, please consider{' '}
diff --git a/packages/docs/src/pages/docs/features/validation.tsx b/packages/docs/src/pages/docs/features/validation.tsx
index 7c3266cd8..084c89a0a 100644
--- a/packages/docs/src/pages/docs/features/validation.tsx
+++ b/packages/docs/src/pages/docs/features/validation.tsx
@@ -211,14 +211,15 @@ if result is VerifyPurchaseResultIOS and result.is_valid:
>
IAPKit
{' '}
- is an open-source (MIT) receipt-validation service
- for App Store, Google Play, Amazon Appstore, and Meta Horizon
- purchases. Instead of running your own backend that talks to each
- store's verification API, you forward the JWS, purchase token, Amazon
- receipt id, or Horizon entitlement payload to IAPKit and get a
- normalized verification response — so one-time in-app purchases are
- checked against the store's authoritative state. Amazon Fire OS and
- Vega OS both use the iapkit.amazon payload. Use the
+ is open-source (MIT) purchase validation and
+ entitlement infrastructure for the OpenIAP ecosystem. Its managed
+ validation endpoint supports App Store, Google Play, Amazon Appstore,
+ and Meta Horizon purchases. Instead of running your own backend that
+ talks to each store's verification API, you forward the JWS, purchase
+ token, Amazon receipt id, or Horizon entitlement payload to IAPKit and
+ get a normalized verification response — so one-time in-app purchases
+ are checked against the store's authoritative state. Amazon Fire OS
+ and Vega OS both use the iapkit.amazon payload. Use the
hosted version at{' '}
- IAPKit is OpenIAP's hosted receipt-validation backend (see{' '}
- Purchase Verification with IAPKit).
- The MAUI package ships the same app-facing helper as the other OpenIAP
- SDKs: create a kit client with your publishable key to read purchase
- status and entitlements and to bind a purchase to a user. Store
- lifecycle events (App Store Server Notifications, Google Play RTDN)
- are delivered to IAPKit's backend, not to your app — the client reads
- current state through these bounded calls rather than subscribing to a
- webhook stream.
+ IAPKit is OpenIAP's hosted purchase-validation and entitlement backend
+ (see Purchase Verification with IAPKit
+ ). The MAUI package ships the same app-facing helper as the other
+ OpenIAP SDKs: create a kit client with your publishable key to read
+ purchase status and entitlements and to bind a purchase to a user.
+ Store lifecycle events (App Store Server Notifications, Google Play
+ RTDN) are delivered to IAPKit's backend, not to your app — the client
+ reads current state through these bounded calls rather than
+ subscribing to a webhook stream.
{`using OpenIap;
diff --git a/packages/docs/src/pages/docs/updates/announcements.tsx b/packages/docs/src/pages/docs/updates/announcements.tsx
index d02e7e954..21eda84cf 100644
--- a/packages/docs/src/pages/docs/updates/announcements.tsx
+++ b/packages/docs/src/pages/docs/updates/announcements.tsx
@@ -1,6 +1,7 @@
import { useMemo } from 'react';
import { Link } from 'react-router-dom';
import Callout from '../../../components/Callout';
+import AmazonWordmark from '../../../components/AmazonWordmark';
import SEO from '../../../components/SEO';
import { useScrollToHash, getHashId } from '../../../hooks/useScrollToHash';
import Pagination from '../../../components/Pagination';
@@ -39,6 +40,7 @@ const linkIconStyle = {
interface Announcement {
id: string;
+ aliases?: readonly string[];
date: Date;
hidden?: boolean;
element: React.ReactNode;
@@ -240,13 +242,14 @@ function Announcements() {
),
},
- // 2026-06-09: Amazon Fire OS / Vega OS
+ // 2026-08-19: Amazon Fire OS / Vega OS
{
- id: '2026-06-09-amazon-fireos-vega',
- date: new Date('2026-06-09'),
- hidden: true,
+ id: '2026-08-19-amazon-fireos-vega',
+ aliases: ['2026-06-09-amazon-fireos-vega'],
+ date: new Date('2026-08-19'),
element: (
-
Today marks a meaningful milestone for our team: We're thrilled to
share that OpenIAP is now backed by Amazon Developer through
@@ -275,11 +292,11 @@ function Announcements() {
for apps on Fire TVs through the OpenIAP ecosystem.
- For context, there's over 300 million Fire TV devices purchased
- around the world. Last year alone these TVs have been used for more
- than 100 billion hours for things like streaming shows, playing
- games, and watching live events. For example, if you have ever
- rented a streaming movie, you have gone through an IAP payment flow.
+ For context, more than 300 million Fire TV devices have been
+ purchased around the world. They are used for streaming shows,
+ playing games, and watching live events. For example, if you have
+ ever rented a streaming movie, you have gone through an IAP payment
+ flow.