{"_id":"@appmerge/appactor-sdk","_rev":"9-1558199ce8ca3cbff6db89e9f8d1e64f","name":"@appmerge/appactor-sdk","dist-tags":{"latest":"3.0.1"},"versions":{"1.0.0":{"name":"@appmerge/appactor-sdk","version":"1.0.0","keywords":["appactor","appmerge","sdk","premium","tokens","subscription"],"author":"","license":"ISC","_id":"@appmerge/appactor-sdk@1.0.0","maintainers":[{"name":"appmerge","email":"appmerge34@gmail.com"}],"dist":{"shasum":"04ee515582751f99dbeb6f117ec2dc8096efe2b6","tarball":"https://registry.npmjs.org/@appmerge/appactor-sdk/-/appactor-sdk-1.0.0.tgz","fileCount":7,"integrity":"sha512-fwB4uypidb9KxPHdwxmSGohthAGW2O6hltCT1fz7Im0CNgGwMAFqx50iQpLF+OyKFbQ4QrEqoMAJfQhNOpY4Yw==","signatures":[{"sig":"MEQCIGfGwtUM0dmI/0L/sjA3/FmpLnYV9p4t/DF9q6w+9rzFAiBd7Sb9wOn/EhcswwnCdzBrTeUjrSTi1QjnQNwKZEk0dw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":12596},"main":"index.js","type":"commonjs","types":"src/types/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"f3efa2804a63b88f3ddf3bdee22ce27c2c6e14fe","private":false,"scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"appmerge","email":"appmerge34@gmail.com"},"_npmVersion":"11.5.1","description":"Official Node.js SDK for AppActor - Manage apps and subscribers with ease","directories":{},"_nodeVersion":"24.6.0","dependencies":{"node-cache":"^5.1.2"},"publishConfig":{"access":"restricted"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/appactor-sdk_1.0.0_1775149159823_0.8616610841422156","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@appmerge/appactor-sdk","version":"1.0.1","keywords":["appactor","appmerge","sdk","premium","tokens","subscription"],"author":"","license":"ISC","_id":"@appmerge/appactor-sdk@1.0.1","maintainers":[{"name":"appmerge","email":"appmerge34@gmail.com"}],"dist":{"shasum":"8e5193af79896df61bd9ef0200c6ca9a5f52058f","tarball":"https://registry.npmjs.org/@appmerge/appactor-sdk/-/appactor-sdk-1.0.1.tgz","fileCount":7,"integrity":"sha512-XEtzhS6FiJq6Fhyx4pnvlu1kGCcAg5tOcCfzVnL4sJJh2sVFmqjkpglWGtSWOuI7GnJm/Kxy6u4BFze6mG9y4Q==","signatures":[{"sig":"MEQCID0i0MkJ8mvKt8AszOTztXnOan9C3tWjArwtOWybbhsAAiBpPU22xa++uzx57mze5e5POcaxHDFbmUTTfHpogclyYQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":12592},"main":"index.js","type":"commonjs","types":"src/types/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"f3efa2804a63b88f3ddf3bdee22ce27c2c6e14fe","private":false,"scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"appmerge","email":"appmerge34@gmail.com"},"_npmVersion":"11.5.1","description":"Official Node.js SDK for AppActor - Manage apps and subscribers with ease","directories":{},"_nodeVersion":"24.6.0","dependencies":{"node-cache":"^5.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/appactor-sdk_1.0.1_1775149217471_0.918426091095925","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@appmerge/appactor-sdk","version":"1.0.2","keywords":["appactor","appmerge","sdk","premium","tokens","subscription"],"author":"","license":"ISC","_id":"@appmerge/appactor-sdk@1.0.2","maintainers":[{"name":"appmerge","email":"appmerge34@gmail.com"}],"dist":{"shasum":"8c2fc67f84dfdd0455c05e822b3e7d41c9c2533e","tarball":"https://registry.npmjs.org/@appmerge/appactor-sdk/-/appactor-sdk-1.0.2.tgz","fileCount":8,"integrity":"sha512-Spr41qLBXfsCORX1i75aFnuDjRSN1pGELANK79bZmnGQQx6z8vmS3OW6Wkb9GRTqfy/5ueInqG1VEtKzFVwDmQ==","signatures":[{"sig":"MEUCIQD9tHO5Eag6BP4cGe8eBkh7bhShDGg9g+39yyOnX41ecQIgIVEZHPLV1r0MMfxMajsna46Fe/RMEpsKmPgP8IQOySY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":19876},"main":"index.js","type":"commonjs","types":"src/types/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"a34824ec8c10adfa2019c36eefb8dc7165e0564f","private":false,"scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"appmerge","email":"appmerge34@gmail.com"},"_npmVersion":"11.5.1","description":"Official Node.js SDK for AppActor - Manage apps and subscribers with ease","directories":{},"_nodeVersion":"24.6.0","dependencies":{"node-cache":"^5.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/appactor-sdk_1.0.2_1775149484573_0.9102143083984842","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@appmerge/appactor-sdk","version":"2.0.0","keywords":["appactor","appmerge","sdk","premium","tokens","subscription"],"author":"","license":"ISC","_id":"@appmerge/appactor-sdk@2.0.0","maintainers":[{"name":"appmerge","email":"appmerge34@gmail.com"}],"dist":{"shasum":"e3d9cff2bf07e39dc302c894b752be330fcf8a21","tarball":"https://registry.npmjs.org/@appmerge/appactor-sdk/-/appactor-sdk-2.0.0.tgz","fileCount":8,"integrity":"sha512-iS4LPZYLrSKzrlE5ho4CZr6xiisjKy3uoMFctopXxgE4yt8F3oYZ2hW6uZfpBePl7ZuEsHpPG98+wEOISM2oRw==","signatures":[{"sig":"MEYCIQCaF7bqcmAjxhf/Iqb9U1dQGtrrxBXfcHsC0g1CYn2T4gIhANnPZbRncDEAzaLjChCHcgz3GxIhnIHjuQkdUX4jbStS","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":20816},"main":"index.js","type":"commonjs","types":"src/types/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"a34824ec8c10adfa2019c36eefb8dc7165e0564f","private":false,"scripts":{"test":"node --test tests/*.test.js"},"_npmUser":{"name":"appmerge","email":"appmerge34@gmail.com"},"_npmVersion":"11.5.1","description":"Official Node.js SDK for AppActor - Manage apps and subscribers with ease","directories":{},"_nodeVersion":"24.6.0","dependencies":{"node-cache":"^5.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/appactor-sdk_2.0.0_1777806259675_0.31860974656248975","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@appmerge/appactor-sdk","version":"2.1.0","keywords":["appactor","appmerge","sdk","premium","tokens","subscription"],"author":"","license":"ISC","_id":"@appmerge/appactor-sdk@2.1.0","maintainers":[{"name":"appmerge","email":"appmerge34@gmail.com"}],"dist":{"shasum":"26fbcac0f622669c033671032b9b7d863aebca73","tarball":"https://registry.npmjs.org/@appmerge/appactor-sdk/-/appactor-sdk-2.1.0.tgz","fileCount":8,"integrity":"sha512-omCtFhBgRdBqJGhHX7xdo0UiLjkZzQCVYcxDU5iprvdFivgpzqnapge3PzwULtXepvEEvFcFa3ayDEJHmdRszQ==","signatures":[{"sig":"MEQCIBVkLRW7k09ZaNp7Ut6LHKFZ2JPAp5spAwz4il6SajIvAiBTyS4B04AivkrvBsWSyFEfdF3TVtGzajU7ZSutJTKvFA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25452},"main":"index.js","type":"commonjs","types":"src/types/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"f125e0202fc3fdfdd185f14ab9c9c4f8f7446ee4","private":false,"scripts":{"test":"node --test tests/*.test.js"},"_npmUser":{"name":"appmerge","email":"appmerge34@gmail.com"},"_npmVersion":"11.5.1","description":"Official Node.js SDK for AppActor - Manage apps and subscribers with ease","directories":{},"_nodeVersion":"24.6.0","dependencies":{"node-cache":"^5.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/appactor-sdk_2.1.0_1777815972428_0.611050352775335","host":"s3://npm-registry-packages-npm-production"}},"2.1.1":{"name":"@appmerge/appactor-sdk","version":"2.1.1","keywords":["appactor","appmerge","sdk","premium","tokens","subscription"],"author":"","license":"ISC","_id":"@appmerge/appactor-sdk@2.1.1","maintainers":[{"name":"appmerge","email":"appmerge34@gmail.com"}],"dist":{"shasum":"842074aadbd6a5067cd31ccc040c4d3f57c83f16","tarball":"https://registry.npmjs.org/@appmerge/appactor-sdk/-/appactor-sdk-2.1.1.tgz","fileCount":8,"integrity":"sha512-V3CZ3aq83NW4V9as1xX2rG8kobeom7VtobNFbphtcf9vefWC0fKpA1/Gk4O9EZG5wxooax6320fDvCrnGpza7A==","signatures":[{"sig":"MEYCIQDhKJmLz8X2LlNlR/tEn/+7cduFRE9TFaVXr321cbPfsQIhAKgPKfw/M5Qr4sO3omhWlWNovH5FWB6D2Z6ax5bl/tRy","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25596},"main":"index.js","type":"commonjs","types":"src/types/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"cb79b1c90e475e2528a8a0d4731a4de1341fcecb","private":false,"scripts":{"test":"node --test tests/*.test.js"},"_npmUser":{"name":"appmerge","email":"appmerge34@gmail.com"},"_npmVersion":"11.5.1","description":"Official Node.js SDK for AppActor - Manage apps and subscribers with ease","directories":{},"_nodeVersion":"24.6.0","dependencies":{"node-cache":"^5.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/appactor-sdk_2.1.1_1777818816230_0.7866920842304612","host":"s3://npm-registry-packages-npm-production"}},"3.0.0":{"name":"@appmerge/appactor-sdk","version":"3.0.0","keywords":["appactor","appmerge","sdk","premium","tokens","subscription"],"author":"","license":"ISC","_id":"@appmerge/appactor-sdk@3.0.0","maintainers":[{"name":"appmerge","email":"appmerge34@gmail.com"}],"dist":{"shasum":"f86d0307833af324c51e5f5afc68d7d0b18ce741","tarball":"https://registry.npmjs.org/@appmerge/appactor-sdk/-/appactor-sdk-3.0.0.tgz","fileCount":8,"integrity":"sha512-0WSHsyWd/G1wgWuk+smDbPTx240kx/58KABtnW2qLklnNJHHhwjN80kx64muIkH1EXWxz69zH/Y1G1+YV87/ow==","signatures":[{"sig":"MEUCIADDwcK3wBrgwV8oEOEnKY9WtO3Y9zFBm/qC+KGI0XHoAiEAnI+uLYG4RTojPYJLmkZ6QtVPGzeu+BdC2jcdzJwqtLw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":29717},"main":"index.js","type":"commonjs","types":"src/types/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"cb79b1c90e475e2528a8a0d4731a4de1341fcecb","private":false,"scripts":{"test":"node --test tests/*.test.js"},"_npmUser":{"name":"appmerge","email":"appmerge34@gmail.com"},"_npmVersion":"11.5.1","description":"Official Node.js SDK for AppActor - Manage apps and subscribers with ease","directories":{},"_nodeVersion":"24.6.0","dependencies":{"node-cache":"^5.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/appactor-sdk_3.0.0_1777835553919_0.015461166708058194","host":"s3://npm-registry-packages-npm-production"}},"3.0.1":{"name":"@appmerge/appactor-sdk","version":"3.0.1","private":false,"publishConfig":{"access":"public"},"description":"Official Node.js SDK for AppActor - Manage apps and subscribers with ease","main":"index.js","types":"src/types/index.d.ts","scripts":{"test":"node --test tests/*.test.js"},"keywords":["appactor","appmerge","sdk","premium","tokens","subscription"],"author":"","license":"ISC","type":"commonjs","engines":{"node":">=18.0.0"},"dependencies":{"node-cache":"^5.1.2"},"_id":"@appmerge/appactor-sdk@3.0.1","gitHead":"0ea2b8f2c8d91e56d033b1ed4e712c723f532713","_nodeVersion":"24.6.0","_npmVersion":"11.5.1","dist":{"integrity":"sha512-rASD3Shi/BdINqtHdz3iqXE8Q44k6H5PYzRV8tnPdL9yXu7g+WaLutPQu9FqQdyHwA8qk1RlMDpovgsFyOmuZA==","shasum":"3dfd91100aea85066a5188c83d01352f32314dd6","tarball":"https://registry.npmjs.org/@appmerge/appactor-sdk/-/appactor-sdk-3.0.1.tgz","fileCount":8,"unpackedSize":36944,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDmdMdKvZ1Taomd2k45PIDDAR0Pn7ggWl4TaO8XKUGJ0gIhAKG/c7XgZHZ/7UEoiT4XXFTJnOXYjYa9BhxYEl0SHgaE"}]},"_npmUser":{"name":"appmerge","email":"appmerge34@gmail.com"},"directories":{},"maintainers":[{"name":"appmerge","email":"appmerge34@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/appactor-sdk_3.0.1_1778941589961_0.18719818499442953"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-02T16:59:19.708Z","modified":"2026-05-16T14:26:30.260Z","1.0.0":"2026-04-02T16:59:19.957Z","1.0.1":"2026-04-02T17:00:17.607Z","1.0.2":"2026-04-02T17:04:44.731Z","2.0.0":"2026-05-03T11:04:19.815Z","2.1.0":"2026-05-03T13:46:12.561Z","2.1.1":"2026-05-03T14:33:36.392Z","3.0.0":"2026-05-03T19:12:34.066Z","3.0.1":"2026-05-16T14:26:30.160Z"},"license":"ISC","keywords":["appactor","appmerge","sdk","premium","tokens","subscription"],"description":"Official Node.js SDK for AppActor - Manage apps and subscribers with ease","maintainers":[{"name":"appmerge","email":"appmerge34@gmail.com"}],"readme":"# AppActor Node.js SDK\n\nOfficial Node.js SDK for [AppActor](https://appactor.com) - Manage apps, subscribers, and token-based subscriptions via server-side API.\n\n## Installation\n\n```bash\nnpm install @appmerge/appactor-sdk\n```\n\n## Quick Start\n\n```javascript\nconst AppActor = require('@appmerge/appactor-sdk');\n\nconst client = new AppActor('sk_your_project_secret_key');\n\n// Get subscriber info\nconst user = await client.users.get('user-id');\nconsole.log(user);\n\n// Get project token balance\nconst tokens = await client.users.getTokenBalances('user-id');\nconsole.log(tokens.tokenBalance.total);\n\n// Debit tokens with project secret + user id. No appId needed.\nawait client.users.debitTokens('user-id', 5, {\n  idempotencyKey: 'request-123',\n  metadata: { feature: 'video-gen' },\n});\n\n// App-specific operations still use forApp(appId)\nconst appClient = client.forApp('your_app_id');\nconst app = await appClient.apps.get();\nconsole.log(app.name, app.platform);\n```\n\n## Features\n\n- **App Info** - Retrieve app details for a selected app\n- **Users/Subscribers** - Look up user info, entitlements, subscriptions, and token balances\n- **Token Operations** - Debit/grant tokens with smart renewable-first priority\n- **Caching** - Automatic 1-hour in-memory cache for app data\n- **Error Handling** - Typed error classes for every failure scenario\n- **TypeScript** - Full type definitions included\n- **Project-scoped auth** - Use one project secret key for customer reads; select an app only when needed\n\n## Initialization\n\n```javascript\nconst AppActor = require('@appmerge/appactor-sdk');\n\nconst client = new AppActor('sk_your_project_secret_key', {\n  appId: 'your_app_id',                // optional, app-specific default\n  baseUrl: 'https://api.appactor.com', // default\n  timeout: 30000,                       // default, in ms\n});\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `secretKey` | string | Yes | Your project-scoped secret API key (`sk_xxx`) from Project Settings |\n| `options.appId` | string | No | Target AppActor app id for app-specific methods. Sent as `X-AppActor-App-Id` only when present |\n| `options.baseUrl` | string | No | API base URL |\n| `options.timeout` | number | No | Request timeout in ms (default: 30000) |\n\n> AppActor secret keys are project-scoped. Customer reads and token mutations use only `sk + userId`.\n> App-specific operations such as app info require an app context.\n> Use `client.forApp(appId)` when you need one.\n\n### `client.forApp(appId)`\n\nReturns an app-scoped client with the same secret key, base URL, and timeout.\n\n```javascript\nconst appClient = client.forApp('your_app_id');\nawait appClient.apps.get();\n```\n\n## Apps Service\n\n### `client.apps.get(skipCache?)`\n\nReturns app information for the configured `appId`. Cached for 1 hour. Requires an app-scoped client.\n\n```javascript\nconst appClient = client.forApp('your_app_id');\nconst app = await appClient.apps.get();\n// {\n//   id: '1273925e-ba9e-4fe5-bd02-8c50a9c23ef8',\n//   projectId: '3f7b3e1b-a98b-4d12-9cdf-6349930cda28',\n//   name: 'Hair AI',\n//   bundleId: 'net.appmerge.hairstyle',\n//   packageName: null,\n//   platform: 'ios'\n// }\n\n// Force fresh data (skip cache)\nconst freshApp = await appClient.apps.get(true);\n```\n\n### `client.apps.clearCache()`\n\nClears cached app data. Returns `true` if cache was cleared.\n\n### `client.apps.getCacheStats()`\n\nReturns cache hit/miss statistics.\n\n```javascript\nconst stats = client.apps.getCacheStats();\n// { hits: 5, misses: 1, keys: 1, ksize: 0, vsize: 0 }\n```\n\n## Subscribers Service\n\n### `client.users.get(userId)` / `client.subscribers.getUser(userId)`\n\nGet full subscriber info including entitlements and subscriptions. Project-scoped clients return `tokenBalance: null`; use `getTokenBalances` for project token totals.\n\n```javascript\nconst subscriber = await client.users.get('appactor-anon-xxx');\n// {\n//   user: {\n//     id: '019d4ef7-...',\n//     appUserId: 'appactor-anon-xxx',\n//     aliases: [...],\n//     entitlements: {},\n//     subscriptions: {},\n//     nonSubscriptions: {},\n//     tokenBalance: null,\n//     firstSeenAt: '2026-04-02T16:11:59.195Z',\n//     lastSeenAt: '2026-04-02T16:19:29.099Z',\n//     trial: {\n//       isTrial: true,\n//       isCanceled: true,\n//       willRenew: false,\n//       activeTrialCount: 1,\n//       canceledTrialCount: 1,\n//       productId: 'weekly_trial',\n//       expiresAt: '2026-04-09T16:11:59.195Z'\n//     }\n//   }\n// }\n```\n\n#### Trial cancellation guard\n\n`user.trial` is computed by the Node SDK from active subscriptions. A user is treated as an active trial when `periodType === 'trial'` and the subscription is still active. A trial is treated as canceled when AppActor sees `autoRenew === false` or `unsubscribeDetectedAt`.\n\n```javascript\nconst subscriber = await client.users.get('appactor-anon-xxx');\n\nif (subscriber.user.trial.isTrial && subscriber.user.trial.isCanceled) {\n  // Block trial-token spending or require an upgrade.\n  return { allowed: false, reason: 'trial_canceled' };\n}\n```\n\n### `client.users.getTokenBalances(userId)`\n\nGet the project token wallet for a user.\n\n```javascript\nconst balances = await client.users.getTokenBalances('user-id');\n// {\n//   currencyCode: 'tokens',\n//   tokenBalance: { renewable: 30, nonRenewable: 10, total: 40 }\n// }\n```\n\n### `client.subscribers.getTokens(userId)`\n\nGet the project token balance. This is a convenience alias that returns only `tokenBalance`.\n\n```javascript\nconst tokens = await client.subscribers.getTokens('user-id');\n// { renewable: 30, nonRenewable: 0, total: 30 }\n```\n\n### `client.users.debitTokens(userId, amount, options?)` / `client.subscribers.debitTokens(...)`\n\nDebit tokens from a user's project wallet. By default, AppActor deducts from **renewable** tokens first, then **non-renewable**, atomically on the API side.\n\n```javascript\n// User has: renewable=30, nonRenewable=10\n\n// Debit 5 tokens\nconst balance = await client.users.debitTokens('user-id', 5, {\n  idempotencyKey: 'request-123',\n  metadata: { feature: 'video-gen' },\n});\n// Result: { renewable: 25, nonRenewable: 10, total: 35 }\n\n// Force a specific bucket if needed\nawait client.users.debitTokens('user-id', 3, { tokenType: 'non_renewable' });\n```\n\n### `client.users.grantTokens(userId, amount, options)` / `client.subscribers.grantTokens(...)`\n\nGrant project tokens. `tokenType` is required for grants.\n\n```javascript\nawait client.users.grantTokens('user-id', 25, {\n  tokenType: 'renewable',\n  idempotencyKey: 'grant-request-123',\n  metadata: { source: 'support-credit' },\n});\n```\n\n### `client.subscribers.debitTokensByAppCost(userId, tokenCost?)`\n\nDebit tokens based on a cost value. Pass `tokenCost` to keep this project-scoped. If `tokenCost` is omitted, the SDK fetches the selected app's `tokenCost`, so that fallback requires `client.forApp(appId)`.\n\n```javascript\n// Project-scoped, no app id needed\nawait client.users.debitTokensByAppCost('user-id', 10);\n\n// Optional app-cost lookup when you intentionally want app settings\nconst appClient = client.forApp('your_app_id');\nawait appClient.subscribers.debitTokensByAppCost('user-id');\n```\n\n## Error Handling\n\nEvery error extends `AppActorError` with a `code` property for programmatic handling.\n\n```javascript\nconst AppActor = require('@appmerge/appactor-sdk');\n\ntry {\n  await client.users.debitTokens('user-id', 100);\n} catch (error) {\n  if (error instanceof AppActor.InsufficientTokensError) {\n    console.log(`Need ${error.required}, have ${error.available}`);\n  } else if (error instanceof AppActor.SubscriberNotFoundError) {\n    console.log(`User not found: ${error.subscriberId}`);\n  } else if (error instanceof AppActor.ApiError) {\n    console.log(`API error ${error.statusCode}: ${error.message}`);\n  }\n}\n```\n\n### Error Classes\n\n| Error | Code | Properties | When |\n|-------|------|------------|------|\n| `AppActorError` | varies | `code` | Base class for all errors |\n| `AppNotFoundError` | `APP_NOT_FOUND` | `appId` | App not found |\n| `AppIdRequiredError` | `APP_ID_REQUIRED` | `operation` | App-specific method called without app context |\n| `SubscriberNotFoundError` | `SUBSCRIBER_NOT_FOUND` | `subscriberId` | User not found |\n| `InsufficientTokensError` | `INSUFFICIENT_TOKENS` | `subscriberId`, `required`, `available` | Not enough tokens |\n| `InvalidTokenCostError` | `INVALID_TOKEN_COST` | `tokenCost` | Token cost is 0 or invalid |\n| `ApiError` | `API_ERROR` | `statusCode`, `body` | HTTP error from API |\n\n## Complete Example\n\n```javascript\nconst AppActor = require('@appmerge/appactor-sdk');\n\nconst client = new AppActor(process.env.APPACTOR_SECRET_KEY);\n\nasync function processUserAction(userId) {\n  try {\n    // Check token balance\n    const tokens = await client.users.getTokens(userId);\n    const tokenCost = 1;\n\n    if (tokens.total < tokenCost) {\n      return { success: false, reason: 'insufficient_tokens', balance: tokens };\n    }\n\n    // Debit tokens\n    const newBalance = await client.users.debitTokensByAppCost(userId, tokenCost, {\n      idempotencyKey: `consume:${userId}:${Date.now()}`,\n      metadata: { feature: 'video-gen' },\n    });\n\n    return { success: true, balance: newBalance };\n\n  } catch (error) {\n    if (error instanceof AppActor.SubscriberNotFoundError) {\n      return { success: false, reason: 'user_not_found' };\n    }\n    throw error;\n  }\n}\n\n// Cleanup when shutting down\nprocess.on('SIGTERM', () => client.destroy());\n```\n\n## Constants\n\n```javascript\nconst { TOKEN_TYPES } = require('@appmerge/appactor-sdk');\n\nTOKEN_TYPES.AUTO          // 'auto'\nTOKEN_TYPES.RENEWABLE     // 'renewable'\nTOKEN_TYPES.NON_RENEWABLE // 'non_renewable'\n```\n\n## TypeScript\n\nFull type definitions are included. Works out of the box with TypeScript projects.\n\n```typescript\nimport AppActor, {\n  App,\n  Subscriber,\n  TokenBalance,\n  ProjectTokenBalances,\n  SubscriberNotFoundError,\n  InsufficientTokensError,\n} from '@appmerge/appactor-sdk';\n\nconst client = new AppActor(process.env.APPACTOR_SECRET_KEY!);\n\nconst subscriber: Subscriber = await client.users.get('user-id');\nif (subscriber.user.trial.isTrial && subscriber.user.trial.isCanceled) {\n  throw new Error('Trial was canceled before renewal.');\n}\n\nconst balances: ProjectTokenBalances = await client.users.getTokenBalances('user-id');\nconst tokens: TokenBalance = await client.users.getTokens('user-id');\nconst newBalance: TokenBalance = await client.users.debitTokens('user-id', 5);\n```\n\n## Requirements\n\n- Node.js >= 18.0.0 (uses native `fetch`)\n- An AppActor project-scoped secret API key (`sk_xxx`)\n- The target AppActor app id only for app-specific operations such as `apps.get()`\n\n## License\n\nISC\n","readmeFilename":"README.md"}