AgentOwl

Run a durable receiver

Verify a signed event, accept it into SQLite, and process it after acknowledgement.

This example gives you a working receiver before you connect an agent runtime. It accepts an illustrative procurement email into a durable inbox and processes it in a separate worker. Python 3.10 or later is the only dependency.

Get the example

Download these three files into one folder:

In a source checkout, they are already in examples/receiver. If you downloaded them into a different folder, use that folder in place of examples/receiver in the commands below. The downloads are published with the updated website; until that release use the source checkout.

Illustrative delivery ledger: a stored message is accepted normally, or retried after HTTP 503 with one durable job.

The diagram uses the same address and event identity as the homepage. It is illustrative; acceptance is separate from agent completion.

1. Start the receiver

cd examples/receiver
export AGENTOWL_SIGNING_SECRET=local-demo-only
python3 receiver.py serve

The example listens at http://127.0.0.1:8788/events. It verifies the v2 signature over the timestamp and raw body before parsing JSON, accepts timestamps within five minutes, and requires the delivery key to match the verified event ID and configured endpoint ID.

2. Send a signed fixture

In a second terminal, open the same folder:

export AGENTOWL_SIGNING_SECRET=local-demo-only
python3 send_fixture.py
python3 receiver.py inspect

Expected: 204 accepted evt_procurement_demo. Inspect shows one job with status: pending and key evt_procurement_demo:ep_demo.

Send the fixture again. You still have one job. A unique database key and transaction enforce duplicate acceptance atomically, including concurrent deliveries. SQLite commits before the receiver acknowledges; a storage failure returns 503 instead.

3. Run the worker

python3 receiver.py worker --once

Expected output:

{"event_id":"evt_procurement_demo","channel":"email","status":"example_processed","text":"Can you check the delivery date for order 42?"}

Running it again produces no output. Restarting the receiver or worker preserves accepted work in receiver.sqlite3. Leave out --once to poll for work continuously.

This output means the example processed the message. It is not a real agent answer or a verified procurement action.

4. Connect real deliveries

  1. Keep the database on private, persistent storage and expose /events through your own public HTTPS endpoint. AgentOwl cannot reach the loopback address.
  2. Create a workspace, endpoint, address, and route using the quickstart or CLI setup.
  3. Prepare the endpoint's signing secret, install that secret as AGENTOWL_SIGNING_SECRET, restart the receiver with --endpoint <your_endpoint_id>, then activate v2 signing. See signing setup. Do not use the local demo secret for real traffic.
  4. Activate routing and send a real email. Confirm the stored event and delivery attempt in Inbox, then inspect the receiver's accepted job.
  5. Replace the fixture worker with your runtime handoff. Preserve durable acceptance, unique delivery identity, and background execution.

The example supports one signing secret. Rotation requires a receiver that temporarily verifies both old and new keys; do not activate a replacement until your receiver supports the overlap.

What to extend

The envelope is shared; bodies differ by channel and provider. The fixture worker reads SMTP email text and SMS text. It does not fetch externalized payloads, archive provider media, run models, or send replies. A payload reference produces requires_payload_fetch; implement the payload API before using its content.

The worker's result is a local database write. External tools require their own idempotency and authorization; delivery deduplication does not make an external action exactly once. Sender authentication results do not authorize requested actions. For runtime progress and human input, see agent work.

Continue with failure and recovery.

On this page