{"_id":"@citation-media/moco-sdk","name":"@citation-media/moco-sdk","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@citation-media/moco-sdk","version":"1.0.0","description":"TypeScript SDK for the MOCO ERP API.","repository":{"type":"git","url":"git+https://github.com/Citation-Media/moco-sdk.git"},"type":"commonjs","main":"./dist/index.js","types":"./dist/index.d.ts","publishConfig":{"access":"public"},"scripts":{"build":"tsc -p tsconfig.json","flue:moco-api-pr":"flue run moco-api-pr --target node --id ${FLUE_RUN_ID:-moco-api-pr-local}","flue:moco-api-watch":"flue run moco-api-watch --target node --id ${FLUE_RUN_ID:-moco-api-watch-local}","generate":"node scripts/generate-from-docs.mjs","moco:api-findings":"node scripts/moco-watch/find-api-findings.mjs","test":"npm run build && node --test test/*.test.mjs"},"keywords":["moco","erp","sdk","typescript"],"license":"MIT","engines":{"node":">=18"},"devDependencies":{"@flue/cli":"latest","@flue/sdk":"latest","typescript":">=4.9","valibot":"latest"},"_id":"@citation-media/moco-sdk@1.0.0","gitHead":"28c2e83f874068d81963091bdc202143349e96a3","bugs":{"url":"https://github.com/Citation-Media/moco-sdk/issues"},"homepage":"https://github.com/Citation-Media/moco-sdk#readme","_nodeVersion":"22.17.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-A3yFywBnn5XTh20wBtLEhUk9i5kDI/NhY0dA85a4lVuRDpQb7cmJY+/H+RTcq2psLsAmB0aCa+FfqLasiuEaig==","shasum":"5f0faf1779fb241becf4a1229229f885007797a8","tarball":"https://registry.npmjs.org/@citation-media/moco-sdk/-/moco-sdk-1.0.0.tgz","fileCount":267,"unpackedSize":825404,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIEmDJE4b34HZgtqnCGw4H+/yFRow62oz0MNJbtYfZAs2AiEAwdnVE2EYHfsFKYXrV3/+pbG8tM8PMsHJlRxv6ZdwnTY="}]},"_npmUser":{"name":"juvojustin","email":"mail@justin-vogt.de"},"directories":{},"maintainers":[{"name":"juvojustin","email":"mail@justin-vogt.de"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/moco-sdk_1.0.0_1778268307818_0.37089317950476075"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-08T19:25:07.741Z","1.0.0":"2026-05-08T19:25:08.019Z","modified":"2026-05-08T19:25:08.304Z"},"maintainers":[{"name":"juvojustin","email":"mail@justin-vogt.de"}],"description":"TypeScript SDK for the MOCO ERP API.","homepage":"https://github.com/Citation-Media/moco-sdk#readme","keywords":["moco","erp","sdk","typescript"],"repository":{"type":"git","url":"git+https://github.com/Citation-Media/moco-sdk.git"},"bugs":{"url":"https://github.com/Citation-Media/moco-sdk/issues"},"license":"MIT","readme":"# MOCO TypeScript SDK\n\nA typed TypeScript SDK for the MOCO ERP API. It is generated from the cloned `mocoapp-api-docs` documentation and currently covers every documented endpoint in `mocoapp-api-docs/sections`.\n\nLast MOCO API feature check with findings: 2026-05-08T18:19:15.395Z\n\n## Install\n\n```bash\nnpm install @citation-media/moco-sdk\n```\n\nFor local development in this repository:\n\n```bash\ngit submodule update --init --recursive\nnpm install\nnpm run generate\nnpm test\n```\n\n## Quick Start\n\n```ts\nimport { MocoClient } from \"@citation-media/moco-sdk\";\n\nconst moco = new MocoClient({\n  subdomain: \"your-company\",\n  apiKey: process.env.MOCO_API_KEY,\n});\n\nconst projects = await moco.projects.list({\n  include_archived: false,\n  updated_after: \"2026-01-01T00:00:00Z\",\n  sort_by: \"name asc\",\n});\n\nconsole.log(projects.data);\nconsole.log(projects.pagination);\n```\n\n## Configuration\n\nMOCO uses a dedicated URL per customer. Configure the client with whichever form matches your account:\n\n```ts\nnew MocoClient({ subdomain: \"example\", apiKey: \"...\" });\nnew MocoClient({ domain: \"example.mocoapp.com\", apiKey: \"...\" });\nnew MocoClient({ baseUrl: \"https://example.mocoapp.com/api/v1\", apiKey: \"...\" });\n```\n\nAuthentication defaults to MOCO's token header:\n\n```http\nAuthorization: Token token=YOUR_API_KEY\n```\n\nBearer tokens and fully custom authorization headers are also supported:\n\n```ts\nnew MocoClient({ subdomain: \"example\", apiKey: \"...\", authScheme: \"bearer\" });\nnew MocoClient({ baseUrl: \"https://example.mocoapp.com/api/v1\", authorizationHeader: \"Bearer ...\" });\n```\n\n## Common Usage\n\n```ts\nconst project = await moco.projects.get({ id: 123 });\n\nawait moco.activities.create({\n  date: \"2026-05-08\",\n  project_id: 123,\n  task_id: 456,\n  seconds: 3600,\n  description: \"Implementation\",\n});\n\nawait moco.projects.archive({ id: 123 });\n```\n\nGenerated resources are grouped by MOCO domain, for example `projects`, `activities`, `companies`, `invoices`, `purchases`, `users`, `offers`, `deals`, `tags`, `webHooks`, and account resources such as `accountTaskTemplates`.\n\nThe full generated endpoint map is exported as `MOCO_ENDPOINTS` and documented in [docs/API_COVERAGE.md](docs/API_COVERAGE.md).\n\n## Filters and Pagination\n\nList methods include MOCO's pagination parameters, sorting, global filters, and entity-specific filters from the docs:\n\n```ts\nconst response = await moco.companies.list({\n  page: 1,\n  per_page: 100,\n  sort_by: \"name desc\",\n  ids: [123, 456],\n  updated_after: new Date(\"2026-01-01T00:00:00Z\"),\n  type: \"customer\",\n  tags: \"Automotive, Pharma\",\n  custom_properties: {\n    Sector: [\"Pharma\", \"Chemistry\"],\n    Newsletter: true,\n  },\n});\n```\n\nEvery list endpoint also has an `All` helper that follows MOCO's `Link: rel=\"next\"` pagination header:\n\n```ts\nfor await (const activity of moco.activities.listAll({ from: \"2026-05-01\", to: \"2026-05-31\" })) {\n  console.log(activity.id);\n}\n```\n\n## Impersonation\n\nSet `X-IMPERSONATE-USER-ID` globally or per request:\n\n```ts\nconst asUser = moco.withImpersonation(933590696);\nawait asUser.activities.create({ date: \"2026-05-08\", project_id: 1, task_id: 2, seconds: 900 });\n\nawait moco.activities.list({}, { impersonateUserId: 933590696 });\n```\n\n## Rate Limits and Errors\n\nMOCO returns `429 Too Many Requests` when the account limit is exceeded. The SDK throws `MocoRateLimitError` with retry metadata from the response headers.\n\n```ts\nimport { MocoRateLimitError } from \"@citation-media/moco-sdk\";\n\ntry {\n  await moco.projects.list();\n} catch (error) {\n  if (error instanceof MocoRateLimitError) {\n    console.log(error.retryAfterSeconds);\n    console.log(error.rateLimit);\n  }\n}\n```\n\nAutomatic retries for 429 responses can be enabled:\n\n```ts\nconst moco = new MocoClient({\n  subdomain: \"example\",\n  apiKey: \"...\",\n  rateLimit: { maxRetries: 2 },\n});\n```\n\n## Webhook Signature Verification\n\nMOCO signs webhook payloads with HMAC-SHA256. Verify the raw request payload before parsing or trusting it:\n\n```ts\nimport { createWebhookEnvelope, verifyWebhookRequest } from \"@citation-media/moco-sdk\";\n\nconst rawBody = await request.text();\nconst valid = await verifyWebhookRequest(rawBody, request.headers, process.env.MOCO_WEBHOOK_SIGNATURE_KEY!);\n\nif (!valid) {\n  throw new Error(\"Invalid MOCO webhook signature\");\n}\n\nconst envelope = createWebhookEnvelope(JSON.parse(rawBody), request.headers);\nconsole.log(envelope.target, envelope.event, envelope.userId);\n```\n\n## Custom and Future Routes\n\nIf MOCO adds a route before this SDK is regenerated, use the typed low-level request API:\n\n```ts\nconst response = await moco.request({\n  method: \"POST\",\n  path: \"/experimental_route\",\n  body: { enabled: true },\n});\n```\n\n## Development\n\nThe upstream MOCO API docs are tracked as a Git submodule at `mocoapp-api-docs`. This keeps the docs related to the SDK without vendoring the full docs repository into this repository's history.\n\nClone with submodules:\n\n```bash\ngit clone --recurse-submodules <repo-url>\n```\n\nIf you already cloned the repository:\n\n```bash\ngit submodule update --init --recursive\n```\n\nUpdate to the latest upstream docs and regenerate the SDK:\n\n```bash\ngit submodule update --remote mocoapp-api-docs\nnpm run generate\nnpm test\n```\n\nCommit the submodule pointer together with generated SDK changes:\n\n```bash\ngit add mocoapp-api-docs src/generated docs/API_COVERAGE.md\ngit commit -m \"Update generated SDK from MOCO API docs\"\n```\n\n```bash\nnpm run generate  # Rebuild resources and API coverage from mocoapp-api-docs\nnpm run build     # Type-check and emit dist\nnpm test          # Build and run SDK behavior tests\n```\n\n## Automation\n\nThis repository uses [Flue](https://github.com/withastro/flue#readme) to watch for new MOCO REST API functionality.\n\n- `MOCO API Watch` runs every Monday at 06:00 UTC and can also be triggered manually from GitHub Actions.\n- Both Flue agents use the direct Workers AI model `cloudflare-workers-ai/@cf/moonshotai/kimi-k2.6`.\n- The watch agent updates the docs submodule in its CI workspace, regenerates `docs/API_COVERAGE.md`, checks `https://www.mocoapp.com/blog.atom`, and opens one issue per new API-relevant finding.\n- Blog posts are treated as supplemental references only. The watch agent creates issues only for concrete upstream docs/API coverage additions that still need SDK work.\n- The watch agent updates the `Last MOCO API feature check with findings` timestamp above by direct commit only when it creates at least one issue.\n- `MOCO API PR Agent` runs on trusted `moco-api-update` issues and opens implementation PRs.\n- Issues created by `github-actions[bot]` are trusted only when they include the expected Flue labels and hidden finding marker. Human-created issues must be authored by a repo collaborator with `write`, `maintain`, or `admin` permission.\n- Add `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN` as GitHub Actions repository secrets before enabling the workflows. The workflow maps `CLOUDFLARE_API_TOKEN` to the `CLOUDFLARE_API_KEY` env var expected by Flue's Workers AI provider.\n- Allow GitHub Actions to create pull requests in the repository settings, or add `FLUE_GITHUB_TOKEN` as a repository secret containing a PAT with access to create branches, issues, workflow dispatches, and pull requests.\n- Package publishing uses npm trusted publishing with GitHub Actions OIDC. Configure `@citation-media/moco-sdk` on npm with this repository and workflow `.github/workflows/deploy.yml`; no `NPM_TOKEN` secret is required for that path.\n\nManual runs:\n\n```bash\ncp .env.example .env\n# Fill GITHUB_TOKEN/GH_TOKEN and Cloudflare AI Gateway values in .env, then load them:\nset -a && source .env && set +a\n\nnpm run flue:moco-api-watch -- --payload '{\"dryRun\":true}'\nnpm run flue:moco-api-pr -- --payload '{\"issueNumber\":123,\"dryRun\":true}'\n```\n","readmeFilename":"README.md","_rev":"1-0ee74fc663f2cbfbcf97fef682695e99"}