{"_id":"@cannasage/sdk","_rev":"2-19ae71019fe887f4094450944bd4007f","name":"@cannasage/sdk","dist-tags":{"beta":"0.1.0-beta.2","latest":"0.1.0-beta.2"},"versions":{"0.1.0-beta.1":{"name":"@cannasage/sdk","version":"0.1.0-beta.1","keywords":["cannasage","cannabis","cultivation","sdk","api-client","agriculture"],"author":{"name":"CannaSage","email":"support@cannasage.app"},"license":"MIT","_id":"@cannasage/sdk@0.1.0-beta.1","maintainers":[{"name":"jkbennitt","email":"jason@appsprout.dev"}],"homepage":"https://cannasage.app/developers","bugs":{"url":"https://github.com/AppSprout-dev/CannaSage/issues"},"dist":{"shasum":"ee39e34af8b123269e2fd6c8b8d3c3f306729d0c","tarball":"https://registry.npmjs.org/@cannasage/sdk/-/sdk-0.1.0-beta.1.tgz","fileCount":9,"integrity":"sha512-1xYIJE5brZ2Y3BTGeTc0hW++XDeWKFMc/Pl7mf/eEompHqwLbmiXxuQznadVe7gtSz/rWkutKpccsT4MsRP6qA==","signatures":[{"sig":"MEQCIEX/rhnbRY0x3Qh0ogGyUMaTQHiF3nPEMybU5ztSv6bIAiAQ8v4dCjBovOyDJzHx72goA2zPudIFtUEj6EgR3Rzj0A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":155985},"main":"./dist/index.cjs","type":"module","_from":"file:cannasage-sdk-0.1.0-beta.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"tsup --watch","lint":"eslint src tests --max-warnings 0","test":"jest","build":"tsup","generate":"tsx scripts/generate.ts","test:watch":"jest --watch"},"_npmUser":{"name":"jkbennitt","email":"jason@appsprout.dev"},"_resolved":"/tmp/2c360759c48dcf3dbb740eff6ee71d79/cannasage-sdk-0.1.0-beta.1.tgz","_integrity":"sha512-1xYIJE5brZ2Y3BTGeTc0hW++XDeWKFMc/Pl7mf/eEompHqwLbmiXxuQznadVe7gtSz/rWkutKpccsT4MsRP6qA==","repository":{"url":"git+https://github.com/AppSprout-dev/CannaSage.git","type":"git","directory":"sdks/js"},"_npmVersion":"10.8.2","description":"Official JavaScript/TypeScript SDK for the CannaSage Developer API.","directories":{},"_nodeVersion":"20.20.2","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","jest":"^29.7.0","tsup":"^8.3.5","ts-jest":"^29.2.5","typescript":"^5.6.3","@types/jest":"^29.5.14","@types/node":"^20.12.0","jest-fetch-mock":"^3.0.3","openapi-typescript":"^7.4.4"},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.1.0-beta.1_1777358954541_0.7659051508125798","host":"s3://npm-registry-packages-npm-production"}},"0.1.0-beta.2":{"_id":"@cannasage/sdk@0.1.0-beta.2","bin":{"cannasage":"dist/cli.js"},"bugs":{"url":"https://github.com/AppSprout-dev/CannaSage/issues"},"dist":{"shasum":"b6a6aa54965982d6581faf4fdc9d3cdcc2a93161","tarball":"https://registry.npmjs.org/@cannasage/sdk/-/sdk-0.1.0-beta.2.tgz","fileCount":11,"integrity":"sha512-9kbf5V/pP55ZMyhtdG0osVfAQZbQUkH9xeEbffK9Hy9wj3gKpr4PSVpMPje2KhbiMbd2+ykL5WDa46rjoG7tRQ==","signatures":[{"sig":"MEQCIGJvFjiEo/cmjjP1bCRy67Bl8REy8Oh5DzlGVp5LjeZuAiAOzontWND+aJR+v2W+U0wvyam+jmkocavS3Bm9F6jUCw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBmTHrGCn04JXCbiorXHfy7MOox12PtuCEfniaeOyIZuAiBov18OnUECjLtoMNvC1FDkqeLJtL3dDzLfgBcGyY3Z2Q=="}],"unpackedSize":225608},"main":"./dist/index.cjs","name":"@cannasage/sdk","type":"module","types":"./dist/index.d.ts","author":{"name":"CannaSage","email":"support@cannasage.app"},"module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"fedbcb3c1d0b7a345a30fdd63abaf8e1f92c66f1","license":"MIT","scripts":{"dev":"tsup --watch","lint":"eslint src tests --max-warnings 0","test":"jest","build":"tsup","generate":"tsx scripts/generate.ts","test:watch":"jest --watch","prepublishOnly":"pnpm run generate && pnpm run test && pnpm run build"},"version":"0.1.0-beta.2","_npmUser":{"name":"jkbennitt","email":"jason@appsprout.dev"},"homepage":"https://cannasage.app/developers","keywords":["cannasage","cannabis","cultivation","sdk","cli","api-client","agriculture"],"repository":{"url":"git+https://github.com/AppSprout-dev/CannaSage.git","type":"git","directory":"sdks/js"},"_npmVersion":"11.2.0","description":"Official JavaScript/TypeScript SDK for the CannaSage Developer API.","directories":{},"maintainers":[{"name":"jkbennitt","email":"jason@appsprout.dev"}],"_nodeVersion":"22.14.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","jest":"^29.7.0","tsup":"^8.3.5","ts-jest":"^29.2.5","typescript":"npm:@typescript/typescript6@^6.0.2","@types/jest":"^29.5.14","@types/node":"^20.12.0","jest-fetch-mock":"^3.0.3","@typescript/native":"npm:typescript@^7.0.2","openapi-typescript":"^7.4.4"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_0.1.0-beta.2_1789433551294_0.6395833820080368"}}},"time":{"created":"2026-04-28T06:49:14.479Z","modified":"2026-09-15T00:52:31.590Z","0.1.0-beta.1":"2026-04-28T06:49:14.655Z","0.1.0-beta.2":"2026-09-15T00:52:31.407Z"},"bugs":{"url":"https://github.com/AppSprout-dev/CannaSage/issues"},"author":{"name":"CannaSage","email":"support@cannasage.app"},"license":"MIT","homepage":"https://cannasage.app/developers","keywords":["cannasage","cannabis","cultivation","sdk","cli","api-client","agriculture"],"repository":{"url":"git+https://github.com/AppSprout-dev/CannaSage.git","type":"git","directory":"sdks/js"},"description":"Official JavaScript/TypeScript SDK for the CannaSage Developer API.","maintainers":[{"name":"jkbennitt","email":"jason@appsprout.dev"}],"readme":"# @cannasage/sdk\r\n\r\nOfficial JavaScript/TypeScript SDK for the **CannaSage Developer API**. Cannabis cultivation data, SOPs, grow projects, environmental sensors, and data connectors — typed, async-friendly, Node 18+ / modern browsers.\r\n\r\n> **Beta.** This SDK is pre-1.0 and may change. Pin a specific version in production.\r\n\r\n## Install\r\n\r\n```bash\r\nnpm install @cannasage/sdk\r\n# or\r\npnpm add @cannasage/sdk\r\n# or\r\nyarn add @cannasage/sdk\r\n# or, for the official CLI:\r\nnpx @cannasage/sdk sops list\r\nnpm install -g @cannasage/sdk   # exposes the `cannasage` command\r\n```\r\n\r\n## CLI\r\n\r\n```bash\r\nexport CANNASAGE_API_KEY=csk_live_...\r\ncannasage sops list\r\ncannasage sops get <id>\r\ncannasage projects list\r\ncannasage --help\r\n```\r\n\r\nThe same binary is available via `npx @cannasage/sdk` without a global install.\r\n\r\n## Quick start\r\n\r\n### API key (server-to-server)\r\n\r\n```ts\r\nimport { CannaSageClient } from '@cannasage/sdk';\r\n\r\nconst cs = new CannaSageClient({ apiKey: process.env.CANNASAGE_API_KEY });\r\n\r\nconst { data: sops } = await cs.sops.list({ category: 'ipm' });\r\nconsole.log(sops);\r\n```\r\n\r\nCreate an API key from the CannaSage dashboard under **Settings → Developer**. Keys are shown once on creation — store them securely.\r\n\r\n### Bearer JWT (short-lived programmatic access)\r\n\r\n```ts\r\nconst cs = new CannaSageClient({ bearerToken: userJwt });\r\n```\r\n\r\n### OAuth 2.0 client credentials\r\n\r\n```ts\r\nconst cs = new CannaSageClient({\r\n  clientId: process.env.CANNASAGE_CLIENT_ID,\r\n  clientSecret: process.env.CANNASAGE_CLIENT_SECRET,\r\n  scopes: ['read', 'connectors'],\r\n});\r\n\r\n// The SDK exchanges credentials for an access token lazily on the first\r\n// request and caches it until ~1 minute before expiry.\r\nawait cs.connectors.list();\r\n```\r\n\r\n## Resources\r\n\r\n| Resource | Methods |\r\n|----------|---------|\r\n| `sops` | `list(params)`, `get(id)` |\r\n| `projects` | `list(params)`, `get(id)` |\r\n| `connectors` | `list()`, `get(id)`, `create(params)`, `update(id, params)`, `delete(id)`, `sync(id)`, `devices(id)`, `data(id, params)`, `pushData(id, params)` |\r\n| `environmental` | `listData(params)` |\r\n| `insights` | `list(params)` |\r\n| `growTracking` | `costs(params)`, `harvests(params)` |\r\n| `webhooks` | `listEvents()`, `list()`, `create(params)`, `update(id, params)`, `delete(id)`, `listDeliveries(id)`, `test(id, event)`, `redeliver(id, deliveryId)` |\r\n| `oauth` | `list()`, `create(params)`, `delete(id)` |\r\n| `keys` | `list()`, `create(params)`, `revoke(id)`, `rotate(id)`, `forceRevoke(id)`, `setRateLimit(id, limit)`, `usage(id, params)` |\r\n\r\n## Webhooks\r\n\r\nVerify incoming webhook requests by validating the `X-CannaSage-Signature` header against the raw request body:\r\n\r\n```ts\r\nimport { verifyWebhookSignature } from '@cannasage/sdk';\r\n\r\napp.post('/webhooks/cannasage', express.raw({ type: 'application/json' }), (req, res) => {\r\n  const signature = req.header('X-CannaSage-Signature') ?? '';\r\n  const isValid = verifyWebhookSignature(req.body, signature, process.env.CANNASAGE_WEBHOOK_SECRET!);\r\n  if (!isValid) return res.status(401).end();\r\n\r\n  const event = JSON.parse(req.body.toString());\r\n  // ... handle event\r\n  res.sendStatus(204);\r\n});\r\n```\r\n\r\nThe helper uses constant-time comparison and never throws on mismatch — it returns `boolean`.\r\n\r\n## Error handling\r\n\r\nAll HTTP errors throw `CannaSageAPIError` with a stable `code`, `status`, and `requestId`:\r\n\r\n```ts\r\nimport { CannaSageAPIError } from '@cannasage/sdk';\r\n\r\ntry {\r\n  await cs.sops.get('missing');\r\n} catch (err) {\r\n  if (err instanceof CannaSageAPIError) {\r\n    console.error(err.code, err.status, err.message);\r\n    if (err.code === 'NOT_FOUND') { /* ... */ }\r\n  }\r\n}\r\n```\r\n\r\n## Rate limits, retries, timeouts\r\n\r\n- **Retries**: the SDK retries up to 3 times on `429` (rate limited) and `503` (overloaded), respecting the `Retry-After` header with a 30s cap. Exponential backoff otherwise.\r\n- **Timeout**: default 30s per request. Override globally via `new CannaSageClient({ timeoutMs: 60_000 })`.\r\n- **Idempotency**: `POST` requests automatically receive an `Idempotency-Key` header (auto-UUID). Pass `{ idempotencyKey: 'your-key' }` per request to override or `{ idempotencyKey: false }` to disable.\r\n\r\n## Configuration reference\r\n\r\n```ts\r\nnew CannaSageClient({\r\n  apiKey: 'csk_live_...',          // or bearerToken, or clientId + clientSecret\r\n  baseUrl: 'https://cannasage.app', // override for local dev / staging\r\n  timeoutMs: 30_000,\r\n  maxRetries: 3,\r\n});\r\n```\r\n\r\n## TypeScript\r\n\r\nAll methods return `Promise<ApiResponse<T>>` where `ApiResponse<T> = { data: T; meta?: Record<string, unknown> }`. Resource types (`SOP`, `Project`, `Connector`, …) are exported from the package root.\r\n\r\n## License\r\n\r\nMIT — see [LICENSE](./LICENSE).\r\n\r\n## Links\r\n\r\n- [API reference](https://cannasage.app/developers/reference)\r\n- [Changelog](https://cannasage.app/developers/changelog)\r\n- [Issues](https://github.com/AppSprout-dev/CannaSage/issues)\r\n","readmeFilename":"README.md"}