Skip to main content

Official Python SDK for Ujex Postbox — email-for-AI-agents with built-in prompt-injection scoring, auth verdicts, and plus-addressed task threading.

Project description

ujex-postbox

The Python SDK for Ujex Postbox — email for AI agents.

pip install ujex-postbox

30-second demo

from ujex_postbox import PostboxClient, plus_address

client = PostboxClient(device_key="ap_live_...", agent_id="agent-hello")

# Send
resp = client.send(
    to=["alice@vendor.com"],
    subject="Re: invoice",
    body="On it — sending by Friday.",
    session_id="sess_run_20260618_001",
    require_human=True,
)
print(resp.id, resp.status, resp.quota_remaining)

# Plus-address a per-task inbox (no new inbox needed)
addr = plus_address("agent-hello@in.ujex.dev", task_id="proj-42-abc")
# -> agent-hello+proj-42-abc@in.ujex.dev

# Read an inbound message
m = client.read_message(agent_id="agent-hello", message_id="msg_01...")
print(m.pi_score, m.pi_reasons, m.auth_verdict.dkim, m.plus_task_id)

Why not AgentMail / SES / SendGrid?

Postbox surfaces Ujex's agent-specific signals as first-class fields:

Field What it gives you
m.pi_score / m.pi_reasons Gemini-scored prompt-injection risk on every inbound, 0-1.
m.auth_verdict.dkim/spf/dmarc RFC 5321/7208/7489 verdicts — bool per message, no parsing SPF strings yourself.
m.plus_task_id Task id automatically extracted from the plus-address.
resp.quota_remaining Per-agent monthly budget remaining in USD-equivalent units.
require_human=True on send Returns waiting_approval; the outbound message dispatches after app/dashboard approval.

API

Method Purpose
PostboxClient.send(to, subject, body, session_id, thread_key?, idempotency_key?, require_human?) Send an email. Returns SendResult.
PostboxClient.list_messages(agent_id, direction?, limit?, since?) List inbound or outbound messages. Returns list[Message].
PostboxClient.read_message(agent_id, message_id) Full message with body + verdicts.
PostboxClient.list_inboxes(agent_id?) Provisioned inboxes.
plus_address(base_inbox, task_id) Build base+task@domain.
parse_plus_address(addr) {base, task_id, domain} or None.
verify_signature(raw_body, signature, secret) Timing-safe HMAC-SHA256 check on an inbound webhook.
parse_inbound_event(raw_body) Raw bytes/dict → InboundEvent.

An AsyncPostboxClient mirrors the same surface on httpx.AsyncClient.

Retries

Retries automatically on 429 and 5xx with exponential backoff and respect for Retry-After. Default max 3 retries. Tune with PostboxClient(max_retries=…).

Webhook verification

from ujex_postbox import verify_signature, parse_inbound_event

ok = verify_signature(
    raw_body=request.body,                      # bytes
    signature=request.headers["x-ujex-signature"],
    secret=os.environ["POSTBOX_INGEST_SECRET"],
)
if not ok:
    return Response(status=401)
event = parse_inbound_event(request.body)
if event.injection_score.score > 0.7:
    # flag for review, don't let the agent process it yet
    quarantine(event)

Framework integrations

Each adapter is an opt-in install that raises a clear ImportError with install instructions if the framework isn't present.

LangChain

pip install 'ujex-postbox[langchain]'
from ujex_postbox import PostboxClient
from ujex_postbox.integrations.langchain import postbox_tools

client = PostboxClient(device_key="ap_live_...", agent_id="agent-hello")
tools = postbox_tools(client, agent_id="agent-hello")
# hand `tools` to any LangChain agent / graph

LlamaIndex

pip install 'ujex-postbox[llamaindex]'
from ujex_postbox import PostboxClient
from ujex_postbox.integrations.llamaindex import postbox_tools

client = PostboxClient(device_key="ap_live_...", agent_id="agent-hello")
tools = postbox_tools(client, agent_id="agent-hello")

CrewAI

pip install 'ujex-postbox[crewai]'
from ujex_postbox import PostboxClient
from ujex_postbox.integrations.crewai import postbox_tools

client = PostboxClient(device_key="ap_live_...", agent_id="agent-hello")
send_tool, read_tool, list_tool = postbox_tools(client, agent_id="agent-hello")

Core loop

Use Python for the Postbox edge of the golden path: send/receive agent mail, request human approval with require_human=True, and inspect prompt-injection signals on inbound messages. Recall memory and owner-scoped Audit are available through the dashboard, CLI, and TypeScript client while the Python SDK remains Postbox-focused.

Generate one stable session_id per agent run and pass it to every outbound send. Ujex uses it for prompt-injection poisoning, exfiltration checks, and budget preflight.

No account yet?

Free tier: 100 sends/month plus approvals, audit, and Recall memory, no card. Sign up at ujex.dev.

License

Apache-2.0

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

ujex_postbox-0.2.0.tar.gz (21.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ujex_postbox-0.2.0-py3-none-any.whl (26.3 kB view details)

Uploaded Python 3

File details

Details for the file ujex_postbox-0.2.0.tar.gz.

File metadata

  • Download URL: ujex_postbox-0.2.0.tar.gz
  • Upload date:
  • Size: 21.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for ujex_postbox-0.2.0.tar.gz
Algorithm Hash digest
SHA256 2d95cd5a02c8429ee089e15b5a6d8491651065ccc381ce919f185fc3d29722a5
MD5 0a8255e61148021a94636b628688afcd
BLAKE2b-256 f88bee6409e7e6daa4baadfb2b43179ef3d36fc2ff01a23323d5eb6c05a1627f

See more details on using hashes here.

File details

Details for the file ujex_postbox-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: ujex_postbox-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 26.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for ujex_postbox-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4c015b3940b7399537cbeb673a5ee40c0c781d5f10fccf4c29ded98e1de307d6
MD5 19fae6374ba7ee09e4a7c586da7910d1
BLAKE2b-256 60f034072f023a627bec5d23b327f0dc2d608bc4ac4e2604192bea76cdf7d235

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page