pyclann¶
Python client library for the Clann family-tree API.
Installation¶
Requires Python 3.10+.
Quick start¶
from pyclann import ClannClient
client = ClannClient(
api_url="http://localhost:8090",
auth_url="http://localhost:8081", # ullav-user-management service
)
client.login(email="user@example.com", password="secret")
# Family trees
trees = client.trees.list(owner="alice")
tree = client.trees.create("walsh-family", "Walsh Family", owner="alice")
# Persons
father = client.persons.create(
"Walsh", "Patrick", "Male",
trees=["walsh-family"],
date_of_birth="1820-06-01",
created_by="alice",
)
son = client.persons.create(
"Walsh", "Michael", "Male",
trees=["walsh-family"],
created_by="alice",
)
# Relationships
client.relationships.add(son.id, "Father", father.id)
rels = client.relationships.get(son.id) # RelationshipsResponse
# Life events
client.life_events.create(
father.id, "Born in Galway", "Birth",
date="1820-06-01",
created_by="alice",
)
# Research notes
note = client.notes.create(
"Walsh Family Research",
trees=["walsh-family"],
body="Found records at Galway archives.",
is_shared=True,
created_by="alice",
)
client.notes.create_reply(note.id, "Also check Dublin records.", created_by="alice")
# Profile picture
with open("patrick.jpg", "rb") as f:
client.persons.upload_image(father.id, f.read(), "image/jpeg")
image_bytes = client.persons.get_image(father.id) # public endpoint, no auth needed
Authentication¶
ClannClient authenticates against the ullav-user-management service, which issues the JWT accepted by the Clann server.
api_url— Clann server base URL (e.g.http://clann-server:8090)auth_url— auth service base URL (e.g.http://ullav-user-management:8081); omit if both services share a hostname
Call client.login(email, password) before any other method. Tokens expire according to the server configuration; call login() again to refresh.
Resource clients¶
| Attribute | Resource |
|---|---|
client.trees |
Family tree CRUD, primary flag, team assignment, avatar image |
client.persons |
Person CRUD, tree membership, profile/life-story media |
client.relationships |
Add/remove father/mother/sibling/spouse edges, family-tree view |
client.life_events |
Life event CRUD per person |
client.notes |
Research note CRUD, folder assignment, replies |
client.folders |
Research folder CRUD |
client.chat |
AI chat session and message management |
client.ai_settings |
Per-user AI provider settings (encrypted BYOK) |
Error handling¶
All exceptions inherit from ClannError:
from pyclann import ClannAuthError, ClannNotFoundError, ClannValidationError
try:
tree = client.trees.get("non-existent-tree")
except ClannNotFoundError:
print("tree not found")
except ClannAuthError:
client.login(email, password) # token expired — re-authenticate
except ClannValidationError as e:
print("bad request:", e)
| Exception | HTTP status |
|---|---|
ClannAuthError |
401 / 403, or login() not called |
ClannNotFoundError |
404 |
ClannValidationError |
400 |
ClannServerError |
5xx |
ClannError |
base class |
Record IDs¶
The Clann API uses SurrealDB record IDs of the form "table:ulid", e.g. "person:01jd4a8xyz". All client methods that take an *_id parameter accept either the full form or the bare ULID:
person = client.persons.get("person:01jd4a8xyz") # full ID
person = client.persons.get("01jd4a8xyz") # bare ULID — both work
Note
When specifying a related_id in relationship calls, always pass the full record ID ("person:01jd4a8xyz"), as the server uses it verbatim in URL paths.