SOLEN

HTTP API

Applications that aren't Node can integrate directly over HTTP. The send API is one endpoint, authenticated with a project API key.

Authentication

Pass the key as a bearer token. Keys are project-scoped; the template you reference must belong to the same project.

Authorization: Bearer solen_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

POST /v1/emails

Requestbash
curl -X POST http://localhost:4000/v1/emails \
  -H "Authorization: Bearer $SOLEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template": "welcome-email",
    "to": "student@example.com",
    "variables": { "name": "Priya", "school": "Northside High" }
  }'
FieldTypeRequiredNotes
templatestringyesTemplate slug within the key's project.
tostringyesRecipient email address.
variablesobjectnoString values substituted into {{placeholders}}.
attachmentsarraynoFiles to attach (base64). Appended after the template's own attachments.

Attachments

Each attachment is an object with a filename, base64 content, and an optional contentType. They are appended to any attachments configured on the template in the dashboard.

Request with an attachmentbash
curl -X POST http://localhost:4000/v1/emails \
  -H "Authorization: Bearer $SOLEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template": "invoice",
    "to": "user@example.com",
    "variables": { "amount": "$42.10" },
    "attachments": [
      {
        "filename": "invoice.pdf",
        "content": "JVBERi0xLjcKJ...",
        "contentType": "application/pdf"
      }
    ]
  }'
FieldTypeRequiredNotes
filenamestringyesFile name shown to the recipient.
contentstringyesFile bytes, base64-encoded.
contentTypestringnoMIME type; derived from the filename when omitted.

Limits: up to 20 attachments and 10 MB total decoded size per email (template + request combined). Over the limit returns INVALID_PAYLOAD.

Success - 200

{
  "id": "log_cmb1xk2...",
  "status": "SUCCESS",
  "messageId": "re_AbC123..."
}

Provider failure - 422

{
  "id": "log_cmb1xk9...",
  "status": "FAILED",
  "error": { "code": "PROVIDER_ERROR", "message": "Domain not verified" }
}

Both outcomes write a log row; id is always the log id. Other errors use the standard error shape without an id when nothing was renderable to log (e.g. unknown template).

Error responses

StatusCodeWhen
401UNAUTHORIZEDMissing, malformed, revoked, or unknown key.
404TEMPLATE_NOT_FOUNDSlug doesn't exist in the key's project.
409TEMPLATE_DISABLEDTemplate is toggled off.
400INVALID_PAYLOADValidation failure; message says which field.
422PROVIDER_ERRORProvider rejected the send (logged).
429RATE_LIMITEDOver 60 requests/minute on one key.

GET /v1/health

Unauthenticated liveness probe; returns { "ok": true }.

Rate limits

The send API allows 60 requests per minute per key. Batch and queue sends client-side for now - bulk sending is a Phase 3 roadmap item.