API keys authenticate every request and can be scoped to restrict which render targets they may produce.

Target scopes

Targets value Allowed render calls

["all"]

html, react-email, mjml

["html"]

html only — any other target returns 403 FORBIDDEN

["mjml"]

mjml only

["react-email"]

react-email only

["html", "mjml"]

html and mjmlreact-email returns 403 FORBIDDEN

Creating a key via the dashboard

See API Keys (Builder) for the UI walkthrough.

Creating a key via the API

curl -X POST https://api.maildeno.com/api/v1/keys \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <ADMIN_KEY>" \
  -d '{"name": "HTML only", "targets": ["html"]}'
{
  "id":         "key_01abc...",
  "name":       "HTML only",
  "key":        "sk_live_...",   // shown once — copy it now
  "targets":    ["html"],
  "created_at": "2025-01-01T00:00:00Z"
}

Handling a 403 in the SDK

When a request hits a target outside the key’s scope, the SDK raises FORBIDDEN:

  • JavaScript

  • Python

try {
  // This key only has targets: ["html"]
  await client.renderMjml("template-id")
} catch (err) {
  if (err instanceof MaildenoError && err.code === "FORBIDDEN") {
    console.error("Key scope insufficient:", err.message)
    // "Key scope insufficient: key targets ['html'], requested 'mjml'"
  }
}
try:
    client.render_mjml("template-id")
except MaildenoError as err:
    if err.code == "FORBIDDEN":
        print("Key scope insufficient:", err.message)
{environment}-{service}-{target}

production-api-server-html
staging-newsletter-worker-all
ci-integration-tests-html

Security best practices

  • Store keys in environment variables or a secrets manager (Vault, AWS Secrets Manager, Doppler).

  • Never commit keys to source control.

  • Use the narrowest scope sufficient for the task — principle of least privilege.

  • Rotate keys on a regular schedule or immediately if a leak is suspected.

  • Use separate keys per environment (development, staging, production).