Frameworks and agents · Published 2026-10-31 · Updated 2026-10-31 · AgentiSend
Send email with Ruby
A Ruby script that posts one statement with Net::HTTP, an idempotency key, and the refusal code and fix.
To send email with Ruby, post JSON to POST /emails with Net::HTTP. There is no Ruby package for this API, and this page does not invent one. The method is what a script, a Rake task, or a route outside Rails calls. Send email from Rails is the controller method; this file is the standard library on its own. It checks the address, sends a statement with an idempotency key derived from the recipient, and prints the message id or the refusal's code and fix. The suite runs it against a local AgentiSend server.
Keep the id the script prints. GET /emails/{id} is that statement's record, and GET /emails/{id}/explain is the reason when it was held instead of delivered.
Install
No gem to add. The file uses the standard library.
Environment
| Variable | Required | What it is |
|---|---|---|
AGENTISEND_API_KEY | yes | A key with sending_access. |
MAIL_FROM | yes | The From address, on a domain you have verified. |
AGENTISEND_BASE_URL | no | Defaults to https://api.agentisend.com. |
The route
# frozen_string_literal: true
# Ruby — one statement with the standard library.
#
# There is no Ruby package for this API. This file posts JSON to POST /emails
# with Net::HTTP. A Rails controller has its own page; call this method from
# a script, a Rake task, or a route that is not Rails.
#
# AGENTISEND_API_KEY is the key. MAIL_FROM is an address on a domain you have
# verified. AGENTISEND_BASE_URL is optional and defaults to the public API.
#
# Run: ruby send.rb
require 'json'
require 'net/http'
require 'uri'
ADDRESS = /^[^@\s]+@[^@\s]+\.[^@\s]+$/
# @param input [Hash]
# @return [Hash] status and the JSON body a caller would render
def send_statement(input)
email = input.fetch('email', '').to_s.strip
note = input.fetch('note', '').to_s
unless ADDRESS.match?(email)
return { 'status' => 400, 'body' => { 'error' => 'email must be one address, like you@example.com' } }
end
from = ENV.fetch('MAIL_FROM') { raise 'Set MAIL_FROM to an address on a domain you have verified.' }
base = (ENV['AGENTISEND_BASE_URL'] || 'https://api.agentisend.com').sub(%r{/+$}, '')
uri = URI("#{base}/emails")
request = Net::HTTP::Post.new(uri)
request['authorization'] = "Bearer #{ENV.fetch('AGENTISEND_API_KEY')}"
request['content-type'] = 'application/json'
request['accept'] = 'application/json'
# The recipient, not the moment: a retry of the same note replays.
request['idempotency-key'] = "statement/#{email}"
request.body = JSON.generate(
'from' => from,
'to' => email,
'subject' => 'Your statement',
'text' => "Statement note: #{note}"
)
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(request)
end
parsed = JSON.parse(response.body)
status = response.code.to_i
if status >= 400
error = parsed.fetch('error', {})
return { 'status' => status, 'body' => { 'code' => error['code'], 'fix' => error['fix'] } }
end
{ 'status' => status, 'body' => { 'id' => parsed['id'] } }
end
def emit(step, result)
puts JSON.generate({ 'step' => step, 'status' => result['status'] }.merge(result['body']))
end
if __FILE__ == $PROGRAM_NAME
emit('send', send_statement('email' => 'ruby@example.com', 'note' => 'first'))
emit('retry', send_statement('email' => 'ruby@example.com', 'note' => 'first'))
emit('invalid', send_statement('email' => 'not-an-address', 'note' => 'first'))
emit('refused', send_statement('email' => 'ruby@example.com', 'note' => 'second'))
endTry it
ruby send.rbThe script prints one JSON object per step: the send, the same request again, a malformed address answered before any request, and a changed note under the same key.
What comes back when it goes wrong
Every refused request carries code, message, fix and docs_url. The method returns code and fix with the API's status. The error catalogue lists every code.