{"_id":"@afini/twin-sdk","name":"@afini/twin-sdk","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@afini/twin-sdk","version":"0.1.0","description":"Official TypeScript SDK for the AfiniTwin B2B API.","type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc -p tsconfig.json","prepublishOnly":"npm run build","lint":"tsc --noEmit"},"keywords":["afini","afinitwin","personality","big-five","ipip-neo","cognitive-profile","ai","llm","system-prompt"],"license":"MIT","author":{"name":"Bilbao AI S.L.","email":"info@afini.ai"},"homepage":"https://afini.ai/afinitwin/api","repository":{"type":"git","url":"git+https://github.com/ricardodevis/afini-twin-sdk-ts.git"},"bugs":{"url":"https://github.com/ricardodevis/afini-twin-sdk-ts/issues","email":"info@afini.ai"},"engines":{"node":">=18"},"publishConfig":{"access":"public"},"devDependencies":{"typescript":"^5.4.0","@types/node":"^20.0.0"},"_id":"@afini/twin-sdk@0.1.0","gitHead":"b5092345578ed14a468f734e6f279f40f4a5dccc","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-V/93KYrN1VU6/ZF/reI+ciLOKvnDMAGWLrxSc5IgR0u0cubuAgPrHL8FplU0PWdJ5gqtkDxJrHj3F5Hr7nVqfw==","shasum":"ee3310b37af656957973f4af5becd43bdac18a75","tarball":"https://registry.npmjs.org/@afini/twin-sdk/-/twin-sdk-0.1.0.tgz","fileCount":8,"unpackedSize":37657,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDLtIVyAaiZuUX6976m/5antx4H0LmtBnUE5HxGzyXkRwIgFbq1zEZXKGDbjEd1bAcLiVwoCwWfht2L5wtVM/qHN14="}]},"_npmUser":{"name":"afiniai","email":"info@afini.ai"},"directories":{},"maintainers":[{"name":"afiniai","email":"info@afini.ai"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/twin-sdk_0.1.0_1778445448606_0.926191580200314"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-10T20:37:28.516Z","0.1.0":"2026-05-10T20:37:28.788Z","modified":"2026-05-10T20:37:29.007Z"},"maintainers":[{"name":"afiniai","email":"info@afini.ai"}],"description":"Official TypeScript SDK for the AfiniTwin B2B API.","homepage":"https://afini.ai/afinitwin/api","keywords":["afini","afinitwin","personality","big-five","ipip-neo","cognitive-profile","ai","llm","system-prompt"],"repository":{"type":"git","url":"git+https://github.com/ricardodevis/afini-twin-sdk-ts.git"},"author":{"name":"Bilbao AI S.L.","email":"info@afini.ai"},"bugs":{"url":"https://github.com/ricardodevis/afini-twin-sdk-ts/issues","email":"info@afini.ai"},"license":"MIT","readme":"# @afini/twin-sdk\n\nOfficial TypeScript SDK for the [AfiniTwin B2B API](https://afini.ai/afinitwin/api).\n\nThe AfiniTwin is a portable cognitive profile (Big Five + 5 supplementary layers) built on the [Afini.ai](https://afini.ai) platform. This SDK gives you typed access to a user's snapshot from your own systems — CRMs, custom assistants, internal pipelines.\n\n## Installation\n\n```bash\nnpm install @afini/twin-sdk\n```\n\nRequires Node ≥ 18 (native `fetch`). For older Node, pass `fetchImpl` from `node-fetch`.\n\n## Get an API key\n\nActive users with a Professional plan on Afini.ai can generate keys at [afini.ai/dashboard/twin/api](https://afini.ai/dashboard/twin/api). The key is shown **once** — store it securely (Vault, Railway secrets, env vars).\n\n## Quick start\n\n```ts\nimport { AfiniTwinClient } from '@afini/twin-sdk';\n\nconst client = new AfiniTwinClient({\n  apiKey: process.env.AFINITWIN_KEY!,\n});\n\n// Identity + plan + remaining quota\nconst me = await client.me();\nconsole.log(`User has ${me.twins.ready} ready snapshots; quota ${me.quota.remaining}/${me.quota.monthlyLimit}`);\n\n// All snapshots\nconst { snapshots } = await client.historic();\n\n// Download the standard preset as Markdown for the latest snapshot\nconst md = await client.preset('estandar', { format: 'md', lang: 'es' });\n```\n\n## Sending data into the user's profile (`twin:write` scope)\n\nIf the API key has the `twin:write` scope, you can seed life-facts and annotations. They go to the user's review queue at `/dashboard/discoveries`; **nothing is injected into the profile until the user approves**.\n\n```ts\nconst result = await client.lifeFacts.create([\n  {\n    category: 'professional',\n    value: 'Trabaja en una startup de IA en Bilbao desde 2023',\n    valence: 'positive',\n    consent: true, // explicit confirmation that you have user consent\n    externalRef: 'crm-12345',\n  },\n]);\nconsole.log(`${result.accepted} candidates queued, see ${result.inboxUrl}`);\n```\n\nFor free-form notes:\n\n```ts\nawait client.annotations.create([\n  { tag: 'observation', text: 'Mostró interés por escalar a Pro', consent: true },\n]);\n```\n\n## Verifying webhook signatures\n\nEvery webhook POST carries an `X-AfiniTwin-Signature: sha256=<hmac>` header. Verify before trusting the payload:\n\n```ts\nimport { verifyWebhookSignature, type WebhookPayload } from '@afini/twin-sdk';\nimport express from 'express';\n\nconst SECRET = process.env.AFINITWIN_WEBHOOK_SECRET!; // whsec_...\n\napp.post('/webhooks/afinitwin', express.raw({ type: 'application/json' }), (req, res) => {\n  const sig = req.header('x-afinitwin-signature');\n  if (!verifyWebhookSignature(req.body, sig, SECRET)) {\n    return res.status(403).end();\n  }\n  const event = JSON.parse(req.body.toString()) as WebhookPayload;\n  switch (event.event) {\n    case 'twin.snapshot.ready':\n      // … pull the new snapshot\n      break;\n    case 'twin.quota.exceeded':\n      // … alert your billing/UX\n      break;\n    default:\n      console.log('event:', event.event);\n  }\n  res.status(200).end();\n});\n```\n\n## Error handling\n\nAll API errors throw `AfiniTwinApiError` with a `status` and structured `body`:\n\n```ts\nimport { AfiniTwinApiError } from '@afini/twin-sdk';\n\ntry {\n  await client.me();\n} catch (err) {\n  if (err instanceof AfiniTwinApiError) {\n    if (err.status === 429 && err.body?.code === 'TIER_QUOTA_EXCEEDED') {\n      // upgrade your B2B tier\n    }\n  }\n  throw err;\n}\n```\n\n## Rate limits\n\n| Endpoint group | Per minute | Per month |\n|----------------|------------|-----------|\n| `/health` | 120 | unlimited |\n| `/me`, `/historic`, `/snapshots/*`, `/preset/*` | 60 | per tier |\n| `/life-facts`, `/annotations` | 30 | per tier |\n\nThe monthly cap is enforced **per user across all keys** based on the B2B tier (Included = 10k, Starter = 100k, Pro = 1M, Enterprise = custom). Hitting the cap returns `429 TIER_QUOTA_EXCEEDED` with `resetsAt` and `upgradeUrl`.\n\n## License\n\nMIT © [Bilbao AI S.L.](https://afini.ai)\n","readmeFilename":"README.md","_rev":"1-7f231a780f784aba30265cd252b4eec1"}