Log in Create workspace
Skip to content

API reference

Email

Send policy-checked transactional or bulk email through a workspace SMTP connector.

Jump to 2 endpoints
On this page

Overview

Use the Email API when an agent needs to send a notification, confirmation, or batch of personalized messages. Messages go through your own SMTP mail server, connected to the workspace as an email-smtp connector. Robots Center checks the agent's permissions and approval policies before sending and records a trace you can inspect afterward.

Authorization is part of every send
The endpoint evaluates grants and approval policies using the recipient payload plus optional top-level environment, amount, risk_score, and contains_phi. A separate auth check is informative only and does not reserve a later decision.
Idempotency protects the SMTP side effect
Supply one Idempotency-Key for each logical single or bulk send. Conflict detection hashes the connector, action, and validated SMTP payload; changing one of those under the same key conflicts. Policy-only environment, amount, risk_score, and contains_phi inputs are evaluated again on every request but are not part of that hash, so keep them stable for a predictable logical retry.
Approval is not queued by email routes
A requires_approval result is returned as HTTP 400 problem JSON, just like a policy denial; the email is not sent and no approval workflow is automatically resumed. Use the Gateway tool-action approval flow when an operator-approved execution is required.

Before you begin

  • The command-line examples require curl and jq; any exception is identified in its introduction.
  • An operator-configured active connector whose slug is email-smtp and whose SMTP host, port, username, password, and from address are valid.
  • A permission grant binding the calling service agent to that connector and permitting the exact email action.
  • A machine credential with email:send; add gateway:check only if the client will preflight the policy decision.

How it works

  1. Prepare connector access

    Have an operator create the SMTP connector and grant the service agent send_email and, if needed, send_bulk_email. Confirm the connector is active and the relay is allowed by outbound network policy.

  2. Optionally preflight policy

    POST /api/v1/auth_checks with the same connector, action name, payload characteristics, and policy fields. Treat the result as a point-in-time explanation, not proof the subsequent send must succeed.

  3. Send once and retain the receipt

    Use a stable idempotency key, then record the returned SMTP message_id, runtime trace_id, connector identity, and duration. A bulk response reports totals and can succeed with a nonzero failed count.

Examples

These examples are illustrative. Replace the sample values with your own. Keep credentials in environment variables or a secret manager, never in client-side code.

Preflight the send policy

This endpoint does not send email. Use the same policy fields that the later send will carry.

bash
export AGENTOPS_BASE_URL="https://robotscenter.net"
export AGENT_TOKEN="machine_token_with_gateway_check"

curl --fail-with-body --silent --show-error \
  -X POST \
  -H 'content-type: application/json' \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  --data '{"connector_slug":"email-smtp","action_name":"send_email","payload":{"to":"customer@example.com","subject":"Receipt"},"environment":"production","contains_phi":false}' \
  "$AGENTOPS_BASE_URL/api/v1/auth_checks" | jq .
What to expect

When authentication, validation, and connector lookup succeed, all policy outcomes return HTTP 200 with decision: allowed, denied, or requires_approval. Check the decision as well as the HTTP status; malformed or unauthorized requests can still return errors.

Send one email with safe retry

Keep the key stable only while retrying this logical send. Connector, action, recipient, subject, body, HTML, and attachments determine an idempotency conflict; keep policy inputs stable even though they are re-evaluated rather than hashed.

bash
export AGENTOPS_BASE_URL="https://robotscenter.net"
export AGENT_TOKEN="machine_token_with_email_send"
export EMAIL_IDEMPOTENCY_KEY="receipt-ord-example-1042-v1"

curl --fail-with-body --silent --show-error \
  -X POST \
  -H 'content-type: application/json' \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "Idempotency-Key: $EMAIL_IDEMPOTENCY_KEY" \
  --data '{"connector_slug":"email-smtp","to":"customer@example.com","subject":"Your receipt","body":"Thank you for order ord-example-1042.","environment":"production","contains_phi":false}' \
  "$AGENTOPS_BASE_URL/api/v1/email/send" | jq .
What to expect

HTTP 200 returns status: sent, the SMTP message_id, connector ID and slug, action, trace ID, and duration in milliseconds.

Send a bounded bulk request

One idempotency key protects the whole array, not each recipient independently.

bash
export AGENTOPS_BASE_URL="https://robotscenter.net"
export AGENT_TOKEN="machine_token_with_email_send"
export EMAIL_IDEMPOTENCY_KEY="daily-digest-2026-09-16"

curl --fail-with-body --silent --show-error \
  -X POST \
  -H 'content-type: application/json' \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "Idempotency-Key: $EMAIL_IDEMPOTENCY_KEY" \
  --data '{"connector_slug":"email-smtp","recipients":[{"to":"one@example.com","subject":"Daily digest","body":"One new item."},{"to":"two@example.com","subject":"Daily digest","body":"Two new items."}],"environment":"production"}' \
  "$AGENTOPS_BASE_URL/api/v1/email/bulk" | jq .
What to expect

HTTP 200 returns status: completed, total, sent, failed, connector details, a trace ID, and duration. Inspect the counts rather than assuming every recipient was delivered.

Troubleshooting

