Verne Software Python SDK
The official Python library for the Verne Nautilus platform.
Server-side only. API keys carry full service access and must never be used in client-side or browser contexts.
Requirements
Python 3.9 or later.
Installation
pip install vernesoft
Quick Start
from vernesoft import Verne
verne = Verne(
relay=os.environ["VERNE_RELAY_KEY"],
gate=os.environ["VERNE_GATE_KEY"],
)
You can also instantiate services independently if you only need one:
from vernesoft import Relay, Gate
relay = Relay(api_key=os.environ["VERNE_RELAY_KEY"])
gate = Gate(api_key=os.environ["VERNE_GATE_KEY"])
Relay — Webhooks-as-a-Service
Send events to all subscribed endpoints:
msg = verne.relay.messages.send(
event_type="user.created",
payload={"id": "usr_123"},
)
Optional parameters:
msg = verne.relay.messages.send(
event_type="order.placed",
payload={"order_id": "999"},
idempotency_key="evt_abc", # prevent duplicate delivery within 24h
channels=["team-a"], # restrict to specific endpoint channels
)
List previously sent events:
page = verne.relay.messages.list(limit=20, event_type="user.created")
print(page.data) # list[Message]
print(page.has_more) # bool
print(page.next_cursor) # pass to the next call to paginate
Gate — Auth-as-a-Service
Identity Management
Manage your end-users. The tenant_id is automatically scoped to your API key.
# Create a user
identity = verne.gate.identities.create(
schema_id="user",
traits={
"email": "user@example.com",
"custom_data": {"role": "editor"},
},
credentials={"password": {"config": {"password": "StrongPassword123!"}}},
state="active",
)
# Get a user
verne.gate.identities.get(identity.id)
# Update a user (JSON Patch — RFC 6902)
verne.gate.identities.patch(identity.id, [
{"op": "replace", "path": "/traits/custom_data/role", "value": "admin"},
])
# Delete a user
verne.gate.identities.delete(identity.id)
# Activate / deactivate a user (an inactive user cannot log in)
verne.gate.identities.deactivate(identity.id)
verne.gate.identities.activate(identity.id)
# …or set the state explicitly:
verne.gate.identities.set_state(identity.id, "inactive")
# Resend the email verification link
verne.gate.identities.resend_verification(identity.id)
Security Settings
Read or replace the tenant's security settings (passwordless login, TOTP MFA):
security = verne.gate.settings.get_security()
# security.passwordless_enabled, security.mfa_enabled
# Both fields are required — the update is a full replacement, not a merge.
verne.gate.settings.update_security(passwordless_enabled=True, mfa_enabled=False)
Access Tokens
Exchange your long-lived API key for a short-lived access token:
token = verne.gate.tokens.create(
subject="usr_123",
scopes=["gate.tokens.read"], # optional
ttl_seconds=3600, # optional, default 3600, max 86400
)
# token.access_token — attach to downstream requests
# token.expires_at — ISO 8601 expiry
Validate a token:
info = verne.gate.tokens.introspect(token.access_token)
if not info.active:
# token is expired or invalid
pass
Authorization
Check whether a subject is allowed to perform an action:
decision = verne.gate.authorize(
subject="usr_123",
action="relay.messages.read",
resource="tenant:ten_001",
)
if not decision.allowed:
raise PermissionError("Forbidden")
Clockwork — Cron-as-a-Service
Schedule recurring HTTP callbacks with cron expressions, or one-shot jobs at a
future time. The tenant_id is automatically scoped to your API key.
Cron Jobs
# List all cron jobs
jobs = verne.clockwork.jobs.list() # list[CronJob]
# Create a recurring job
job = verne.clockwork.jobs.create(
name="nightly-report",
schedule="0 2 * * *", # standard cron expression
url="https://example.com/hooks/report",
method="POST", # optional, defaults server-side
headers={"X-Api-Key": "secret"}, # optional
body='{"kind":"report"}', # optional
)
# Partially update a job — only the fields you pass are changed
verne.clockwork.jobs.update(job.id, schedule="0 3 * * *", is_active=False)
# Inspect execution history
executions = verne.clockwork.jobs.executions(job.id) # list[Execution]
# Delete a job
verne.clockwork.jobs.delete(job.id)
Delayed Jobs
One-shot jobs that fire once at run_at:
# Schedule a delayed job
delayed = verne.clockwork.delayed.create(
name="welcome-email",
run_at="2026-07-26T09:00:00Z", # ISO 8601 timestamp
url="https://example.com/send",
body='{"template":"welcome"}', # optional
)
# List pending / completed delayed jobs
verne.clockwork.delayed.list() # list[DelayedJob]
# Inspect execution history
verne.clockwork.delayed.executions(delayed.id) # list[Execution]
# Cancel a pending delayed job
verne.clockwork.delayed.cancel(delayed.id)
Async Support
Every client has an async counterpart — AsyncVerne, AsyncRelay, AsyncGate, AsyncClockwork — with the same interface, where all methods are coroutines:
from vernesoft import AsyncVerne
verne = AsyncVerne(
relay=os.environ["VERNE_RELAY_KEY"],
gate=os.environ["VERNE_GATE_KEY"],
)
msg = await verne.relay.messages.send(event_type="user.created", payload={"id": "usr_123"})
page = await verne.relay.messages.list(limit=10)
identity = await verne.gate.identities.create(schema_id="user", traits={"email": "a@b.com"})
token = await verne.gate.tokens.create(subject="usr_123")
decision = await verne.gate.authorize(subject="usr_123", action="relay.messages.read", resource="tenant:ten_001")
Error Handling
All API errors raise VerneAPIError with structured fields:
from vernesoft import VerneAPIError, VerneError
try:
verne.relay.messages.send(event_type="ping", payload={})
except VerneAPIError as e:
print(e.code) # e.g. 'invalid_payload', 'unauthorized'
print(e.status) # HTTP status code
print(e.request_id) # include in support requests
except VerneError as e:
# network failure or timeout
print(e)
Configuration
Both Verne and the per-service clients accept an optional timeout in seconds (default 30):
verne = Verne(
relay=os.environ["VERNE_RELAY_KEY"],
timeout=10,
)
Individual requests can be cancelled by passing an httpx timeout or by closing the client. Per-request timeouts are supported natively through httpx:
import httpx
from vernesoft import Relay
relay = Relay(api_key=os.environ["VERNE_RELAY_KEY"], timeout=5)
License
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 vernesoft-1.3.0.tar.gz.
File metadata
- Download URL: vernesoft-1.3.0.tar.gz
- Upload date:
- Size: 63.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a6fd8d7c4e6c7ba5f8838d90a4ac9d5b7d5cef2bef8c9e4493915d6a06625c1a
|
|
| MD5 |
c8ba4122f34fb1f26a2c9a4cb842225a
|
|
| BLAKE2b-256 |
1e065ad5c245e4ae7693bfa0d336a41980127322ebeeaac400f821054cb45fbb
|
File details
Details for the file vernesoft-1.3.0-py3-none-any.whl.
File metadata
- Download URL: vernesoft-1.3.0-py3-none-any.whl
- Upload date:
- Size: 23.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b3c2170ef6aa368b7d82775b4d615ea4bb0b82ab84a6ee8932cd2d58758eea5
|
|
| MD5 |
08a99d9245b9044678b480cc976f5a35
|
|
| BLAKE2b-256 |
f3e76fadbf3464b16d225594c495900625552e8ba15198a831e6c93e9c6be950
|