afini-twin-sdk
Official Python SDK for the AfiniTwin B2B API.
The AfiniTwin is a portable cognitive profile (Big Five + 5 supplementary layers) built on the Afini.ai platform. This SDK gives you typed access to a user's snapshot from your own systems — CRMs, custom assistants, internal pipelines.
Installation
pip install afini-twin-sdk
Requires Python ≥ 3.10. Built on httpx and pydantic v2.
Get an API key
Active users with a Professional plan on Afini.ai can generate keys at afini.ai/dashboard/twin/api. The key is shown once — store it securely.
Quick start
Async
import asyncio
import os
from afini_twin import AfiniTwinClient
async def main():
async with AfiniTwinClient(api_key=os.environ["AFINITWIN_KEY"]) as client:
me = await client.me()
print(f"User has {me['twins']['ready']} ready snapshots; quota {me['quota']['remaining']}/{me['quota']['monthlyLimit']}")
snapshots = await client.historic()
for s in snapshots["snapshots"]:
print(s["id"], s["snapshotDate"])
# Download standard preset as Markdown
md = await client.preset("estandar", format="md", lang="es")
print(md[:200], "…")
asyncio.run(main())
Sync
from afini_twin import AfiniTwinSyncClient
with AfiniTwinSyncClient(api_key=os.environ["AFINITWIN_KEY"]) as client:
me = client.me()
Sending data into the user's profile (twin:write scope)
If your 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.
from afini_twin import AfiniTwinClient, LifeFactInput
async with AfiniTwinClient(api_key=os.environ["AFINITWIN_KEY"]) as client:
result = await client.life_facts_create([
LifeFactInput(
category="professional",
value="Trabaja en una startup de IA en Bilbao desde 2023",
valence="positive",
consent=True,
external_ref="crm-12345",
)
])
print(result["accepted"], "candidates queued ->", result["inboxUrl"])
For free-form notes:
from afini_twin import AnnotationInput
await client.annotations_create([
AnnotationInput(tag="observation", text="Mostró interés por escalar a Pro", consent=True)
])
The pydantic models reject consent != True at validation time — you can't accidentally submit without an explicit confirmation.
Verifying webhook signatures
Every webhook POST carries an X-AfiniTwin-Signature: sha256=<hmac> header. Verify before trusting the payload:
FastAPI
from fastapi import FastAPI, Request, HTTPException
from afini_twin import verify_webhook_signature
app = FastAPI()
SECRET = os.environ["AFINITWIN_WEBHOOK_SECRET"]
@app.post("/webhooks/afinitwin")
async def afinitwin_hook(request: Request):
raw = await request.body()
sig = request.headers.get("x-afinitwin-signature")
if not verify_webhook_signature(raw, sig, SECRET):
raise HTTPException(403)
payload = await request.json()
if payload["event"] == "twin.snapshot.ready":
# … pull the new snapshot
pass
elif payload["event"] == "twin.quota.exceeded":
# … alert your billing
pass
return {"ok": True}
Django / Flask
The function is framework-agnostic: pass the raw body (bytes or str) plus the header value plus the secret.
Error handling
from afini_twin import AfiniTwinClient, AfiniTwinApiError
try:
async with AfiniTwinClient(api_key=key) as client:
await client.me()
except AfiniTwinApiError as e:
if e.status == 429 and e.body and e.body.get("code") == "TIER_QUOTA_EXCEEDED":
# upgrade your B2B tier
...
raise
Rate limits
| Endpoint group | Per minute | Per month |
|---|---|---|
/health |
120 | unlimited |
/me, /historic, /snapshots/*, /preset/* |
60 | per tier |
/life-facts, /annotations |
30 | per tier |
The monthly cap is enforced per user across all keys based on the B2B tier (Included = 10k, Starter = 100k, Pro = 1M, Enterprise = custom).
License
MIT © Bilbao AI S.L.
Release files for afini-twin-sdk 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| afini_twin_sdk-0.1.0.tar.gz | 7.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| afini_twin_sdk-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 16.6 kB
Release files / afini_twin_sdk-0.1.0.tar.gz
| Download URL | afini_twin_sdk-0.1.0.tar.gz |
|---|---|
| Size | 7.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
09fb43a17e1afd5b74b94f91ead7d614d3d1b09d616e0eaf1e22f961a5b4bcc5
|
|
BLAKE2b-256 checksum How to use checksums |
ffb413f0bc8eac420a03179e41607a1a9912ade37333f5f050455d0109786950
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|
Release files / afini_twin_sdk-0.1.0-py3-none-any.whl
| Download URL | afini_twin_sdk-0.1.0-py3-none-any.whl |
|---|---|
| Size | 9.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6f051c8be9c77beb9f2554a0c75a7168ca883e64fb666e2acc3f83a4fb373636
|
|
BLAKE2b-256 checksum How to use checksums |
9d1707aec4f9f4f4757cd44f39b61587096102b36f1221bc40686c980d090f10
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|