{"_id":"@assinafy/chat-sdk","_rev":"9-b8872bec5e1469a1b7158810a5e9cafe","name":"@assinafy/chat-sdk","dist-tags":{"latest":"2.3.0"},"versions":{"0.1.0":{"name":"@assinafy/chat-sdk","version":"0.1.0","keywords":["assinafy","chat","sdk","bot","esignature","signature","documents"],"author":{"name":"Assinafy"},"license":"MIT","_id":"@assinafy/chat-sdk@0.1.0","maintainers":[{"name":"billm950","email":"billm@billm.org"}],"homepage":"https://github.com/assinafy/chat-sdk#readme","bugs":{"url":"https://github.com/assinafy/chat-sdk/issues"},"dist":{"shasum":"f9a40d34f3443b539285f408d10578dafe38a123","tarball":"https://registry.npmjs.org/@assinafy/chat-sdk/-/chat-sdk-0.1.0.tgz","fileCount":43,"integrity":"sha512-O6B1Pakza1jSCY1n7G4/5+Oo6VTc9u9APz/yazqalotntc5u9ZoiW9ehSq37NKgQoUnjdr0BOP4yaZ6M0YY4uQ==","signatures":[{"sig":"MEUCICdU/WX9VFnSkOCe0wilHpi9KEnmoBtA87y1EWvMSbWMAiEA4kMFWIh9qgGrHSLI23hSF+esX2JkydelDeUZ9oVQKYw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@assinafy%2fchat-sdk@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1270640},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./ai":{"types":"./dist/ai/index.d.ts","import":"./dist/ai/index.js","require":"./dist/ai/index.cjs"},"./cards":{"types":"./dist/cards/index.d.ts","import":"./dist/cards/index.js","require":"./dist/cards/index.cjs"},"./state":{"types":"./dist/state/index.d.ts","import":"./dist/state/index.js","require":"./dist/state/index.cjs"},"./client":{"types":"./dist/client/index.d.ts","import":"./dist/client/index.js","require":"./dist/client/index.cjs"},"./adapters":{"types":"./dist/adapters/index.d.ts","import":"./dist/adapters/index.js","require":"./dist/adapters/index.cjs"}},"gitHead":"b9a3d8ab23e5f1dfc479bc5ce9abae526d3e579c","scripts":{"dev":"tsup --watch","lint":"eslint .","test":"vitest run","build":"tsup","test:unit":"vitest run tests/unit","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run build && npm run test:unit","test:integration":"vitest run tests/integration"},"_npmUser":{"name":"billm950","email":"billm@billm.org"},"repository":{"url":"git+https://github.com/assinafy/chat-sdk.git","type":"git"},"_npmVersion":"10.8.2","description":"Chat SDK for Assinafy — build chat bots and conversational integrations on top of the Assinafy document-signing API.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","dotenv":"^16.6.1","eslint":"^9.17.0","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^22.10.0","@typescript-eslint/parser":"^8.18.0","@typescript-eslint/eslint-plugin":"^8.18.0"},"_npmOperationalInternal":{"tmp":"tmp/chat-sdk_0.1.0_1779989941817_0.7082234635926801","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@assinafy/chat-sdk","version":"1.0.0","keywords":["assinafy","chat","sdk","bot","esignature","signature","documents"],"author":{"name":"Assinafy"},"license":"MIT","_id":"@assinafy/chat-sdk@1.0.0","maintainers":[{"name":"billm950","email":"billm@billm.org"}],"homepage":"https://github.com/assinafy/chat-sdk#readme","bugs":{"url":"https://github.com/assinafy/chat-sdk/issues"},"dist":{"shasum":"25e365ebe41b8ad2b290968780e4d0a05ebb632b","tarball":"https://registry.npmjs.org/@assinafy/chat-sdk/-/chat-sdk-1.0.0.tgz","fileCount":43,"integrity":"sha512-hBC8woDIlODQPZ4/FDviAiDVJiz0gr7GcEL03rEO6Ydbr9Ce0FitYxpF/9pVRetJ41bvAUxNdY9tdKURHqC0EA==","signatures":[{"sig":"MEQCICaZAQCAWyvpjdsBkf6eojtI+zCNYwJVD2j3WC2cWI6VAiBjpU9Ss0l4ywh7Q0iH7TNRtpqrLrIhMV7iNc86vpGCkA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@assinafy%2fchat-sdk@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1281061},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./ai":{"types":"./dist/ai/index.d.ts","import":"./dist/ai/index.js","require":"./dist/ai/index.cjs"},"./cards":{"types":"./dist/cards/index.d.ts","import":"./dist/cards/index.js","require":"./dist/cards/index.cjs"},"./state":{"types":"./dist/state/index.d.ts","import":"./dist/state/index.js","require":"./dist/state/index.cjs"},"./client":{"types":"./dist/client/index.d.ts","import":"./dist/client/index.js","require":"./dist/client/index.cjs"},"./adapters":{"types":"./dist/adapters/index.d.ts","import":"./dist/adapters/index.js","require":"./dist/adapters/index.cjs"}},"gitHead":"cf81e3df0a0d14c98eb04565fe7f236e29222b9a","scripts":{"dev":"tsup --watch","lint":"eslint .","test":"vitest run","build":"tsup","test:unit":"vitest run tests/unit","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run build && npm run test:unit","test:integration":"vitest run tests/integration"},"_npmUser":{"name":"billm950","email":"billm@billm.org"},"repository":{"url":"git+https://github.com/assinafy/chat-sdk.git","type":"git"},"_npmVersion":"10.8.2","description":"Chat SDK for Assinafy — build chat bots and conversational integrations on top of the Assinafy document-signing API.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","dotenv":"^16.6.1","eslint":"^9.17.0","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^22.10.0","@typescript-eslint/parser":"^8.18.0","@typescript-eslint/eslint-plugin":"^8.18.0"},"_npmOperationalInternal":{"tmp":"tmp/chat-sdk_1.0.0_1780680454140_0.9351136892364817","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@assinafy/chat-sdk","version":"1.1.0","keywords":["assinafy","chat","sdk","bot","esignature","signature","documents"],"author":{"name":"Assinafy"},"license":"MIT","_id":"@assinafy/chat-sdk@1.1.0","maintainers":[{"name":"billm950","email":"billm@billm.org"}],"homepage":"https://github.com/assinafy/chat-sdk#readme","bugs":{"url":"https://github.com/assinafy/chat-sdk/issues"},"dist":{"shasum":"4ce6dd09d0c419852590ba52f05a1889c2bae73d","tarball":"https://registry.npmjs.org/@assinafy/chat-sdk/-/chat-sdk-1.1.0.tgz","fileCount":43,"integrity":"sha512-RlOHFbqdIDhm2GI6X6y8RmqQd10LvO6idw8rSBvoA4ORm4zWOAOSyhTtDGRyDt2K+yr8WcuzA+X2l5UUUaIIIw==","signatures":[{"sig":"MEYCIQDSgz3FUvAXxkDL2agWHi+jk6FCcjhmzI6S7MlOm/zcbgIhAIooxRwvBAOD72do6j/m7u4SzwNB/nDITsnA/Lav0ZWT","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@assinafy%2fchat-sdk@1.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1406368},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./ai":{"types":"./dist/ai/index.d.ts","import":"./dist/ai/index.js","require":"./dist/ai/index.cjs"},"./cards":{"types":"./dist/cards/index.d.ts","import":"./dist/cards/index.js","require":"./dist/cards/index.cjs"},"./state":{"types":"./dist/state/index.d.ts","import":"./dist/state/index.js","require":"./dist/state/index.cjs"},"./client":{"types":"./dist/client/index.d.ts","import":"./dist/client/index.js","require":"./dist/client/index.cjs"},"./adapters":{"types":"./dist/adapters/index.d.ts","import":"./dist/adapters/index.js","require":"./dist/adapters/index.cjs"}},"gitHead":"1ce68bd3d1569fd30ee539a2b729fef8211c77de","scripts":{"dev":"tsup --watch","lint":"eslint .","test":"vitest run","build":"tsup","test:unit":"vitest run tests/unit","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run build && npm run test:unit","typecheck:tests":"tsc -p tsconfig.test.json","test:integration":"vitest run tests/integration"},"_npmUser":{"name":"billm950","email":"billm@billm.org"},"repository":{"url":"git+https://github.com/assinafy/chat-sdk.git","type":"git"},"_npmVersion":"10.9.8","description":"Chat SDK for Assinafy — build chat bots and conversational integrations on top of the Assinafy document-signing API.","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","dotenv":"^16.6.1","eslint":"^9.17.0","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^22.10.0","@typescript-eslint/parser":"^8.18.0","@typescript-eslint/eslint-plugin":"^8.18.0"},"_npmOperationalInternal":{"tmp":"tmp/chat-sdk_1.1.0_1784314331000_0.4896093458757116","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@assinafy/chat-sdk","version":"2.0.0","keywords":["assinafy","chat","sdk","bot","esignature","signature","documents"],"author":{"name":"Assinafy"},"license":"MIT","_id":"@assinafy/chat-sdk@2.0.0","maintainers":[{"name":"billm950","email":"billm@billm.org"}],"homepage":"https://github.com/assinafy/chat-sdk#readme","bugs":{"url":"https://github.com/assinafy/chat-sdk/issues"},"dist":{"shasum":"f1bf6bc6d2eb594ea604287baeb4fe80d6d625f3","tarball":"https://registry.npmjs.org/@assinafy/chat-sdk/-/chat-sdk-2.0.0.tgz","fileCount":45,"integrity":"sha512-PuO1p94WUwEsi3Z3sRVdXL81KXmDrtv9eU5XxkTgPXPbV4qmC2VkpGPXH0RSvdng3fIGGMyoL9X9FABOFx3+8w==","signatures":[{"sig":"MEQCIBxErB264RSwL1M66PwMDmhabGj9RixvOWb7K01kSx/LAiB5UQUEHWUUSMG8wTrLZtF16Ak2iXT5vSoItZjKQ62j4w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@assinafy%2fchat-sdk@2.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1604950},"main":"./dist/index.cjs","type":"module","_from":"file:package/assinafy-chat-sdk-2.0.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=24"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./ai":{"types":"./dist/ai/index.d.ts","import":"./dist/ai/index.js","require":"./dist/ai/index.cjs"},"./cards":{"types":"./dist/cards/index.d.ts","import":"./dist/cards/index.js","require":"./dist/cards/index.cjs"},"./state":{"types":"./dist/state/index.d.ts","import":"./dist/state/index.js","require":"./dist/state/index.cjs"},"./client":{"types":"./dist/client/index.d.ts","import":"./dist/client/index.js","require":"./dist/client/index.cjs"},"./adapters":{"types":"./dist/adapters/index.d.ts","import":"./dist/adapters/index.js","require":"./dist/adapters/index.cjs"}},"scripts":{"dev":"tsup --watch","lint":"eslint .","test":"npm run test:unit","build":"tsup","verify":"npm run typecheck && npm run typecheck:tests && npm run typecheck:examples && npm run lint && npm run test:coverage && npm run build && npm run test:package","test:unit":"vitest run tests/unit","typecheck":"tsc --noEmit","test:watch":"vitest","test:package":"node scripts/smoke-package.mjs","test:coverage":"vitest run tests/unit --coverage","prepublishOnly":"npm run verify","typecheck:tests":"tsc -p tsconfig.test.json","test:integration":"vitest run tests/integration","typecheck:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:00c10562-6246-4fc7-8371-75990d6e311c"}},"_resolved":"/home/runner/work/chat-sdk/chat-sdk/package/assinafy-chat-sdk-2.0.0.tgz","overrides":{"esbuild":"0.28.2"},"_integrity":"sha512-PuO1p94WUwEsi3Z3sRVdXL81KXmDrtv9eU5XxkTgPXPbV4qmC2VkpGPXH0RSvdng3fIGGMyoL9X9FABOFx3+8w==","repository":{"url":"git+https://github.com/assinafy/chat-sdk.git","type":"git"},"_npmVersion":"11.17.0","description":"Chat SDK for Assinafy — build chat bots and conversational integrations on top of the Assinafy document-signing API.","directories":{},"sideEffects":false,"_nodeVersion":"24.19.0","allowScripts":{"esbuild@0.28.2":true,"fsevents@2.3.3":true},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"npm@11.16.0","devDependencies":{"tsx":"^4.23.12","tsup":"^8.5.1","dotenv":"^17.4.2","eslint":"^10.8.1","vitest":"^4.1.11","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/node":"^24.13.3","@vitest/coverage-v8":"^4.1.11","@typescript-eslint/parser":"^8.67.0","@typescript-eslint/eslint-plugin":"^8.67.0"},"_npmOperationalInternal":{"tmp":"tmp/chat-sdk_2.0.0_1787243717424_0.07312099246122372","host":"s3://npm-registry-packages-npm-production"}},"2.0.1":{"name":"@assinafy/chat-sdk","version":"2.0.1","keywords":["assinafy","chat","sdk","bot","esignature","signature","documents"],"author":{"name":"Assinafy"},"license":"MIT","_id":"@assinafy/chat-sdk@2.0.1","maintainers":[{"name":"billm950","email":"billm@billm.org"}],"homepage":"https://github.com/assinafy/chat-sdk#readme","bugs":{"url":"https://github.com/assinafy/chat-sdk/issues"},"dist":{"shasum":"cdfdc9be3f6d253c8d83917b3a5dd5757b134c42","tarball":"https://registry.npmjs.org/@assinafy/chat-sdk/-/chat-sdk-2.0.1.tgz","fileCount":45,"integrity":"sha512-TFouaPIM+YXiOwl9ZiAdMbZnVqG3heffOJRrbb8xbk92n6aeDa8V0sOqjzxiL0Yz/Zv1uFknVB6e6YhaE3deXA==","signatures":[{"sig":"MEQCIG9jPb9JLaigmaioIQhfWYtUSkLGuniWZhdRnNBiF7ftAiAtThMY7mFuQaIG3SlQDNzH6HhKJNf5rn16HMIeB1pY/w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@assinafy%2fchat-sdk@2.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1659802},"main":"./dist/index.cjs","type":"module","_from":"file:package/assinafy-chat-sdk-2.0.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=24"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./ai":{"import":{"types":"./dist/ai/index.d.ts","default":"./dist/ai/index.js"},"require":{"types":"./dist/ai/index.d.cts","default":"./dist/ai/index.cjs"}},"./cards":{"import":{"types":"./dist/cards/index.d.ts","default":"./dist/cards/index.js"},"require":{"types":"./dist/cards/index.d.cts","default":"./dist/cards/index.cjs"}},"./state":{"import":{"types":"./dist/state/index.d.ts","default":"./dist/state/index.js"},"require":{"types":"./dist/state/index.d.cts","default":"./dist/state/index.cjs"}},"./client":{"import":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"require":{"types":"./dist/client/index.d.cts","default":"./dist/client/index.cjs"}},"./adapters":{"import":{"types":"./dist/adapters/index.d.ts","default":"./dist/adapters/index.js"},"require":{"types":"./dist/adapters/index.d.cts","default":"./dist/adapters/index.cjs"}}},"scripts":{"dev":"tsup --watch","lint":"eslint .","test":"npm run test:unit","build":"tsup","verify":"npm run typecheck && npm run typecheck:tests && npm run typecheck:examples && npm run lint && npm run test:coverage && npm run build && npm run test:package","test:unit":"vitest run tests/unit","typecheck":"tsc --noEmit","test:watch":"vitest","test:package":"node scripts/smoke-package.mjs","test:coverage":"vitest run tests/unit --coverage","prepublishOnly":"npm run verify","typecheck:tests":"tsc -p tsconfig.test.json","test:integration":"vitest run tests/integration","typecheck:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:00c10562-6246-4fc7-8371-75990d6e311c"}},"_resolved":"/home/runner/work/chat-sdk/chat-sdk/package/assinafy-chat-sdk-2.0.1.tgz","overrides":{"esbuild":"0.28.2"},"_integrity":"sha512-TFouaPIM+YXiOwl9ZiAdMbZnVqG3heffOJRrbb8xbk92n6aeDa8V0sOqjzxiL0Yz/Zv1uFknVB6e6YhaE3deXA==","repository":{"url":"git+https://github.com/assinafy/chat-sdk.git","type":"git"},"_npmVersion":"11.17.0","description":"Chat SDK for Assinafy — build chat bots and conversational integrations on top of the Assinafy document-signing API.","directories":{},"sideEffects":false,"_nodeVersion":"24.19.0","allowScripts":{"esbuild@0.28.2":true,"fsevents@2.3.3":true},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"npm@11.19.0","devDependencies":{"tsx":"^4.23.12","tsup":"^8.5.1","dotenv":"^17.4.2","eslint":"^10.9.1","vitest":"^4.1.11","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/node":"^24.13.3","@vitest/coverage-v8":"^4.1.11","@typescript-eslint/parser":"^8.68.0","@typescript-eslint/eslint-plugin":"^8.68.0"},"_npmOperationalInternal":{"tmp":"tmp/chat-sdk_2.0.1_1787843595503_0.6641429196165229","host":"s3://npm-registry-packages-npm-production"}},"2.0.2":{"name":"@assinafy/chat-sdk","version":"2.0.2","keywords":["assinafy","chat","sdk","bot","esignature","signature","documents"],"author":{"name":"Assinafy"},"license":"MIT","_id":"@assinafy/chat-sdk@2.0.2","maintainers":[{"name":"billm950","email":"billm@billm.org"}],"homepage":"https://github.com/assinafy/chat-sdk#readme","bugs":{"url":"https://github.com/assinafy/chat-sdk/issues"},"dist":{"shasum":"d5835f85584e267cd1676bcec02c319fb9c651b1","tarball":"https://registry.npmjs.org/@assinafy/chat-sdk/-/chat-sdk-2.0.2.tgz","fileCount":45,"integrity":"sha512-Ty0DUExZIAe6/twFbZmENFwxFnTIm8S1otkqGxtnQ8/sbAFMA5Kmjt/KrzXqHuqVKVro6bW276neW87jcVCHEg==","signatures":[{"sig":"MEUCIFLgpD6R4wY/QtFAVIXo+RMeY5hNuKcIDUQlA81QpK3iAiEAtFtLnkwT+smiEMpBe9pamZ83T4rruAWf5J+k8yf9Ylc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@assinafy%2fchat-sdk@2.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1679751},"main":"./dist/index.cjs","type":"module","_from":"file:package/assinafy-chat-sdk-2.0.2.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=24"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./ai":{"import":{"types":"./dist/ai/index.d.ts","default":"./dist/ai/index.js"},"require":{"types":"./dist/ai/index.d.cts","default":"./dist/ai/index.cjs"}},"./cards":{"import":{"types":"./dist/cards/index.d.ts","default":"./dist/cards/index.js"},"require":{"types":"./dist/cards/index.d.cts","default":"./dist/cards/index.cjs"}},"./state":{"import":{"types":"./dist/state/index.d.ts","default":"./dist/state/index.js"},"require":{"types":"./dist/state/index.d.cts","default":"./dist/state/index.cjs"}},"./client":{"import":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"require":{"types":"./dist/client/index.d.cts","default":"./dist/client/index.cjs"}},"./adapters":{"import":{"types":"./dist/adapters/index.d.ts","default":"./dist/adapters/index.js"},"require":{"types":"./dist/adapters/index.d.cts","default":"./dist/adapters/index.cjs"}}},"scripts":{"dev":"tsup --watch","lint":"eslint .","test":"npm run test:unit","build":"tsup","verify":"npm run typecheck && npm run typecheck:tests && npm run typecheck:examples && npm run lint && npm run test:coverage && npm run build && npm run test:package","test:unit":"vitest run tests/unit","typecheck":"tsc --noEmit","test:watch":"vitest","test:package":"node scripts/smoke-package.mjs","test:coverage":"vitest run tests/unit --coverage","prepublishOnly":"npm run verify","typecheck:tests":"tsc -p tsconfig.test.json","test:integration":"vitest run tests/integration","typecheck:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:00c10562-6246-4fc7-8371-75990d6e311c"}},"_resolved":"/home/runner/work/chat-sdk/chat-sdk/package/assinafy-chat-sdk-2.0.2.tgz","overrides":{"esbuild":"0.28.2"},"_integrity":"sha512-Ty0DUExZIAe6/twFbZmENFwxFnTIm8S1otkqGxtnQ8/sbAFMA5Kmjt/KrzXqHuqVKVro6bW276neW87jcVCHEg==","repository":{"url":"git+https://github.com/assinafy/chat-sdk.git","type":"git"},"_npmVersion":"11.17.0","description":"Chat SDK for Assinafy — build chat bots and conversational integrations on top of the Assinafy document-signing API.","directories":{},"sideEffects":false,"_nodeVersion":"24.19.0","allowScripts":{"esbuild@0.28.2":true,"fsevents@2.3.3":true},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"npm@11.19.0","devDependencies":{"tsx":"^4.23.12","tsup":"^8.5.1","dotenv":"^17.4.2","eslint":"^10.9.1","vitest":"^4.1.11","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/node":"^24.13.3","@vitest/coverage-v8":"^4.1.11","@typescript-eslint/parser":"^8.68.0","@typescript-eslint/eslint-plugin":"^8.68.0"},"_npmOperationalInternal":{"tmp":"tmp/chat-sdk_2.0.2_1787870898684_0.6363947585227452","host":"s3://npm-registry-packages-npm-production"}},"2.2.0":{"name":"@assinafy/chat-sdk","version":"2.2.0","keywords":["assinafy","chat","sdk","bot","esignature","signature","documents"],"author":{"name":"Assinafy"},"license":"MIT","_id":"@assinafy/chat-sdk@2.2.0","maintainers":[{"name":"billm950","email":"billm@billm.org"}],"homepage":"https://github.com/assinafy/chat-sdk#readme","bugs":{"url":"https://github.com/assinafy/chat-sdk/issues"},"dist":{"shasum":"5978b08abef670e6e8df167783a90531c2b9933c","tarball":"https://registry.npmjs.org/@assinafy/chat-sdk/-/chat-sdk-2.2.0.tgz","fileCount":46,"integrity":"sha512-ZjhLaBkwfrFVVK0uRoMZlPQsAV/6qL3U2d4eKho2Pk1rur2eFvWe7UGNLrcT6uNRw0iqmqmBJrPRK4myBz71hw==","signatures":[{"sig":"MEUCIQCxjoLTIXTpsQSURovK8N4U/PhsQ9bIyy2OikPSJob9swIgHGtTvNq1cxp8rXCkqXwjT6kfG7yQORAlsZ7ynFN6EQU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIAnZ6WM2oE1nA8IJi5WLtoZ1AEKgY8M2J9p0Fneys1PNAiEAh/DPzJzLEyUFJnGJ0VZTVgPXcEI7AGp3k/4SOFR9jGM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@assinafy%2fchat-sdk@2.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":2099317},"main":"./dist/index.cjs","type":"module","_from":"file:package/assinafy-chat-sdk-2.2.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=24"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./ai":{"import":{"types":"./dist/ai/index.d.ts","default":"./dist/ai/index.js"},"require":{"types":"./dist/ai/index.d.cts","default":"./dist/ai/index.cjs"}},"./cards":{"import":{"types":"./dist/cards/index.d.ts","default":"./dist/cards/index.js"},"require":{"types":"./dist/cards/index.d.cts","default":"./dist/cards/index.cjs"}},"./state":{"import":{"types":"./dist/state/index.d.ts","default":"./dist/state/index.js"},"require":{"types":"./dist/state/index.d.cts","default":"./dist/state/index.cjs"}},"./client":{"import":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"require":{"types":"./dist/client/index.d.cts","default":"./dist/client/index.cjs"}},"./adapters":{"import":{"types":"./dist/adapters/index.d.ts","default":"./dist/adapters/index.js"},"require":{"types":"./dist/adapters/index.d.cts","default":"./dist/adapters/index.cjs"}}},"scripts":{"dev":"tsup --watch","lint":"eslint .","test":"npm run test:unit","build":"tsup","verify":"npm run typecheck && npm run typecheck:tests && npm run typecheck:examples && npm run lint && npm run test:coverage && npm run build && npm run test:package","test:unit":"vitest run tests/unit","typecheck":"tsc --noEmit","test:watch":"vitest","test:package":"node scripts/smoke-package.mjs","test:coverage":"vitest run tests/unit --coverage","prepublishOnly":"npm run verify","typecheck:tests":"tsc -p tsconfig.test.json","test:integration":"vitest run tests/integration","typecheck:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:00c10562-6246-4fc7-8371-75990d6e311c"}},"_resolved":"/home/runner/work/chat-sdk/chat-sdk/package/assinafy-chat-sdk-2.2.0.tgz","overrides":{"esbuild":"0.28.2"},"_integrity":"sha512-ZjhLaBkwfrFVVK0uRoMZlPQsAV/6qL3U2d4eKho2Pk1rur2eFvWe7UGNLrcT6uNRw0iqmqmBJrPRK4myBz71hw==","repository":{"url":"git+https://github.com/assinafy/chat-sdk.git","type":"git"},"_npmVersion":"11.19.0","description":"Chat SDK for Assinafy — build chat bots and conversational integrations on top of the Assinafy document-signing API.","directories":{},"sideEffects":false,"_nodeVersion":"24.21.0","allowScripts":{"esbuild@0.28.2":true,"fsevents@2.3.3":true},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"npm@11.19.0","devDependencies":{"tsx":"^4.23.12","tsup":"^8.5.1","dotenv":"^17.4.2","eslint":"^10.9.1","vitest":"^4.1.11","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/node":"^24.13.3","@vitest/coverage-v8":"^4.1.11","@typescript-eslint/parser":"^8.68.0","@typescript-eslint/eslint-plugin":"^8.68.0"},"_npmOperationalInternal":{"tmp":"tmp/chat-sdk_2.2.0_1790207557135_0.27883908598107654","host":"s3://npm-registry-packages-npm-production"}},"2.2.1":{"name":"@assinafy/chat-sdk","version":"2.2.1","keywords":["assinafy","chat","sdk","bot","esignature","signature","documents"],"author":{"name":"Assinafy"},"license":"MIT","_id":"@assinafy/chat-sdk@2.2.1","maintainers":[{"name":"billm950","email":"billm@billm.org"}],"homepage":"https://github.com/assinafy/chat-sdk#readme","bugs":{"url":"https://github.com/assinafy/chat-sdk/issues"},"dist":{"shasum":"0078f9190a9b08e1b4588820889eacc764ce7aba","tarball":"https://registry.npmjs.org/@assinafy/chat-sdk/-/chat-sdk-2.2.1.tgz","fileCount":46,"integrity":"sha512-oNDY0+TjjX4QIAQsMP3Q4VwMlnVWapWecWeeXXHV790U2+KOsxOG7m6OQzJ2j6GCgJq6+w4wuZrG7uu0kYxrtw==","signatures":[{"sig":"MEQCIC+KQHb8J94lUev7lGjeXiEZeQQhzeNTu9BjfknARBl5AiBEr/cHnUHLCbJwovRaZaslsGP0Hr7FHGFGwWGM3vPc4g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCICV3u2oOUw2nXQPsO0xUMbfKZDWtI3GPSqVyKwPD7s7SAiEAzt+GQ38YcnnhCGV3q1U9PaiK9TuprGmRI+JwluWJhxs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@assinafy%2fchat-sdk@2.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":2099638},"main":"./dist/index.cjs","type":"module","_from":"file:package/assinafy-chat-sdk-2.2.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=24"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./ai":{"import":{"types":"./dist/ai/index.d.ts","default":"./dist/ai/index.js"},"require":{"types":"./dist/ai/index.d.cts","default":"./dist/ai/index.cjs"}},"./cards":{"import":{"types":"./dist/cards/index.d.ts","default":"./dist/cards/index.js"},"require":{"types":"./dist/cards/index.d.cts","default":"./dist/cards/index.cjs"}},"./state":{"import":{"types":"./dist/state/index.d.ts","default":"./dist/state/index.js"},"require":{"types":"./dist/state/index.d.cts","default":"./dist/state/index.cjs"}},"./client":{"import":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"require":{"types":"./dist/client/index.d.cts","default":"./dist/client/index.cjs"}},"./adapters":{"import":{"types":"./dist/adapters/index.d.ts","default":"./dist/adapters/index.js"},"require":{"types":"./dist/adapters/index.d.cts","default":"./dist/adapters/index.cjs"}}},"scripts":{"dev":"tsup --watch","lint":"eslint .","test":"npm run test:unit","build":"tsup","verify":"npm run typecheck && npm run typecheck:tests && npm run typecheck:examples && npm run lint && npm run test:coverage && npm run build && npm run test:package","test:unit":"vitest run tests/unit","typecheck":"tsc --noEmit","test:watch":"vitest","test:package":"node scripts/smoke-package.mjs","test:coverage":"vitest run tests/unit --coverage","prepublishOnly":"npm run verify","typecheck:tests":"tsc -p tsconfig.test.json","test:integration":"vitest run tests/integration","typecheck:examples":"tsc -p tsconfig.examples.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:00c10562-6246-4fc7-8371-75990d6e311c"}},"_resolved":"/home/runner/work/chat-sdk/chat-sdk/package/assinafy-chat-sdk-2.2.1.tgz","overrides":{"esbuild":"0.28.2"},"_integrity":"sha512-oNDY0+TjjX4QIAQsMP3Q4VwMlnVWapWecWeeXXHV790U2+KOsxOG7m6OQzJ2j6GCgJq6+w4wuZrG7uu0kYxrtw==","repository":{"url":"git+https://github.com/assinafy/chat-sdk.git","type":"git"},"_npmVersion":"11.19.0","description":"Chat SDK for Assinafy — build chat bots and conversational integrations on top of the Assinafy document-signing API.","directories":{},"sideEffects":false,"_nodeVersion":"24.21.0","allowScripts":{"esbuild@0.28.2":true,"fsevents@2.3.3":true},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"npm@11.19.0","devDependencies":{"tsx":"^4.23.12","tsup":"^8.5.1","dotenv":"^17.4.2","eslint":"^10.9.1","vitest":"^4.1.11","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/node":"^24.13.3","@vitest/coverage-v8":"^4.1.11","@typescript-eslint/parser":"^8.68.0","@typescript-eslint/eslint-plugin":"^8.68.0"},"_npmOperationalInternal":{"tmp":"tmp/chat-sdk_2.2.1_1790211034379_0.6379688990303019","host":"s3://npm-registry-packages-npm-production"}},"2.3.0":{"_id":"@assinafy/chat-sdk@2.3.0","bugs":{"url":"https://github.com/assinafy/chat-sdk/issues"},"dist":{"shasum":"b03c1f4fea6109d24405931e3178a499762e262f","tarball":"https://registry.npmjs.org/@assinafy/chat-sdk/-/chat-sdk-2.3.0.tgz","fileCount":46,"integrity":"sha512-c+71YpYxa2PstLWsszdAEEXv/uCo85FtUQS6pX5a6u7FVz63DDW68hsUeL31q3Np8DYkunIvKkRexDQg4H2AUg==","signatures":[{"sig":"MEYCIQDUsgchI2jveAnUH9dqWC6/Hw2QgTlDQW5UocgRWTncYQIhAPy+znO2OfZwIQAqd5/1bq9JuBnRhj5b6+h4oncqWY16","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDKvunIZAmLSG06EvQjFpT4KMOKXkhVPwWInLlpmvGT/QIhAL+3PPybDbOpafBRlJqTA+TyJnI3uTIfVEqsl5b+6MmN"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@assinafy%2fchat-sdk@2.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":2128704},"main":"./dist/index.cjs","name":"@assinafy/chat-sdk","type":"module","_from":"file:package/assinafy-chat-sdk-2.3.0.tgz","types":"./dist/index.d.ts","author":{"name":"Assinafy"},"module":"./dist/index.js","engines":{"node":">=24"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./ai":{"import":{"types":"./dist/ai/index.d.ts","default":"./dist/ai/index.js"},"require":{"types":"./dist/ai/index.d.cts","default":"./dist/ai/index.cjs"}},"./cards":{"import":{"types":"./dist/cards/index.d.ts","default":"./dist/cards/index.js"},"require":{"types":"./dist/cards/index.d.cts","default":"./dist/cards/index.cjs"}},"./state":{"import":{"types":"./dist/state/index.d.ts","default":"./dist/state/index.js"},"require":{"types":"./dist/state/index.d.cts","default":"./dist/state/index.cjs"}},"./client":{"import":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"require":{"types":"./dist/client/index.d.cts","default":"./dist/client/index.cjs"}},"./adapters":{"import":{"types":"./dist/adapters/index.d.ts","default":"./dist/adapters/index.js"},"require":{"types":"./dist/adapters/index.d.cts","default":"./dist/adapters/index.cjs"}}},"license":"MIT","scripts":{"dev":"tsup --watch","lint":"eslint .","test":"npm run test:unit","build":"tsup","verify":"npm run typecheck && npm run typecheck:tests && npm run typecheck:examples && npm run lint && npm run test:coverage && npm run build && npm run test:package","test:unit":"vitest run tests/unit","typecheck":"tsc --noEmit","test:watch":"vitest","test:package":"node scripts/smoke-package.mjs","test:coverage":"vitest run tests/unit --coverage","prepublishOnly":"npm run verify","typecheck:tests":"tsc -p tsconfig.test.json","test:integration":"vitest run tests/integration","typecheck:examples":"tsc -p tsconfig.examples.json"},"version":"2.3.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:00c10562-6246-4fc7-8371-75990d6e311c"}},"homepage":"https://github.com/assinafy/chat-sdk#readme","keywords":["assinafy","chat","sdk","bot","esignature","signature","documents"],"_resolved":"/home/runner/work/chat-sdk/chat-sdk/package/assinafy-chat-sdk-2.3.0.tgz","overrides":{"esbuild":"0.28.2"},"_integrity":"sha512-c+71YpYxa2PstLWsszdAEEXv/uCo85FtUQS6pX5a6u7FVz63DDW68hsUeL31q3Np8DYkunIvKkRexDQg4H2AUg==","repository":{"url":"git+https://github.com/assinafy/chat-sdk.git","type":"git"},"_npmVersion":"11.19.0","description":"Chat SDK for Assinafy — build chat bots and conversational integrations on top of the Assinafy document-signing API.","directories":{},"maintainers":[{"name":"billm950","email":"billm@billm.org"}],"sideEffects":false,"_nodeVersion":"24.21.0","allowScripts":{"esbuild@0.28.2":true,"fsevents@2.3.3":true},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"npm@11.19.0","devDependencies":{"tsx":"^4.23.12","tsup":"^8.5.1","dotenv":"^17.4.2","eslint":"^10.9.1","vitest":"^4.1.11","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/node":"^24.13.3","@vitest/coverage-v8":"^4.1.11","@typescript-eslint/parser":"^8.68.0","@typescript-eslint/eslint-plugin":"^8.68.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/chat-sdk_2.3.0_1790365854152_0.36328196212119934"}}},"time":{"created":"2026-05-28T17:39:01.479Z","modified":"2026-09-25T19:50:54.711Z","0.1.0":"2026-05-28T17:39:02.028Z","1.0.0":"2026-06-05T17:27:34.377Z","1.1.0":"2026-07-17T18:52:11.248Z","2.0.0":"2026-08-20T16:35:17.576Z","2.0.1":"2026-08-27T15:13:15.686Z","2.0.2":"2026-08-27T22:48:18.844Z","2.2.0":"2026-09-23T23:52:37.257Z","2.2.1":"2026-09-24T00:50:34.498Z","2.3.0":"2026-09-25T19:50:54.256Z"},"bugs":{"url":"https://github.com/assinafy/chat-sdk/issues"},"author":{"name":"Assinafy"},"license":"MIT","homepage":"https://github.com/assinafy/chat-sdk#readme","keywords":["assinafy","chat","sdk","bot","esignature","signature","documents"],"repository":{"url":"git+https://github.com/assinafy/chat-sdk.git","type":"git"},"description":"Chat SDK for Assinafy — build chat bots and conversational integrations on top of the Assinafy document-signing API.","maintainers":[{"name":"billm950","email":"billm@billm.org"}],"readme":"# @assinafy/chat-sdk\n\n*Português · [Read in English](README.en.md)*\n\n[![CI](https://github.com/assinafy/chat-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/assinafy/chat-sdk/actions/workflows/ci.yml)\n[![CodeQL](https://github.com/assinafy/chat-sdk/actions/workflows/codeql.yml/badge.svg)](https://github.com/assinafy/chat-sdk/actions/workflows/codeql.yml)\n[![npm version](https://img.shields.io/npm/v/@assinafy/chat-sdk.svg)](https://www.npmjs.com/package/@assinafy/chat-sdk)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\nSDK TypeScript para a API de assinatura de documentos Assinafy v1 — plataforma\nbrasileira de assinatura eletrônica — e para construir fluxos de assinatura\nconversacionais sobre ela.\n\nEste documento foi escrito para ser lido de ponta a ponta. Começa pelo conteúdo\ndo pacote, instala e configura o SDK, cobre as duas formas de autenticação, faz\na primeira requisição, explica como respostas e erros se comportam, percorre o\nciclo de vida completo da assinatura de documentos e só então passa às camadas\nde chat, cards e IA. Cada seção assume a anterior.\n\n---\n\n## 1. O que há no pacote\n\nO SDK é um pacote com duas metades que podem ser usadas de forma independente.\n\n**O cliente da API** cobre a API REST Assinafy v1: **93 operações em 71\ncaminhos**, agrupadas em doze recursos — contas, autenticação, OAuth, usuários,\nsignatários, documentos, tags, templates, assignments, campos, o fluxo de\nassinatura do signatário e webhooks. Toda operação é tipada, e o transporte\ncuida da autenticação, do envelope de resposta, da paginação, dos metadados de\nrate limit, dos retries e do mapeamento de erros.\n\n**A camada de chat** transforma essas operações em fluxos conversacionais: um\norquestrador `Chat` que roteia mensagens de entrada para handlers, uma visão\n`Thread` entregue a cada handler, um contrato de adapter para conectar\nplataformas de mensagem, um contrato de estado plugável para assinaturas e\narmazenamento por thread, um sistema declarativo de cards com renderizadores de\ntexto, Markdown e HTML, e 36 descritores de ferramenta neutros de provedor para\ntool calling de LLM.\n\nDois documentos de referência acompanham este e vão mais fundo:\n\n- **[Referência da API](./docs/API_REFERENCE.md)** — todo método público com seu\n  modo de autenticação, payloads completos de requisição e resposta, as\n  superfícies de chat, card, adapter e estado, e o catálogo completo de\n  ferramentas de IA.\n- **[Índice de operações](./docs/API_COVERAGE.md)** — as 93 operações publicadas\n  mapeadas ao método do SDK, mais os pontos em que a superfície HTTP do SDK vai\n  além do documento publicado.\n\nO contrato upstream com autoridade é a\n[documentação oficial da API Assinafy](https://api.assinafy.com.br/v1/docs).\n\n---\n\n## 2. Requisitos e escopo de runtime\n\nAplicações de servidor e os exemplos deste repositório têm como alvo o\n**Node.js 24 LTS**, que é o que o campo `engines` do pacote exige e o que a CI\nroda. A API só aceita HTTPS com TLS 1.2 ou superior, o padrão de todos os\nruntimes listados abaixo.\n\nNem todo ponto de entrada precisa de Node. O pacote publica subcaminhos\nfocados, para que um bundle de browser ou edge possa trazer apenas o cliente\nREST:\n\n| Import | Conteúdo | Roda em |\n| --- | --- | --- |\n| `@assinafy/chat-sdk/client` | Cliente REST Assinafy v1 e OAuth | Node 24, Bun, Deno e browsers com as APIs Fetch padrão |\n| `@assinafy/chat-sdk/cards` | Tipos, builders e renderizadores de card | Qualquer runtime JavaScript |\n| `@assinafy/chat-sdk/state` | Contrato de estado e implementação em memória | Qualquer runtime JavaScript |\n| `@assinafy/chat-sdk/ai` | Descritores de ferramenta e helpers de mensagem | Qualquer runtime JavaScript |\n| `@assinafy/chat-sdk/adapters` | Contratos de adapter, adapter em memória, verificação HMAC | Node.js — a verificação de webhook importa `node:crypto` |\n| `@assinafy/chat-sdk` | Tudo acima | Node.js, porque a raiz reexporta os helpers de webhook |\n\nA regra prática: se um bundle só conversa com a API, importe\n`@assinafy/chat-sdk/client` e nada mais. Tanto ES modules quanto CommonJS são\npublicados, com declarações de tipo para cada um. O pacote não tem nenhuma\ndependência de runtime.\n\n---\n\n## 3. Instalação\n\nPelo npm:\n\n```bash\nnpm install @assinafy/chat-sdk\n```\n\nTodo release também é publicado no GitHub Packages. Para instalar de lá, aponte\no escopo `@assinafy` para aquele registry em um `.npmrc` local do projeto:\n\n```ini\n@assinafy:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}\n```\n\nDepois instale normalmente — o mapeamento de escopo faz o roteamento:\n\n```bash\nnpm install @assinafy/chat-sdk\n```\n\n---\n\n## 4. Configuração e autenticação\n\nO cliente autentica com uma chave de API de vida longa enviada como\n`X-Api-Key`, ou com um token de acesso bearer — obtido de `auth.login()` ou um\ntoken de acesso OAuth. Os dois são mutuamente exclusivos; passar ambos lança\n`ConfigurationError`.\n\n```ts\nimport { AssinafyClient } from \"@assinafy/chat-sdk/client\";\n\nnew AssinafyClient({ apiKey: \"chave-de-api\" });\nnew AssinafyClient({ accessToken: \"token-bearer\" });\n```\n\nUma chave de API age sobre a **sua própria** conta. Se, em vez disso, seu\nproduto é conectado por outras pessoas às **contas delas**, use OAuth — é a\npróxima seção.\n\n`AssinafyClient.fromEnv()` lê as mesmas configurações do ambiente, que é o que\nos exemplos e a suíte de testes usam:\n\n| Variável | Padrão | Propósito |\n| --- | --- | --- |\n| `ASSINAFY_API_KEY` | nenhum | Chave de API, enviada como `X-Api-Key` |\n| `ASSINAFY_ACCESS_TOKEN` | nenhum | Token bearer, usado no lugar da chave de API |\n| `ASSINAFY_BASE_URL` | `https://api.assinafy.com.br/v1` | Use `https://sandbox.assinafy.com.br/v1` para o sandbox |\n| `ASSINAFY_ACCOUNT_ID` | nenhum | ID de conta padrão, legível de volta em `client.accountId` |\n\nConstruir sem credencial nenhuma é **deliberado e suportado**: um cliente não\nautenticado é o que você usa para `auth.login()`, para verificação pública de\ndocumento, para os endpoints do signatário que autenticam com um código de\nacesso, e para todo o fluxo OAuth.\n\nAlém das credenciais, o construtor aceita configurações de transporte — um\n`fetch` customizado, `maxRetries`, `retryBaseDelayMs`, sobrescrita de\n`userAgent` e um observador `onRateLimit`. Todos são opcionais e todos são\nrepassados ao `HttpClient` subjacente.\n\nNunca comite credenciais. Use uma conta de sandbox dedicada para\ndesenvolvimento e rotacione qualquer chave exposta.\n\n---\n\n## 5. Conectando contas de terceiros com OAuth\n\nA seção 4 cobre automatizar a **sua própria** conta. Quando seu produto é\ninstalado por *outras pessoas* nas contas *delas*, elas não devem entregar uma\nchave de API a você: use OAuth, e elas aprovam um conjunto específico de\npermissões que podem revogar a qualquer momento.\n\n| | Chave de API | OAuth |\n| --- | --- | --- |\n| Age sobre | Sua própria conta | A conta de outra pessoa, com a permissão dela |\n| Pode fazer | Tudo o que sua conta pode | Só os escopos aprovados |\n| A pessoa pode desligar | Não | Sim, a qualquer momento |\n| Escolha quando | Você automatiza a sua conta | Outras pessoas conectam seu produto às contas delas |\n\nRegistre a aplicação em **Configurações → Aplicações OAuth**. Você recebe um\n`client_id` e — para uma aplicação *confidencial*, cujo código roda num\nservidor seu — um `client_secret` exibido **uma única vez**. Uma aplicação\n*pública* (mobile, single-page) não recebe segredo e autentica só com PKCE. As\nURIs de redirecionamento precisam ser `https://` e são comparadas caractere a\ncaractere.\n\nDois hosts participam de propósito: a tela de consentimento fica em\n`auth.assinafy.com.br` e só recebe um navegador, enquanto os endpoints de\ntoken, revogação e userinfo ficam em `api.assinafy.com.br` e só são chamados de\nservidor para servidor. Ambos vêm da descoberta automática, então nada fica\nfixo no código.\n\n### O ciclo completo\n\n```ts\nimport { AssinafyClient, OAuthError } from \"@assinafy/chat-sdk/client\";\n\n// Nenhuma credencial é necessária — os endpoints OAuth autenticam a aplicação.\nconst client = new AssinafyClient();\n\n// 1. Antes de redirecionar: gere o par PKCE e o state, e monte a URL de consentimento.\nconst request = await client.oauth.createAuthorizationUrl({\n  clientId: process.env.ASSINAFY_CLIENT_ID!,\n  redirectUri: \"https://meuapp.example/oauth/callback\",\n  scopes: [\"documents:read\", \"documents:write\", \"offline_access\"],\n});\nsession.oauth = request;          // guarde o objeto inteiro: state, issuer, codeVerifier\nresponse.redirect(request.url);   // navegação de página inteira, não fetch()\n```\n\n```ts\n// 2. Em https://meuapp.example/oauth/callback — valida state e iss por você, e\n//    transforma um consentimento recusado em OAuthError(\"access_denied\").\nconst { code } = client.oauth.readAuthorizationCallback(query, session.oauth);\n\n// 3. Troque o código. Ele é de uso único e expira 60 segundos após o\n//    redirecionamento, então faça isso imediatamente.\nconst tokens = await client.oauth.exchangeCode({\n  code,\n  codeVerifier: session.oauth.codeVerifier,\n  redirectUri: \"https://meuapp.example/oauth/callback\",\n  clientId: process.env.ASSINAFY_CLIENT_ID!,\n  clientSecret: process.env.ASSINAFY_CLIENT_SECRET, // omita numa aplicação pública\n});\n\n// 4. O token cobre exatamente uma conta. Pergunte qual e guarde o id dela.\nconst conectado = new AssinafyClient({ accessToken: tokens.access_token });\nconst [conta] = await conectado.accounts.list();\n```\n\nA partir daqui `conectado` é um cliente comum: todo recurso deste README\nfunciona igual, limitado aos escopos que a pessoa aprovou.\n\n### Mantendo a conexão e desconectando\n\n```ts\n// Renove antes de completar uma hora (exige offline_access).\nconst renovado = await client.oauth.refreshToken({\n  refreshToken: armazenado.refresh_token!,\n  clientId: process.env.ASSINAFY_CLIENT_ID!,\n  clientSecret: process.env.ASSINAFY_CLIENT_SECRET,\n});\nawait salvar(renovado);                 // antes de qualquer outro uso da resposta\n// O token de acesso também mudou: monte o cliente com o novo.\nconst conectado = new AssinafyClient({ accessToken: renovado.access_token });\n\n// Quando a pessoa desconectar: revogue o refresh token salvo mais recentemente,\n// nunca uma cópia lida antes do último refresh — essa já está aposentada.\nconst atual = await carregar();\nawait client.oauth.revokeToken({\n  token: atual.refresh_token!,\n  tokenTypeHint: \"refresh_token\",\n  clientId: process.env.ASSINAFY_CLIENT_ID!,\n  clientSecret: process.env.ASSINAFY_CLIENT_SECRET,\n});\n```\n\nCinco regras decidem se uma integração OAuth é confiável:\n\n- **Uma conexão é uma conta.** Qualquer outra conta responde `403`, mesmo uma da\n  qual a mesma pessoa participa. Um cliente com várias contas conecta cada uma\n  separadamente.\n- **Tokens de acesso duram uma hora; refresh tokens rotacionam.** Cada refresh\n  devolve um novo refresh token e aposenta o anterior. Um refresh token\n  reapresentado é indistinguível de um roubado, então o servidor encerra a\n  conexão inteira. Persista o novo token **antes** de qualquer outro uso da\n  resposta, e nunca rode dois refreshes ao mesmo tempo para uma conexão, nem\n  um enquanto uma desconexão a revoga.\n- **Envie cada refresh token uma única vez.** Um timeout, uma conexão caída ou\n  um `5xx` podem chegar depois que o servidor já rotacionou o token, então\n  trate-os como \"pode ter funcionado\": releia o token armazenado e, se ainda\n  for o enviado, nunca o envie de novo — marque a conexão como inutilizável e\n  peça a reconexão. Só um token mais novo no seu armazenamento é seguro. A\n  única falha que pode ser repetida com o mesmo token é a que comprovadamente\n  ocorreu antes do envio: um `ConfigurationError`, uma falha de DNS, uma\n  conexão recusada ou um erro no handshake TLS. `refreshToken` nunca repete a\n  requisição por conta própria, e um sucesso sem um novo refresh token gera\n  `OAuthError` `invalid_grant`.\n- **Um refresh token vale 30 dias, e cada refresh devolve um novo com mais 30\n  dias.** A conexão só expira se a sua aplicação passar 30 dias sem renovar;\n  depois disso, a pessoa precisa conectar de novo.\n- **Peça o mínimo.** A pessoa aprova tudo o que você pediu ou nada;\n  `offline_access` é o que dá o refresh token, e `openid` o `id_token`. Leia o\n  `scope` da resposta em vez de assumir.\n\n### Escopos\n\n| Escopo | Concede |\n| --- | --- |\n| `documents:read` | Ler documentos, signatários e situação de assinatura |\n| `documents:write` | Criar documentos e enviá-los para assinatura — consome créditos de notificação |\n| `templates:read` / `templates:write` | Ler / gerenciar templates |\n| `account:read` | Ler o nome e as configurações da conta |\n| `webhooks:write` | Configurar e desativar a assinatura de webhooks da conta |\n| `openid`, `profile`, `email` | Identificar a pessoa; `oauth.getUserInfo()` devolve os claims |\n| `offline_access` | Receber um refresh token |\n\nCobrança, membros da conta, credenciais e administração da plataforma nunca\nficam disponíveis a uma aplicação, qualquer que seja o escopo.\n\n### Erros\n\n`OAuthError` estende `ApiError` e acrescenta `error`, `errorDescription` e —\npara uma permissão faltante — `scope`:\n\n```ts\ntry {\n  await conectado.documents.upload(accountId, arquivo);\n} catch (error) {\n  if (error instanceof OAuthError && error.error === \"insufficient_scope\") {\n    // error.scope nomeia a permissão com a qual reconectar.\n  }\n}\n```\n\n`access_denied` significa que a pessoa recusou; `invalid_grant` significa um\ncódigo ou refresh token expirado, reapresentado ou divergente, e exige uma nova\nautorização; `invalid_client` significa credenciais de aplicação erradas. Um\ntoken de acesso expirado responde `401` comum — renove e, se falhar, peça a\nreconexão.\n\nO [`examples/oauth-connect.ts`](https://github.com/assinafy/chat-sdk/blob/main/examples/oauth-connect.ts)\ntraz o fluxo inteiro como um servidor `node:http` executável.\n\n> **Disponibilidade.** O OAuth é servido pelo host de produção. O sandbox não o\n> expõe, então desenvolva a parte OAuth da integração contra produção, usando\n> uma conta de teste dedicada.\n\n> **Migrando para a 2.3.0.** `readAuthorizationCallback` recusa um callback sem\n> `iss`, inclusive os retornos com `?error=`, com `OAuthError`\n> `invalid_request`; quando o objeto passado não tem `issuer`, o `iss` precisa\n> ser `https://auth.assinafy.com.br`. Guarde a requisição inteira devolvida por\n> `createAuthorizationUrl` — não só o `state` — e passe-a de volta.\n> `refreshToken` gera `OAuthError` `invalid_grant` para um sucesso sem um novo\n> refresh token; peça a reconexão.\n\n---\n\n## 6. A primeira requisição\n\nTodo método de recurso recebe os identificadores de que precisa como argumentos\nexplícitos, de modo que o próprio cliente permanece sem estado:\n\n```ts\nimport { AssinafyClient, ApiError } from \"@assinafy/chat-sdk/client\";\n\nconst accountId = process.env.ASSINAFY_ACCOUNT_ID;\nif (!accountId) throw new Error(\"ASSINAFY_ACCOUNT_ID é obrigatório\");\nconst apiKey = process.env.ASSINAFY_API_KEY;\nif (!apiKey) throw new Error(\"ASSINAFY_API_KEY é obrigatório\");\n\nconst client = new AssinafyClient({\n  apiKey,\n  accountId,\n  baseUrl: \"https://sandbox.assinafy.com.br/v1\", // omita para produção\n});\n\ntry {\n  const { data: documentos, pagination } = await client.documents.list(accountId, {\n    status: \"pending_signature\",\n    perPage: 20,\n  });\n  console.log(documentos, pagination);\n} catch (error) {\n  if (error instanceof ApiError) {\n    console.error(error.status, error.method, error.path, error.body);\n  }\n  throw error;\n}\n```\n\nO `{ data, pagination }` desestruturado e o ramo `ApiError` são consequências\nde como o transporte funciona, que é a próxima seção.\n\n---\n\n## 7. Como respostas, paginação, downloads e erros se comportam\n\nEntender estes quatro comportamentos torna o resto do SDK previsível, porque\ntodo método de recurso os herda.\n\n### Respostas vêm desembrulhadas\n\nA Assinafy envolve respostas JSON em um envelope:\n\n```json\n{ \"status\": 200, \"message\": \"Success\", \"data\": { \"id\": \"id-do-recurso\" } }\n```\n\nO transporte o remove. Um método de recurso devolve `data` diretamente — aqui,\n`{ \"id\": \"id-do-recurso\" }`. Um envelope válido sem `data`, e qualquer `204`,\nresolvem para `undefined`; os métodos documentados como `void` são exatamente\nesses.\n\nOs endpoints OAuth e os documentos `.well-known` são a exceção deliberada: eles\nrespondem JSON plano, sem envelope, para que bibliotecas OAuth padrão\nfuncionem. O transporte reconhece a diferença e repassa o objeto intacto.\n\n### Listas são paginadas\n\nMétodos que devolvem uma coleção devolvem tanto os itens quanto os metadados de\npaginação lidos dos cabeçalhos `X-Pagination-*`:\n\n```json\n{\n  \"data\": [{ \"id\": \"id-do-recurso\" }],\n  \"pagination\": { \"currentPage\": 1, \"pageCount\": 1, \"perPage\": 20, \"totalCount\": 1 }\n}\n```\n\n`page` precisa ser um inteiro positivo e `perPage` precisa estar entre 1 e 100;\no SDK rejeita valores fora desses limites antes de enviar a requisição, e\ncodifica `perPage` como o `per-page` da API. Quando você escreveria um laço de\npaginação, `documents.iterate()` e `signers.iterate()` são iteradores\nassíncronos que percorrem todas as páginas:\n\n```ts\nfor await (const documento of client.documents.iterate(accountId, { status: \"certificated\" })) {\n  console.log(documento.name);\n}\n```\n\n### Downloads devolvem a resposta crua\n\nMétodos de artefato devolvem o `Response` nativo, para que você faça stream,\nbuffer ou pipe conforme a situação exigir:\n\n```ts\nconst response = await client.documents.download(documentId, \"original\");\nconst bytes = new Uint8Array(await response.arrayBuffer());\n```\n\nSó o corpo bem-sucedido fica sem parse. Um download que falha continua lançando\n`ApiError` antes de qualquer `Response` ser devolvido. Os nomes canônicos de\nartefato são `original`, `certificated`, `certificate-page`, `pades` e\n`bundle`; miniaturas e imagens de página individuais têm métodos próprios.\n\n### Erros são tipados, e só requisições seguras têm retry\n\nToda resposta não-2xx lança `ApiError`, carregando `status`, o `body` já\nparseado, o `path` requisitado e o `method`. Códigos de acesso de signatário\nque apareçam no caminho são redigidos antes de o erro ser construído.\n\n| Classe de erro | Quando é lançada |\n| --- | --- |\n| `AssinafyError` | Classe base de todo erro que o SDK define |\n| `ConfigurationError` | Base URL, combinação de credenciais, configuração de transporte, argumento de requisição ou adaptador de chat inválido |\n| `ApiError` | Qualquer resposta não-2xx da API |\n| `OAuthError` | Um `ApiError` cuja resposta trouxe um código de erro OAuth — acrescenta `error`, `errorDescription` e `scope` |\n| `NotImplementedError` | Um adapter recebeu uma operação que sua plataforma não suporta |\n| `WebhookSignatureError` | Assinatura de webhook inválida ou fora da janela de replay |\n\nFalhas de rede, `408`, `425`, `429` e alguns `5xx` são repetidos com backoff\nexponencial, respeitando um `Retry-After` do servidor quando existir. Os\nretries valem **apenas** para `GET`, `HEAD` e `OPTIONS`. Requisições que mutam\nnunca são repetidas, porque a API não publica contrato de chave de idempotência\ne um retry silencioso poderia criar uma solicitação de assinatura duplicada.\nAbortar via `RequestInit.signal` cancela também uma espera de retry em curso.\n\nPasse `onRateLimit` para observar os metadados `X-Rate-Limit-*` conforme\nchegam; uma exceção lançada por esse observador é engolida, para que nunca\ntransforme uma requisição bem-sucedida em falha.\n\n---\n\n## 8. O ciclo de vida da assinatura de documentos\n\nCom o transporte entendido, este é o fluxo em torno do qual a API foi\nconstruída. Um documento normalmente passa por estas etapas:\n\n1. Criar ou reutilizar os registros de signatário.\n2. Enviar um PDF — no máximo 25 MB e 2.000 páginas.\n3. Esperar o processamento de metadados quando o fluxo precisar de coordenadas\n   de página.\n4. Estimar o custo do assignment e confirmar que a conta tem os recursos.\n5. Criar o assignment, o que dispara notificação e assinatura.\n6. Acompanhar o progresso por webhook ou por polling limitado.\n7. Baixar o artefato certificado quando o status for `certificated`.\n\nO exemplo abaixo é o fluxo completo de assinatura virtual, com polling por\nclareza. Fluxos em produção devem preferir uma inscrição de webhook, coberta\nmais adiante.\n\n```ts\nimport { readFile } from \"node:fs/promises\";\nimport { AssinafyClient } from \"@assinafy/chat-sdk/client\";\n\nconst client = AssinafyClient.fromEnv();\nconst accountId = client.accountId;\nif (!accountId) throw new Error(\"ASSINAFY_ACCOUNT_ID é obrigatório\");\nif (!process.env.ASSINAFY_API_KEY && !process.env.ASSINAFY_ACCESS_TOKEN) {\n  throw new Error(\"ASSINAFY_API_KEY ou ASSINAFY_ACCESS_TOKEN é obrigatório\");\n}\n\n// 1. Signatários são registros no escopo da conta, reutilizáveis entre documentos.\nconst signatario = await client.signers.create(accountId, {\n  full_name: \"Aline Costa\",\n  email: \"signatario@example.test\",\n});\n\n// 2. Upload. `body` aceita Blob, ArrayBuffer ou Uint8Array — um Buffer do Node\n//    é um Uint8Array, então a saída de `readFile` funciona direto.\nconst documento = await client.documents.upload(accountId, {\n  filename: \"contrato.pdf\",\n  body: await readFile(\"contrato.pdf\"),\n  contentType: \"application/pdf\",\n});\n\n// 3. O processamento de metadados renderiza as imagens e atribui ids de página.\nasync function aguardarStatus(\n  documentId: string,\n  aceitos: ReadonlySet<string>,\n  timeoutMs = 120_000,\n) {\n  const limite = Date.now() + timeoutMs;\n  while (Date.now() < limite) {\n    const atual = await client.documents.get(documentId);\n    if (aceitos.has(atual.status)) return atual;\n    if ([\"failed\", \"expired\", \"rejected_by_signer\", \"rejected_by_user\"].includes(atual.status)) {\n      throw new Error(`Documento entrou em status terminal: ${atual.status}`);\n    }\n    await new Promise((resolve) => setTimeout(resolve, 1_000));\n  }\n  throw new Error(\"Tempo esgotado aguardando o status do documento\");\n}\n\nawait aguardarStatus(documento.id, new Set([\"metadata_ready\"]));\n\n// 4. Calcule o preço antes de se comprometer.\nconst estimativa = await client.assignments.estimateCost(documento.id, {\n  method: \"virtual\",\n  signers: [{ verification_method: \"Email\", notification_methods: [\"Email\"] }],\n});\nif (estimativa.has_sufficient_resources === false) {\n  throw new Error(estimativa.message ?? estimativa.blocking_reason ?? \"Recursos insuficientes\");\n}\n\n// 5. Criar o assignment envia as notificações.\nconst assignment = await client.assignments.create(documento.id, {\n  method: \"virtual\",\n  signers: [\n    {\n      id: signatario.id,\n      verification_method: \"Email\",\n      notification_methods: [\"Email\"],\n      step: 1,\n    },\n  ],\n  message: \"Por favor, assine até sexta-feira.\",\n});\n\nconsole.log(`Assignment criado: ${assignment.id}`);\n\n// 6 e 7. O signatário completa o link entregue pela Assinafy; em produção,\n// retome a partir de um webhook, ou mantenha o polling limitado mostrado aqui.\nawait aguardarStatus(documento.id, new Set([\"certificated\"]));\nconst certificado = await client.documents.download(documento.id, \"certificated\");\nconst bytes = new Uint8Array(await certificado.arrayBuffer());\nconsole.log(`Baixados ${bytes.byteLength} bytes certificados`);\n```\n\nSignatários que compartilham um `step` assinam em paralelo; um passo só é\nativado quando todos os signatários do anterior assinaram. `verification_method`\nseleciona como o signatário comprova identidade e `notification_methods`\nseleciona os canais usados para alcançá-lo — o assunto da próxima subseção.\n\n### Métodos de verificação do signatário\n\nDefinidos por signatário ao criar o assignment. O método de verificação e o de\nnotificação são **acoplados**: envie um, os dois ou nenhum — o lado que faltar é\ninferido. Sem nenhum dos dois, ambos assumem `Email`.\n\n| Método | Como funciona | Notificação | Custo por signatário |\n| --- | --- | --- | --- |\n| `Email` *(padrão)* | Código de uso único (OTP) por e-mail, exigido antes de assinar | `Email` | Gratuito |\n| `Whatsapp` | Código de uso único (OTP) por WhatsApp | `Whatsapp` (obrigatória) | 0,45 crédito, só em planos pagos |\n| `DigitalCertificate` | O signatário assina com o **próprio certificado ICP-Brasil — A1** (arquivo) ou **A3** (token/cartão) — pela extensão de navegador Web PKI, gerando uma assinatura **PAdES qualificada** | `Email` **ou** `Whatsapp` | 2 créditos |\n\nApenas um método de notificação por signatário. O certificado digital exige\nainda o recurso habilitado na conta (planos Standard e Pro), CPF ou CNPJ em\n`government_id`, e que o signatário esteja **sozinho no seu passo**. Um CPF\nexige o certificado daquela pessoa; um CNPJ exige o e-CNPJ daquela empresa.\n\nSignatários por certificado digital não completam pelo endpoint comum de\nassinatura — ele responde `400`. A assinatura deles vem de um handshake de dois\npassos com a extensão Web PKI (`/v1/signers/certificate/start` + `/complete`),\nrotas **somente de produção** que o SDK deliberadamente não embrulha, já que\ndependem da extensão no navegador do signatário.\n\n### Assignments de coleta posicionam campos na página\n\n`method: \"virtual\"` pede ao signatário que assine o documento como está.\n`method: \"collect\"` pede também que ele preencha campos nomeados, e por isso\nexige que o documento chegue a `metadata_ready` antes: cada posicionamento\nreferencia um id de página real e é posicionado em pixels sobre a imagem de\npágina de 150 DPI da Assinafy, medido a partir do canto superior esquerdo.\n\n```ts\nconst pronto = await client.documents.get(documento.id);\nconst pagina = pronto.pages![0]!;\n\nawait client.assignments.create(documento.id, {\n  method: \"collect\",\n  signers: [{ id: signatario.id }],\n  entries: [\n    {\n      page_id: pagina.id,\n      fields: [\n        {\n          signer_id: signatario.id,\n          field_id: definicaoDeCampo.id,\n          display_settings: { left: 69, top: 282, width: 421, height: 40, fontSize: 12 },\n        },\n      ],\n    },\n  ],\n});\n```\n\nAs definições de campo em si são no escopo da conta e reutilizáveis — crie-as\ncom `client.fields.create()`, liste os tipos disponíveis com\n`client.fields.listTypes()` e valide valores antes do envio com\n`client.fields.validate()` ou `validateMultiple()`.\n\n### Templates dispensam o upload\n\nQuando o mesmo documento é enviado repetidamente, um template transforma todas\nas etapas 2 a 5 em uma única chamada. Templates definem papéis em vez de\nsignatários, e instanciar um deles vincula um signatário concreto a cada papel:\n\n```ts\nconst { data: templates } = await client.templates.list(accountId, { perPage: 10 });\nconst template = await client.templates.get(accountId, templates[0]!.id);\nconst papel = template.roles![0]!;\n\nconst criado = await client.templates.instantiate(accountId, template.id, {\n  name: \"nda-acme.pdf\",\n  signers: [{ role_id: papel.id, id: signatario.id }],\n});\n```\n\n`client.templates.estimateCost()` precifica uma instanciação do mesmo modo que\n`assignments.estimateCost()` precifica um assignment direto.\n\n### Tags organizam documentos\n\nTags são rótulos coloridos no nível da conta, anexados a documentos por id:\n\n```ts\nconst tag = await client.tags.create(accountId, { name: \"Jurídico\", color: \"#2563EB\" });\n\nawait client.tags.setForDocument(accountId, documento.id, [tag.id]);   // substitui\nawait client.tags.addToDocument(accountId, documento.id, [tag.id]);    // acrescenta\nawait client.tags.removeFromDocument(accountId, documento.id, tag.id); // remove uma\n```\n\n`documents.list()` aceita um filtro `tags` e devolve apenas documentos que\ncarregam **todas** as tags listadas.\n\n### Webhooks substituem o polling\n\nUma conta tem uma inscrição de webhook. Aponte-a para seu endpoint, liste os\neventos que interessam, e a Assinafy entrega cada um:\n\n```ts\nawait client.webhooks.updateSubscription(accountId, {\n  events: [\"document_ready\", \"signer_signed_document\", \"document_processing_failed\"],\n  is_active: true,\n  url: \"https://example.com/hooks/assinafy\",\n  email: \"ops@example.test\",\n});\n```\n\n`client.webhooks.listEventTypes()` enumera todo evento suportado com sua\ndescrição. Quando uma entrega falha, `listDispatches()` mostra o histórico de\ntentativas com o status HTTP e o corpo da resposta, e `retryDispatch()` reenvia\numa. `inactivate()` interrompe a entrega preservando a URL e a seleção de\neventos — a API não expõe exclusão real de uma inscrição.\n\nVerifique cada entrega antes de confiar nela. O SDK traz as primitivas de HMAC,\nde modo que um adapter só escreve o parsing de cabeçalho da sua plataforma:\n\n```ts\nimport { verifyWebhookSignature } from \"@assinafy/chat-sdk/adapters\";\n\nverifyWebhookSignature({\n  secret: process.env.WEBHOOK_SECRET!,\n  body: corpoCruDaRequisicao,   // os bytes crus, antes do parse de JSON\n  signature: request.headers[\"x-signature\"] as string,\n  timestamp: request.headers[\"x-timestamp\"] as string, // habilita proteção contra replay\n});\n```\n\nEla lança `WebhookSignatureError` em divergência, assinatura malformada,\nsegredo ausente ou timestamp fora da janela de tolerância — cinco minutos por\npadrão. `isValidWebhookSignature()` é a mesma checagem devolvendo um booleano. A\nassinatura precisa ser calculada sobre o corpo cru: fazer parse e re-serializar\no JSON antes muda os bytes e quebra a verificação.\n\n### O fluxo do signatário\n\nTudo acima é o lado do titular da conta. Os signatários autenticam com um\n`signer-access-code` que a Assinafy entregou fora de banda, e nunca com uma\nchave de API — então essas chamadas usam um cliente não autenticado:\n\n```ts\nconst publicClient = new AssinafyClient({ baseUrl: \"https://sandbox.assinafy.com.br/v1\" });\n\nconst eu = await publicClient.signature.self(accessCode);\nawait publicClient.signature.verify(accessCode, otpDoEmail);\nconst contexto = await publicClient.signature.signContext(accessCode);\nawait publicClient.signature.sign(documentId, assignmentId, accessCode, entries);\n```\n\n`SignatureResource` cobre o fluxo inteiro: buscar o próprio registro do\nsignatário, aceitar termos, verificar o código de uso único, enviar imagem de\nassinatura ou rubrica, recuperar o contexto de assinatura, listar e buscar os\ndocumentos do signatário, baixar artefatos, e assinar ou recusar — um documento\npor vez ou vários de uma vez. Signatários por certificado digital precisam\nconfirmar seus dados e aceitar os termos antes de pedir o contexto de\nassinatura, o que `client.signers.confirmDataForDocument()` faz numa chamada só.\n\nDocumentos também podem ser liberados sem código algum:\n`client.documents.publicGet()` busca um resumo público,\n`client.documents.verify()` valida um hash de assinatura sem credencial, e\n`client.documents.sendPublicToken()` pede à Assinafy que entregue um novo token\nde acesso:\n\n```ts\nawait publicClient.documents.sendPublicToken(documentId, { email: \"signatario@example.test\" });\n```\n\nEssa requisição é enviada exatamente uma vez e nunca repetida, porque pode\ndisparar um e-mail ou uma mensagem de WhatsApp.\n\n### Trate códigos de acesso como credenciais\n\nUm código de acesso de signatário, e qualquer URL que o contenha, é uma\ncredencial bearer para aquele documento. Mantenha ambos fora de logs, analytics,\nmensagens de exceção, controle de versão e qualquer armazenamento visível ao\ncliente que a interface de assinatura não exija. Envie-os apenas por HTTPS,\nevite colocá-los em URLs de redirecionamento de terceiros, defina um\n`Referrer-Policy` restritivo como `no-referrer` nas páginas voltadas ao\nsignatário, e redija query strings antes de registrar caminhos de requisição. O\nSDK já os redige de `ApiError.path`, mas só sua aplicação controla o resto.\nSempre que possível, deixe a Assinafy entregar os links de assinatura pelos\ncanais de notificação configurados em vez de manipular os códigos você mesmo.\n\n---\n\n## 9. Construindo um fluxo de chat\n\nA camada de chat embrulha o mesmo cliente em formato conversacional. Quatro\npeças se encaixam:\n\n- **`Chat`** recebe eventos normalizados e os roteia para seus handlers.\n- **Um adapter** conecta o `Chat` a uma plataforma de mensagens e normaliza os\n  payloads dela. O pacote traz um adapter em memória; adapters de produção\n  implementam o mesmo contrato `ChatAdapter`.\n- **`Thread`** é o handle por conversa que todo handler recebe.\n- **Um backend de estado** guarda as inscrições de thread e dados chave/valor\n  por thread. A implementação em memória vem incluída; backends Redis ou\n  Postgres implementam o mesmo contrato `ChatState`.\n\n```ts\nimport {\n  AssinafyClient,\n  Card,\n  Chat,\n  DocumentPreview,\n  MemoryStateAdapter,\n  createMemoryAdapter,\n} from \"@assinafy/chat-sdk\";\n\nconst client = AssinafyClient.fromEnv();\nif (!process.env.ASSINAFY_API_KEY && !process.env.ASSINAFY_ACCESS_TOKEN) {\n  throw new Error(\"ASSINAFY_API_KEY ou ASSINAFY_ACCESS_TOKEN é obrigatório\");\n}\n\nconst memory = createMemoryAdapter();\nconst chat = new Chat({\n  userName: \"Assinafy Bot\",\n  adapters: { memory },\n  state: new MemoryStateAdapter(),\n  client,\n});\n\nchat.onCommand(\"status\", async (thread, message) => {\n  const documentId = message.text.replace(/^[/!]status\\s*/i, \"\").trim();\n  const documento = await client.documents.get(documentId);\n  await thread.post(\n    Card({\n      title: \"Situação do documento\",\n      children: [\n        DocumentPreview({\n          documentId: documento.id,\n          name: documento.name,\n          status: documento.status,\n          signingUrl: documento.signing_url ?? undefined,\n        }),\n      ],\n    }),\n  );\n});\n\nawait memory.receive({ text: \"/status doc_01J00000000000000000000000\", isMention: true });\nconsole.log(memory.lastSent);\n```\n\nUma mensagem de entrada é oferecida aos handlers registrados numa ordem fixa de\nprioridade, e a primeira categoria que casar vence: comandos de barra\n(`onCommand`), depois casamentos por regex (`onNewMessage`), depois follow-ups\nem uma thread inscrita (`onSubscribedMessage`), depois menções explícitas\n(`onNewMention`) e, por fim, o catch-all (`onFallback`). Cliques de botão e\neventos semelhantes vão para `onAction`.\n\nÉ a terceira regra que faz conversas de vários turnos funcionarem. Chamar\n`thread.subscribe()` marca a thread como uma que o bot está acompanhando, de\nmodo que as mensagens seguintes chegam a `onSubscribedMessage` sem precisar de\noutra menção. `thread.get()`, `set()` e `delete()` guardam dados por thread — o\ndocumento em que a pessoa está trabalhando, por exemplo — pelo mesmo backend de\nestado.\n\n### Cards renderizam em qualquer lugar\n\nUm card é uma estrutura JSON simples, não marcação de plataforma, então a mesma\nmensagem pode ser entregue a uma plataforma de chat que renderiza blocos ricos,\na um e-mail que precisa de HTML e a uma CLI que precisa de texto puro. Quinze\ntipos de elemento estão disponíveis: `card`, `text`, `heading`, `divider`,\n`section`, `fields`, `link-button`, `button`, `actions`, `image`, `table`,\n`select`, `radio-select`, `document-preview` e `signer-status`. Os dois últimos\nsão conveniências específicas da Assinafy.\n\n```ts\nimport {\n  Card, Heading, Text, Divider, Actions, LinkButton, Button,\n  renderText, renderMarkdown, renderHtml,\n} from \"@assinafy/chat-sdk/cards\";\n\nconst mensagem = Card({\n  title: \"Documento enviado\",\n  children: [\n    Heading(2, \"contrato.pdf\"),\n    Text(\"Enviado para signatario@example.test para assinatura.\"),\n    Divider(),\n    Actions([\n      LinkButton({ label: \"Abrir\", url: signingUrl }),\n      Button({ id: \"lembrar\", label: \"Lembrar\", style: \"secondary\" }),\n    ]),\n  ],\n});\n\nrenderText(mensagem);     // SMS, e-mail simples, CLI\nrenderMarkdown(mensagem); // plataformas de chat com Markdown\nrenderHtml(mensagem);     // e-mail HTML, visualizações web\n```\n\nOs builders são exportados tanto com nomes capitalizados (`Card`, `Text`)\nquanto com apelidos minúsculos (`card`, `text`). Um adapter que suporte\nmensagens ricas nativas pode percorrer as mesmas primitivas para emitir o\npróprio formato em vez de usar estes renderizadores. O renderizador HTML escapa\ntodo texto e restringe `href` e `src` a `http`, `https`, `mailto` e `tel`, de\nmodo que uma URL hostil no nome de um documento não vire execução de script.\n\n---\n\n## 10. Dirigindo a API a partir de um LLM\n\n`createChatTools(client)` devolve 36 descritores de ferramenta neutros de\nprovedor — as operações de leitura e escrita de que um assistente\nconversacional realmente precisa. Cada descritor carrega um `name`, uma\n`description`, um JSON Schema exposto tanto como `input_schema` (nome do campo\nna Anthropic) quanto como `parameters` (na OpenAI), e um `execute()` que valida\nseus argumentos antes de chamar o cliente.\n\n```ts\nimport { createChatTools, runTool } from \"@assinafy/chat-sdk/ai\";\n\nconst tools = createChatTools(client, {\n  include: [\"list_documents\", \"get_document\", \"document_activities\"],\n});\n\nconst resultado = await runTool(tools, \"list_documents\", { status: \"pending_signature\" });\n```\n\nAs opções `include` e `exclude` controlam a superfície que o modelo enxerga, e\né assim que se mantém um assistente somente-leitura. Argumentos vindos de um\nmodelo são entrada não confiável, então `execute()` os valida contra o schema —\ntipos, enums, limites, campos obrigatórios e os formatos `email`, `uri` e\n`date-time` — antes de qualquer requisição. Definir `accountId` no cliente, ou\nem `createChatTools`, permite que o modelo o omita em toda chamada.\n\nO SDK nunca importa um pacote de provedor de LLM e nunca roda o laço de\nferramentas por conta própria; sua aplicação mantém o controle da conversa. O\nexemplo [`examples/ai-bot.ts`](https://github.com/assinafy/chat-sdk/blob/main/examples/ai-bot.ts)\nmostra um laço completo de tool call contra a Anthropic usando apenas o `fetch`\nembutido do Node.\n\n---\n\n## 11. Exemplos\n\nOs exemplos importam o código-fonte do repositório diretamente e têm seus tipos\nverificados na CI por `tsconfig.examples.json`:\n\n- [`examples/basic-bot.ts`](https://github.com/assinafy/chat-sdk/blob/main/examples/basic-bot.ts)\n  — um bot `/status` em memória, a menor ligação completa.\n- [`examples/live-cli.ts`](https://github.com/assinafy/chat-sdk/blob/main/examples/live-cli.ts)\n  — um REPL `/docs` e `/status` sobre o sandbox, que valida credenciais antes de\n  iniciar.\n- [`examples/ai-bot.ts`](https://github.com/assinafy/chat-sdk/blob/main/examples/ai-bot.ts)\n  — o laço de tool call com a Anthropic descrito acima.\n- [`examples/oauth-connect.ts`](https://github.com/assinafy/chat-sdk/blob/main/examples/oauth-connect.ts)\n  — o ciclo OAuth completo sobre `node:http`: consentimento, callback, troca do\n  código, uma chamada autenticada, renovação e revogação.\n\nRode um deles com as dependências de desenvolvimento do repositório instaladas:\n\n```bash\nASSINAFY_API_KEY=... \\\nASSINAFY_ACCOUNT_ID=... \\\nASSINAFY_BASE_URL=https://sandbox.assinafy.com.br/v1 \\\nnpx tsx examples/live-cli.ts\n```\n\n`examples/ai-bot.ts` lê ainda `ANTHROPIC_API_KEY` e, opcionalmente,\n`ANTHROPIC_MODEL` para sobrescrever seu padrão `claude-sonnet-5`.\n`examples/oauth-connect.ts` lê `ASSINAFY_CLIENT_ID`, `ASSINAFY_REDIRECT_URI` e,\nopcionalmente, `ASSINAFY_CLIENT_SECRET`, e precisa de um túnel https porque\n`http://localhost` não pode ser registrado como URI de redirecionamento.\n\n---\n\n## 12. Desenvolvimento e verificação\n\nUm comando roda tudo o que a CI roda — verificação de tipos do código-fonte,\ndos testes e dos exemplos; lint; testes unitários com limites de cobertura; o\nbuild; e um smoke test que carrega a saída ES module e CommonJS de cada ponto\nde entrada e confere que as exportações batem:\n\n```bash\nnpm run verify\n```\n\nA suíte ao vivo é separada porque conversa com a rede. Ela tem duas metades: a\nsuíte de sandbox, que precisa de credenciais e se pula sozinha sem elas, e uma\nsuíte de contrato sem credenciais, que lê o documento OpenAPI de produção e os\nendpoints públicos de descoberta OAuth.\n\n```bash\nnpm run test:integration\n```\n\nA metade de sandbox cria e apaga recursos descartáveis — signatários,\ndocumentos, campos, tags e uma conta temporária — e exercita CRUD de conta,\nupload de logo e mutação de webhook. Rode-a apenas contra uma conta de sandbox\ndedicada, nunca produção; a suíte recusa qualquer base URL que não seja o host\nde sandbox.\n\nDois testes ficam atrás de `ASSINAFY_TEST_NOTIFICATIONS=1` porque fazem a\nAssinafy enviar notificações reais: instanciação de template e o caminho feliz\ncompleto de assinatura. Habilitá-los exige também\n`ASSINAFY_TEST_EMAIL_PRIMARY` e `ASSINAFY_TEST_EMAIL_SECONDARY`.\n\n| Variável | Padrão | Propósito |\n| --- | --- | --- |\n| `ASSINAFY_TEST_NOTIFICATIONS` | `0` | Defina `1` apenas para uma execução que pode enviar notificações no sandbox |\n| `ASSINAFY_TEST_EMAIL_PRIMARY` | nenhum | Primeiro destinatário; obrigatório só quando as notificações estão habilitadas |\n| `ASSINAFY_TEST_EMAIL_SECONDARY` | nenhum | Segundo destinatário; mesma condição |\n\nTestes unitários e a verificação de tipos dos exemplos não precisam de rede nem\nde credenciais.\n\nA CI roda verificação de tipos, lint, testes unitários e empacotamento em todo push e\npull request. Tags de release repetem a verificação e publicam o mesmo artefato\nno npm com procedência OIDC e no GitHub Packages.\n\n---\n\n## Licença\n\nMIT — veja [LICENSE](./LICENSE).\n","readmeFilename":"README.md"}