The send returns denied or requires_approval
Both are HTTP 400 problem responses on the email routes and no SMTP action runs. Check the connector grant and policy inputs; use the Gateway approval flow instead if execution truly must wait for a human decision.
The connector is 404 or the wrong type
Connector lookups are workspace-scoped and return only active, non-archived rows. Email endpoints additionally require slug email-smtp; another connector type produces invalid_connector_type.
An idempotency retry conflicts
The key was reused with a changed connector, action, or validated SMTP payload, or a prior attempt is still in progress or failed indeterminately. Policy-only inputs do not produce this hash conflict, but they are re-authorized. Do not generate a new key merely to force a second SMTP side effect.
An attachment is rejected
Send inline base64 only: at most 10 files, 2 MiB decoded per file, and 5 MiB decoded in total. URL and filesystem-path attachments are intentionally refused.

Endpoint reference

Ready to integrate? Open an endpoint for its required permissions, request fields, response format, and status codes. The examples above explain the workflow; these details define each individual request.

POST
/api/v1/email/send API key / access token

Send a single email via SMTP connector

email:send

Description

Sends a single transactional email using an Email (SMTP) connector. Requires a connector with slug 'email-smtp' bound to the service agent with send_email action permitted. Idempotency-Key enables safe retry; the same key and request replay the first result, while changed input returns 409.

Auth

API key / access token

Required Scopes

email:send
Request Body
Field Type Required Description
body string Yes Plain text email body
to string Yes Recipient email address
amount number No Fintech policy amount (top-level PDP input)
html string No Optional HTML email body
subject string Yes Email subject line
environment string No Environment for approval-policy conditions (top-level)
attachments array<object> No Up to 10 inline objects with filename, content_type, and content_base64. URL/path attachments are rejected. Each decoded file is limited to 2 MiB and all decoded files to 5 MiB. Only basename/type/size/SHA-256 metadata is persisted.
connector_id string No Email connector UUID
connector_slug string No Email connector slug (alternative to connector_id)
contains_phi boolean No Healthcare policy input. Hoisted into payload.contains_phi for the same PDP envelope as /gateway/execute.
risk_score number No Risk score for approval-policy conditions (top-level)
Parameters
Name In Type Required Description
Idempotency-Key header string No Optional key of at most 200 visible ASCII bytes. Blank, duplicated, non-visible ASCII, or overlong values return 422; exact retries replay the prior result.
Responses
200

Email sent

Response Body
Field Type Required Description
status string (sent) Yes Always sent
action string (send_email) Yes Always send_email
message_id string Yes SMTP message id
trace_id string Yes Runtime trace id
duration_ms integer Yes Execution time in milliseconds
connector_id string Yes Connector UUID
connector_slug string Yes Connector slug
Example
{
  "status": "sent",
  "action": "send_email",
  "message_id": "<abc123@smtp.delivered>",
  "trace_id": "a1b2c3d4e5f6",
  "duration_ms": 245,
  "connector_id": "550e8400-e29b-41d4-a716-446655440000",
  "connector_slug": "email-smtp"
}
400

Problem JSON. Denied, requires_approval, and invalid_connector_type fall through FallbackController as 400 with detail denied, requires_approval, or invalid_connector_type. SMTP/executor failures also use this status.

401

Invalid or missing token

403

Missing email:send scope or workspace write gate

404

Connector not found

409

Problem JSON: Idempotency-Key conflicts with changed input, is in progress, or a prior execution failed and cannot safely retry

422

Validation failed: missing to/subject/body or connector ref, invalid email, or invalid connector UUID

POST
/api/v1/email/bulk API key / access token

Send bulk emails via SMTP connector

email:send

Description

Sends multiple emails to different recipients using an Email (SMTP) connector. Each recipient can have individual to, subject, body, html, and bounded inline attachment fields. Idempotency-Key protects the whole bulk side effect.

Auth

API key / access token

Required Scopes

email:send
Request Body
Field Type Required Description
amount number No Fintech policy amount (top-level PDP input)
environment string No Environment for approval-policy conditions (top-level)
recipients array<object> Yes Array of at most 100 recipient objects with to, subject, body, html fields
connector_id string No Email connector UUID
connector_slug string No Email connector slug (alternative to connector_id)
contains_phi boolean No Healthcare policy input. Hoisted into payload.contains_phi for the same PDP envelope as /gateway/execute.
risk_score number No Risk score for approval-policy conditions (top-level)
Parameters
Name In Type Required Description
Idempotency-Key header string No Optional key of at most 200 visible ASCII bytes. Blank, duplicated, non-visible ASCII, or overlong values return 422; exact retries replay the prior result.
Responses
200

Bulk send finished

Response Body
Field Type Required Description
status string (completed) Yes Always completed
total integer Yes Recipient count
failed integer Yes Failed count
sent integer Yes Delivered count
action string (send_bulk_email) Yes Always send_bulk_email
trace_id string Yes Runtime trace id
duration_ms integer Yes Execution time in milliseconds
connector_id string Yes Connector UUID
connector_slug string Yes Connector slug
Example
{
  "status": "completed",
  "total": 3,
  "failed": 0,
  "sent": 3,
  "action": "send_bulk_email",
  "trace_id": "a1b2c3d4e5f6",
  "duration_ms": 812,
  "connector_id": "550e8400-e29b-41d4-a716-446655440000",
  "connector_slug": "email-smtp"
}
400

Problem JSON. Denied, requires_approval, and invalid_connector_type fall through FallbackController as 400 with detail denied, requires_approval, or invalid_connector_type.

401

Invalid or missing token

403

Missing email:send scope or workspace write gate

404

Connector not found

409

Problem JSON: Idempotency-Key conflicts with changed input, is in progress, or a prior execution failed and cannot safely retry

422

Validation failed: missing or more than 100 recipients, missing connector ref, or invalid connector UUID

Where to go next

see also