Frameworks and agents · Published 2026-09-29 · Updated 2026-09-30 · AgentiSend
Send email from PHP
A PHP function that posts one receipt with streams, an idempotency key, and the code and fix of a refusal.
To send email from PHP, post JSON to POST /emails with the language's HTTP stream. There is no PHP package for this API, and this page does not invent one. The function below checks the address, sends with an idempotency key derived from the recipient, and returns the message id, or the refusal's code and fix. It is run by the test suite against a local AgentiSend server.
Install
No package to add. The file uses the standard library. allow_url_fopen stays on, which is the default, so file_get_contents can post.
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
<?php
/**
* PHP — one send with the standard library.
*
* There is no PHP SDK. This file posts JSON to POST /emails with PHP streams.
* 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: php send.php
*/
/** Enough to refuse an obvious typo here; the API checks the address properly. */
function agentisend_address_ok(string $email): bool
{
return preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/', $email) === 1;
}
/**
* @param array{email?: string, note?: string} $input
* @return array{status: int, body: array<string, mixed>}
*/
function agentisend_send(array $input): array
{
$email = trim((string) ($input['email'] ?? ''));
$note = (string) ($input['note'] ?? '');
if (!agentisend_address_ok($email)) {
return [
'status' => 400,
'body' => ['error' => 'email must be one address, like you@example.com'],
];
}
$from = getenv('MAIL_FROM');
if ($from === false || $from === '') {
throw new RuntimeException('Set MAIL_FROM to an address on a domain you have verified.');
}
$base = getenv('AGENTISEND_BASE_URL');
if ($base === false || $base === '') {
$base = 'https://api.agentisend.com';
}
$base = rtrim($base, '/');
$key = getenv('AGENTISEND_API_KEY');
if ($key === false) {
$key = '';
}
$payload = json_encode([
'from' => $from,
'to' => $email,
'subject' => 'Your receipt',
'text' => 'Receipt note: ' . $note,
], JSON_THROW_ON_ERROR);
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => implode("\r\n", [
'authorization: Bearer ' . $key,
'content-type: application/json',
'accept: application/json',
// The recipient, not the moment: a retry of the same note replays.
'idempotency-key: receipt/' . $email,
]),
'content' => $payload,
'ignore_errors' => true,
'timeout' => 30,
],
]);
$raw = file_get_contents($base . '/emails', false, $context);
if ($raw === false) {
throw new RuntimeException('POST /emails failed before a response');
}
$status = 0;
if (isset($http_response_header[0]) && preg_match('/\s(\d{3})\s/', $http_response_header[0], $match) === 1) {
$status = (int) $match[1];
}
/** @var array<string, mixed> $parsed */
$parsed = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
if ($status >= 400) {
$error = is_array($parsed['error'] ?? null) ? $parsed['error'] : [];
return ['status' => $status, 'body' => ['code' => $error['code'] ?? null, 'fix' => $error['fix'] ?? null]];
}
return ['status' => $status, 'body' => ['id' => $parsed['id'] ?? null]];
}
/**
* @param array{status: int, body: array<string, mixed>} $result
*/
function agentisend_emit(string $step, array $result): void
{
echo json_encode(['step' => $step, 'status' => $result['status']] + $result['body'], JSON_THROW_ON_ERROR), "\n";
}
if (PHP_SAPI === 'cli' && isset($argv[0]) && realpath($argv[0]) === __FILE__) {
agentisend_emit('send', agentisend_send(['email' => 'php@example.com', 'note' => 'first']));
agentisend_emit('retry', agentisend_send(['email' => 'php@example.com', 'note' => 'first']));
agentisend_emit('invalid', agentisend_send(['email' => 'not-an-address', 'note' => 'first']));
agentisend_emit('refused', agentisend_send(['email' => 'php@example.com', 'note' => 'second']));
}Try it
php send.phpThe file 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 function returns code and fix with the API's status. The error catalogue lists every code.