How to run this route from Python with the Ouro SDK.
API access requires an API key. Create one in Settings → API Keys, then set OURO_API_KEY in your environment. Install the SDK with pip install ouro-py.
POST /send-and-logParameters and request body schema for this route.
Recipient email address
Email HTML body
coldfollowupreplynudgeEmail intent for idempotency key and CRM logic
Zero-side-effect preview: performs the read-only CRM lookup, builds the exact send payload and would-be CRM row, but sends no email and writes nothing to the CRM.
Email subject line
Inbound message id being replied to, or 'first' for cold, or 'followup-{email_id}'
CRM row id for this contact
Workspace path to the file
Download name; defaults to the path basename
Optional MIME type
Workspace files to attach. Each item needs path (relative to workspace root or /workspace/...). filename defaults to the basename.
CRM next action after the send
For a new CRM row: name, type, institution, email, focus, batch, and optional next_action
Legacy hint retained for compatibility; first-send status is detected from the CRM row
For live-thread replies: updated CRM reply_received note (e.g. append that the open question was answered). Optional.
Today's total outbound count (from outreach-triage / Resend). At >= 8, cold/followup/nudge sends are refused with daily_cap_exceeded; a live-thread reply is still allowed and still counts.
execute returns an action: the record of this run, with its status, response, and any assets it created.
To avoid blocking on a slow run, start it without waiting and collect the result later.
By default a failed run comes back as an action with status error. Pass raise_on_error=True to raise an exception instead.
Every run is saved as an action. List yours, or read the logs of a single run. See the Python SDK reference for everything an action carries.
import os
from ouro import Ouro
ouro = Ouro(api_key=os.environ.get("OURO_API_KEY"))
# The ID also works, and stays the same if the route is renamed: "c1782c41-a034-468f-9a03-bb7e946ecd2c"
route = ouro.routes.retrieve("hermes/send-outreach-email-and-log-to-crm")
action = route.execute(
body={
"to": "example_string",
"html": "example_string",
"intent": "cold",
"subject": "example_string",
"contact_id": "example_string",
},
)
print(action.status) # "success" or "error"
print(action.final_data)# Returns as soon as the run is accepted
action = route.execute(
body={
"to": "example_string",
"html": "example_string",
"intent": "cold",
"subject": "example_string",
"contact_id": "example_string",
},
wait=False,
)
print(action.id, action.status)
# Later, even from another process
action = ouro.routes.poll_action(str(action.id), poll_interval=5, timeout=1800)
print(action.final_data)from ouro import ExternalServiceError, RouteExecutionError
try:
action = route.execute(
body={
"to": "example_string",
"html": "example_string",
"intent": "cold",
"subject": "example_string",
"contact_id": "example_string",
},
raise_on_error=True,
)
except ExternalServiceError as exc:
# The API behind this route failed
print(exc.status_code, exc.retryable)
except RouteExecutionError as exc:
print(exc.action_id, exc.status, exc.response)
except TimeoutError as exc:
# Still running on Ouro; pick it up again later
action = ouro.routes.poll_action(exc.action_id, timeout=None)route = ouro.routes.retrieve("hermes/send-outreach-email-and-log-to-crm")
# Your runs of this route
actions = route.read_actions()
for action in actions:
print(action.id, action.status, action.created_at)
# One run and its logs
action = ouro.routes.retrieve_action("your-action-id")
for entry in action.read_logs(chronological=True):
print(entry.level, entry.message)Send an outreach email with optional workspace-file attachments, idempotency, and required CCs, then record it in the CRM.
Execution
Usage
1 callView history