Send email from cURL

Five steps, all from a terminal: check you have curl, create a test key, send to the sandbox, read the log, then verify a domain so you can send to anyone.

1. Install

No SDK needed. The API is plain JSON over HTTPS, and curl ships with macOS, Windows 10 and later, and most Linux distributions. Run curl --version to check.

2. Create an API key

Sign in, open API keys in the dashboard and create a test key. It starts with av_test_. Export it so the commands below can read it:

shell
export AVELTO_API_KEY=av_test_...
Sandbox rules

Test keys never deliver anything; they run the pipeline and record events. The sandbox sender you@sandbox.avelto.dev only delivers to your account's verified owner email and to the simulator addresses delivered@, bounced@ and complained@sandbox.avelto.dev. Anything else is refused with 403 sandbox_recipient_not_allowed. To send to anyone, verify a domain (step 5).

3. Send your first email

Every request carries the key as a bearer token.

curl
curl -X POST https://api-staging.avelto.dev/v1/emails \
  -H "Authorization: Bearer $AVELTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "you@sandbox.avelto.dev",
    "to": "delivered@sandbox.avelto.dev",
    "subject": "Hello from Avelto",
    "text": "It works."
  }'

The API answers 201 Created with the email id:

HTTP
HTTP/1.1 201 Created
Content-Type: application/json

{ "id": "9c1f4a52-6f6e-4b8f-9b8e-2e1a5c7d3f10" }

A rejected send comes back as a non-2xx status with a JSON body of { "error": { "code", "message" } }. Add -i to see the status line. For example, sending from the sandbox to an address that is not yours:

HTTP
HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "error": {
    "code": "sandbox_recipient_not_allowed",
    "message": "The sandbox sender @sandbox.avelto.dev only delivers to the account owner (you@example.com). Verify a domain to send to anyone.",
    "details": { "recipients_not_allowed": ["jane@example.com"] }
  }
}
Retrying safely

Send an Idempotency-Key header (any unique string, such as your order id) with every POST /v1/emails. If the request times out or comes back 429, 502, 503 or 504, wait a moment and send it again unchanged with the same key: the API returns the original email id instead of sending twice. See Idempotency.

4. Check the log

Fetch the email by id. status moves from queued to sent to delivered, and events records each step: email.queued, email.sent, email.delivered.

curl
curl https://api-staging.avelto.dev/v1/emails/9c1f4a52-6f6e-4b8f-9b8e-2e1a5c7d3f10 \
  -H "Authorization: Bearer $AVELTO_API_KEY"
JSON
{
  "id": "9c1f4a52-6f6e-4b8f-9b8e-2e1a5c7d3f10",
  "mode": "test",
  "from": "you@sandbox.avelto.dev",
  "to": ["delivered@sandbox.avelto.dev"],
  "subject": "Hello from Avelto",
  "status": "delivered",
  "events": [
    {
      "id": "e1f0c3a4-8b2d-4c6e-9a1f-5d7b3e2c8a90",
      "type": "email.queued",
      "payload": {},
      "occurred_at": "2026-09-17T10:12:04.000Z"
    },
    {
      "id": "a7c2e9d1-3f4b-4a8e-b6c0-2d9e1f7b5c34",
      "type": "email.sent",
      "payload": { "test": true, "ses_message_id": "test-9c1f4a52-6f6e-4b8f-9b8e-2e1a5c7d3f10" },
      "occurred_at": "2026-09-17T10:12:06.000Z"
    },
    {
      "id": "c4b8d2f6-7e1a-4d3c-8f9b-6a2e0c5d1b78",
      "type": "email.delivered",
      "payload": { "test": true, "recipients": ["delivered@sandbox.avelto.dev"] },
      "occurred_at": "2026-09-17T10:12:06.000Z"
    }
  ]
}

5. Verify a domain

Add a domain. The response lists the DNS records to publish (three DKIM CNAMEs, an SPF TXT and a DMARC TXT) in dns_records, each with type, name, value and purpose. Use a subdomain such as mail.acme.com.

curl
curl -X POST https://api-staging.avelto.dev/v1/domains \
  -H "Authorization: Bearer $AVELTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "mail.acme.com" }'

Publish the records at your DNS provider, then fetch the domain until status is verified. The GET re-checks DNS on every call, so run it again every minute or so.

curl
curl https://api-staging.avelto.dev/v1/domains/1d3f2b7e-0d4a-4e5b-8d0c-6a7e9f1b2c3d \
  -H "Authorization: Bearer $AVELTO_API_KEY"

Once the domain is verified, export a live key (av_live_) as AVELTO_API_KEY and change from to an address on it, such as hello@mail.acme.com. Nothing else changes.

Next

  • Send email: every field, attachments, tags, scheduling and idempotency.
  • Webhooks: get events pushed to your app.
  • Test mode: test keys, the sandbox sender and the simulator addresses.