nexbasira (Python)
SDK de Python del lado del servidor. Misma superficie de recursos que el SDK de Node, basado en requests para reintentos transparentes, con Session mockeable para las pruebas.
Instalar
pip install nexbasira Requiere Python 3.10+.
Inicializar
from nexbasira import NexBasira
nb = NexBasira(
api_key=os.environ["NB_PUBLIC_KEY"],
api_secret=os.environ["NB_SECRET_KEY"],
# base_url="https://app.nexbasira.com/api/v1/public", # default
# timeout=30, # default
) Recursos
nb.sessions # create / list / retrieve / end / invite
nb.evidence # list / retrieve / signed download URL
nb.whiteboards # list per session
nb.webhooks # register / rotate_secret / test_fire / construct_event
nb.branding # read-only
nb.org # read-only Patrones comunes
Crear e invitar
session = nb.sessions.create(
notes="Vehicle damage claim CL-2026-0042",
scheduled_for="2026-05-23T10:00:00Z",
locale="fr",
)
invite = nb.sessions.invite(
session.id,
recipient_email="alex@policyholder.com",
send_email=True,
)
print(invite.url) # shown ONCE Paginación con iterador
# Auto-pages through all sessions:
for session in nb.sessions.iter(limit=50):
print(session.id, session.status)
# Or one page at a time if you want pagination control:
page = nb.sessions.list_page(limit=25)
# page.data, page.has_more, page.next_cursor POST idempotente
import uuid
session = nb.sessions.create(
notes="...",
idempotency_key=str(uuid.uuid4()),
) Verificar un webhook (Flask)
from flask import Flask, request, abort
from nexbasira import NexBasira, InvalidSignatureError
app = Flask(__name__)
nb = NexBasira(api_key="...", api_secret="...")
@app.post("/nb-webhook")
def webhook():
sig = request.headers.get("NB-Signature", "")
secret = os.environ["NB_WEBHOOK_SECRET"]
try:
# IMPORTANT: pass request.data (raw bytes), NOT request.get_json()
event = nb.webhooks.construct_event(request.data, sig, secret)
except InvalidSignatureError:
abort(401, "bad signature")
if event.type == "session.completed":
on_completed(event.data)
elif event.type == "audit.anchored":
on_anchored(event.data)
return "", 204 FastAPI / async
El SDK es síncrono por diseño (sin async def en los métodos de recurso). En los manejadores de FastAPI, llámelo desde un run_in_threadpool o utilice anyio.to_thread.run_sync():
from fastapi import FastAPI, Request, HTTPException
from anyio.to_thread import run_sync
from nexbasira import InvalidSignatureError
app = FastAPI()
@app.post("/nb-webhook")
async def webhook(req: Request):
body = await req.body()
sig = req.headers.get("nb-signature", "")
try:
event = await run_sync(
nb.webhooks.construct_event, body, sig, WEBHOOK_SECRET,
)
except InvalidSignatureError:
raise HTTPException(401, "bad signature")
return {"ok": True} Errores tipados
from nexbasira import (
AuthenticationError,
PermissionError,
NotFoundError,
RateLimitError,
InvalidSignatureError,
NexBasiraError,
)
try:
nb.sessions.create(...)
except RateLimitError as e:
time.sleep(e.retry_after_seconds)
# retry
except NexBasiraError as e:
log.warning("nb error: %s (status=%d)", e.code, e.status)
raise Pruebas — Session mockeable
El SDK utiliza una requests.Session internamente. Pase la suya propia (o un mock) mediante el argumento http_session para interceptarla en las pruebas:
from unittest.mock import MagicMock
from nexbasira import NexBasira
def test_my_handler():
session = MagicMock()
session.request.return_value.status_code = 201
session.request.return_value.json.return_value = {"id": "sess-1", "status": "created", ...}
nb = NexBasira(api_key="x", api_secret="y", http_session=session)
s = nb.sessions.create(notes="test")
assert s.id == "sess-1"
session.request.assert_called_once() Modelos tipados
Generados a partir de la especificación OpenAPI mediante datamodel-code-generator; importables como dataclasses:
from nexbasira import Session, Evidence, WebhookEvent
def on_completed(session: Session) -> None:
...