Official Python client for letter.app - onboarding email drip campaigns for product teams.
pip install letterappRequires Python 3.8+. Zero runtime dependencies (standard library only).
import os
from letterapp import Letter
letter = Letter(api_key=os.environ["LETTER_API_KEY"]) # Dashboard -> Settings -> API keys
# Tell Letter who your user is (call where users sign up or log in).
letter.identify(
user_id="user_123",
email="alice@example.com",
traits={"name": "Alice", "plan": "free"},
)
# Report something they did.
letter.track(user_id="user_123", event="Signed Up", properties={"source": "web"})
# Required before the process exits so no events are lost.
letter.close()Or use it as a context manager, which flushes on exit:
with Letter(api_key=os.environ["LETTER_API_KEY"]) as letter:
letter.track(user_id="user_123", event="Workspace Created")There is no background time to flush in a serverless handler, so set
flush_at=1 and flush() at the end of each invocation:
letter = Letter(api_key=os.environ["LETTER_API_KEY"], flush_at=1)
def handler(event, context):
letter.track(user_id="user_123", event="Checkout Started")
letter.flush()- Auto-batching - calls are queued and flushed every 100ms or 50 events by a background daemon thread.
- Retries -
429waitsRetry-After;5xxand network errors back off exponentially with jitter, up tomax_retries(default 3). - Idempotent - every call gets a UUID
message_idso retries are deduplicated server-side. - No dependencies - HTTP over the standard library
urllib.
Letter(
api_key,
base_url="https://api.letter.app", # only set for self-hosted / local
flush_at=50, # 1 for serverless
flush_interval=0.1, # seconds
max_retries=3,
timeout=10.0,
on_error=None, # callback(Exception) for bg errors
)
letter.identify(user_id, email=None, traits=None, timezone=None, timestamp=None, message_id=None)
letter.group(user_id, account_id, name=None, traits=None, timestamp=None, message_id=None)
letter.track(user_id, event, properties=None, timestamp=None, message_id=None)
letter.flush() # send queued calls now, block until done
letter.close() # flush + stop the background thread (also runs at exit)Configuration errors and non-retryable API responses raise LetterError
(with .status and .body). Background transport errors are passed to
on_error instead, since they cannot be raised to the caller.
- SDK reference: https://letter.app/docs/python-sdk
- Ingestion API: https://letter.app/docs/api
MIT - see LICENSE.