curl

The API from a shell, which is also the version to paste into a bug report.

Nothing here needs a client library. If you can make an HTTPS request, you can send email, and curl is the shortest way to prove a key works before you write any code.

Send

POST /v1/emails
curl -X POST https://emails.sh/v1/emails \
  -H "Authorization: Bearer $EMAILSSH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Acme <hello@acme.com>",
    "to": ["ada@example.com"],
    "subject": "Your receipt from Acme",
    "html": "<p>Thanks for your order. Your receipt is attached.</p>",
    "text": "Thanks for your order. Your receipt is attached."
  }'
200 OK
{
  "id": "em_01J9X8Q2K7Y4RN3M",
  "status": "queued"
}

The key comes from https://emails.sh/dashboard/api-keys and lives in the environment, not in the command: a key typed inline ends up in your shell history and in the terminal scrollback you paste into an issue.

Read the result

Send, then check
# Send and keep the id
ID=$(curl -s -X POST https://emails.sh/v1/emails \
  -H "Authorization: Bearer $EMAILSSH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"from":"onboarding@emails.sh","to":["you@example.com"],"subject":"Test","text":"Hello."}' \
  | jq -r .id)

# Ask what happened to it
curl -s https://emails.sh/v1/emails/$ID \
  -H "Authorization: Bearer $EMAILSSH_API_KEY" | jq .status

From a file

A body with real HTML in it is easier to keep in a file than to quote in a shell. -d @file reads it, and the file can be generated by anything.

Body in a file
cat > email.json <<'JSON'
{
  "from": "Acme <hello@acme.com>",
  "to": ["ada@example.com"],
  "subject": "Your receipt from Acme",
  "html": "<p>Thanks for your order.</p>",
  "text": "Thanks for your order."
}
JSON

curl -X POST https://emails.sh/v1/emails \
  -H "Authorization: Bearer $EMAILSSH_API_KEY" \
  -H "Content-Type: application/json" \
  -d @email.json

Attach a file

Base64 into the body with jq
PDF=$(base64 -w 0 receipt.pdf)   # macOS: base64 -i receipt.pdf

curl -X POST https://emails.sh/v1/emails \
  -H "Authorization: Bearer $EMAILSSH_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg pdf "$PDF" '{
    from: "Acme <hello@acme.com>",
    to: ["ada@example.com"],
    subject: "Your receipt",
    html: "<p>Your receipt is attached.</p>",
    attachments: [{ filename: "receipt.pdf", content_type: "application/pdf", content_base64: $pdf }]
  }')"

Fail loudly in a script

curl exits 0 on a 422 by default, which is how a broken deploy script stays quiet for a week. --fail-with-body gives you a non-zero exit and still prints the reason.

A send that a CI job notices
set -euo pipefail

curl --fail-with-body -sS -X POST https://emails.sh/v1/emails \
  -H "Authorization: Bearer $EMAILSSH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"from":"onboarding@emails.sh","to":["you@example.com"],"subject":"Deploy","text":"Shipped."}'