Every dashboard action is an MCP tool your agent can call.Read the docs

Send emails from PHP

Transactional and marketing email from any PHP app. One HTTP call with the client you already use, and a message ID you can follow to the inbox.

✓Free to start. Unlimited contacts on every tier.

Server quickstart
Php

One request, complete trace

Request

POST /api/v1/emails

Authorization: Bearer aem_…

Idempotency-Key: welcome-8f21

{ "to": "[email protected]", "template": "welcome" }

202 Accepted

{ "id": "msg_7ca31", "status": "queued" }

RuntimeServer process
RequestPOST /api/v1/emails
Result202 · queued
Credentials stay server-side

[ 01 / 04 ]Quickstart

How do I send email from PHP?

Three steps from no account to a queued email. No SDK needed.

  1. Create an API key

    In the dashboard, open Settings → API & MCP and create a key with the Send + manage permission (the default). It starts with aem_ and is shown once.

  2. Store it as an environment variable

    Keep the key on your server, never in front-end code. Your code reads it with getenv('AEM_KEY').

    Terminal
    export AEM_KEY="aem_..."
  3. Send the email

    POST the recipient and a published template. This uses Guzzle, which throws on a 4xx or 5xx; Laravel's HTTP client or any PSR-18 client sends the same request.

    send-welcome.php
    $client = new \GuzzleHttp\Client();
    $response = $client->post('https://sendandretain.com/api/v1/emails', [
      'headers' => [
        'Authorization' => 'Bearer ' . getenv('AEM_KEY'),
        'Idempotency-Key' => "welcome-{$user->id}",
      ],
      'json' => ['to' => $user->email, 'template' => 'welcome'],
    ]);
    $result = json_decode((string) $response->getBody(), true);
  4. Keep the message ID

    A 202 means queued, not delivered. Store the id; delivery, bounce, and complaint events arrive later on your signed webhook.

    202 Accepted
    { "id": "msg_7ca31", "status": "queued" }

[ 02 / 04 ]In production

What does a production PHP integration need?

The HTTP client you already use

Laravel's client, Guzzle, a PSR-18 client, or the standard library. No special runtime.

Retries with the same key

Send from a queued job after the record is saved. Retry timeouts and 5xx errors with the same Idempotency-Key; don't retry validation or auth errors.

Webhooks checked on the raw body

Read php://input and verify the signature before any framework parsing.

Events for marketing email

Send product events like signup or trial started. Automations you've turned on decide which email goes out.

[ 03 / 04 ]Example prompts

What can you ask your coding agent to do in PHP?

Paste these into Claude Code, Cursor, or Codex. Connect the MCP server and it can build the templates and automations too.

  • Welcome email

    “Find where we register users and send a welcome email with the Send & Retain API. Read the key from AEM_KEY and use the user ID as the idempotency key.”

  • Queued job

    “Move our order emails into a Laravel queued job that retries timeouts with the same Idempotency-Key and never retries 4xx errors.”

  • Webhooks

    “Add a route that verifies Send & Retain webhook signatures on php://input and flags bounced addresses in our users table.”

  • Trial automation

    “Send a trial_started event to Send & Retain when a trial begins, then build a paused trial automation for it.”

Questions people ask us

Send your first email from PHP.

Create a key, make one call, and follow the message ID to the inbox.