Python SDK for Nevo — the inbound event gateway.
Project description
Nevo Python Library
The official Python library for Nevo — the inbound event gateway for AI agents. Point webhooks, inbound email, and messaging channels at one URL and receive them on a single handler: signature-verified, persisted, and rendered prompt-ready for a model. Traditional backends use the same surface without the rendering step.
Documentation
See the Python quickstart and events reference.
Requirements
- Python 3.10+
Installation
pip install nevo-sdk
The import path stays nevo — only the distribution name carries the -sdk suffix:
from nevo import Nevo
Usage
You need an API key from the Nevo dashboard (starts with nvo_live_).
import asyncio, os
from nevo import Nevo
async def main():
async with Nevo(token=os.environ["NEVO_API_KEY"]) as client:
@client.on_event()
async def handle(event):
# event.prompt_ready is a pre-rendered text version of the
# event — feed it straight to a model, or ignore it and read
# event.data / event.email / event.webhook yourself.
if event.type == "email.received":
await event.reply(text="Got it — on it.")
elif event.type == "webhook.received":
print(event.webhook.method, event.webhook.path)
await client.run()
asyncio.run(main())
For a full worked example — receive email, ask Claude to draft a reply, send it back on the same thread — see the building an agent guide.
For scripts that don't want to manage the event loop:
client.run_sync()
Events
Every event exposes the following fields:
| Field | Type | |
|---|---|---|
id |
str |
Stable across replays |
type |
str |
webhook.received or email.received |
origin |
str |
live or replay |
created_at |
datetime |
Server-side ingest time |
channel |
Channel |
The channel that received the event |
data |
dict |
The raw source payload |
webhook |
WebhookData | None |
Set when type == "webhook.received" |
email |
EmailData | None |
Set when type == "email.received" |
prompt_ready |
str |
Text rendering of the event (useful when the next hop is a model) |
Branch on event.type:
@client.on_event()
async def handle(event):
if event.type == "email.received":
print(event.email.from_, event.email.subject)
elif event.type == "webhook.received":
print(event.webhook.method, event.webhook.path)
Replies
Respond on the same channel the event arrived on. Email replies are threaded into the original conversation automatically.
await event.reply(
text="Thanks for your message.",
subject="Re: API limits", # email-only; defaults to "Re: <original>"
cc=["ops@acme.com"], # email-only
)
Channels that don't accept replies (webhook, cron) raise UnsupportedChannelError client-side — no round-trip.
Configuration
client = Nevo(
token="nvo_live_...",
handler_timeout=30.0,
reconnect_max_backoff=30.0,
http_timeout=10.0,
http_max_retries=3,
logger=None,
)
| Option | Default | Description |
|---|---|---|
token |
— | API key from the Nevo dashboard |
handler_timeout |
30.0 |
Seconds before a handler is considered stuck |
reconnect_max_backoff |
30.0 |
Cap on exponential reconnect delay |
http_timeout |
10.0 |
Per-request timeout for HTTP calls |
http_max_retries |
3 |
Retries for transient HTTP failures |
logger |
logging.getLogger("nevo") |
Your own logging.Logger |
Handler behavior
Handlers must be declared with async def. One handler per client — branch on event.type inside it.
- Returning cleanly means the event was handled.
- Raising logs the exception and continues the stream.
- Exceeding
handler_timeoutis logged as a timeout and the stream continues.
Events are not re-delivered to the SDK on handler failure. Replay them from the dashboard when you need to.
Reconnects
The client reconnects automatically with exponential jittered backoff, capped at reconnect_max_backoff. A 401 from the server raises AuthError and does not retry — fix the key and reconnect.
Logging
The SDK logs under the nevo logger:
import logging
logging.getLogger("nevo").setLevel(logging.DEBUG)
Errors
All exceptions inherit from nevo.NevoError. Catch broadly, act specifically.
| Exception | Raised when | Retryable |
|---|---|---|
AuthError |
API key rejected by the server | No |
ConnectionError |
WebSocket cannot connect after retries | No |
HandlerTimeoutError |
Handler exceeded handler_timeout |
No |
ReplyError |
Base class for reply failures | — |
UnsupportedChannelError |
Channel type doesn't accept replies | No |
RequestError |
Client-side validation or server 4xx | No |
ServerError |
Server 5xx after retries | Yes |
NetworkError |
Network failure after retries | Yes |
from nevo import Nevo, AuthError, ReplyError
try:
asyncio.run(client.run())
except AuthError:
# Fix the key and restart.
...
try:
await event.reply(text="…")
except ReplyError as exc:
if exc.is_transient:
# Enqueue for later retry.
...
Support
- Issues: github.com/nevo-sh/sdk-python/issues
- Email: support@nevo.sh
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nevo_sdk-0.1.4.tar.gz.
File metadata
- Download URL: nevo_sdk-0.1.4.tar.gz
- Upload date:
- Size: 15.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa2f42ca59e0ad9bbd44689999304096e9a26de0f65c90461ca9f15f0b505944
|
|
| MD5 |
c1a1dd002aede88e575057428fc23d23
|
|
| BLAKE2b-256 |
8c017e569a4c2552cb7c700d9231c18e3e40d8776345971d4ab840cafdd92204
|
File details
Details for the file nevo_sdk-0.1.4-py3-none-any.whl.
File metadata
- Download URL: nevo_sdk-0.1.4-py3-none-any.whl
- Upload date:
- Size: 18.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
929e21b3a136775ac56fc16d83552ac59a55f4c7a6cb9e084d39c89c3882ef66
|
|
| MD5 |
a34cc13b839e3acabdf860f7dbfa3de8
|
|
| BLAKE2b-256 |
df61f3a40cf65b206bb83fede84fc88c1869721bfd1dff6b701823c89ecde88f
|