---
title: "Cloud API, MailKaro Docs"
description: "Cloud API in MailKaro: render a design to HTML from any language, POST it to the hosted endpoint, no SDK needed."
---

# Cloud API

Rendering

# Cloud API

Render a design to HTML from any language, POST it to the hosted endpoint, no SDK needed.

bash curl

```
curl -X POST https://api.mailkaro.com/v3/render \
 -H "content-type: application/json" \
 -d '{
 "design": { /* your design JSON */ }, "format": "html", "merge_tags": { "first_name": "Alex" }
 }'
```

Returns `{ format, output }`. Formats: `html` (default), `mjml`, `amp`, `text`, `document`. An invalid design returns `400 { error: "invalid_design" }`.

`/v3/render` is open: it needs no key (it stores nothing) and is rate limited. `merge_tags` can hold plain values, lists and objects, so `{{#if}}` and `{{#each}}` work (see [merge tags](https://docs.mailkaro.com/merge-tags)). `locale` renders a translation of the design (see [languages](https://docs.mailkaro.com/languages)).

## Templates

Store and fetch designs, authenticated with your **secret server key** (`Authorization: Bearer sk_live_…`). Scoped to the key's organization. The Cloud API is a server-to-server surface, so it accepts only the `sk_live_…` server key, never the publishable `pk_live_…` embed token (that one is frontend-only, for the editor). This applies to every authenticated `/v3` endpoint below.

bash

```
# create
POST /v3/templates { "name": "Welcome", "design": { … } } → 201 { template }
# list / fetch
GET /v3/templates → { templates: [ … ] }
GET /v3/templates/:id → { template }
```

## Webhooks

Register a URL to receive signed events (e.g. `template.created`). The create call returns a signing `secret` once; each delivery carries an `x-mailkaro-signature` header, `sha256=HMAC(secret, body)`, so you can verify authenticity.

bash

```
POST /v3/webhooks { "url": "https://you/hook", "events": ["template.created"] }
GET /v3/webhooks → { webhooks: [ … ] } # secret never re-shown
DELETE /v3/webhooks/:id
```

## Send email

MailKaro renders your design and sends it, over **real SMTP** (set `MAILKARO_SMTP_URL`, e.g. `smtp://user:pass@host:587`) or by forwarding to an HTTP provider (set `MAILKARO_EMAIL_PROVIDER_URL` for Resend / SendGrid / SES). Either way, inbox deliverability and the sender reputation stay with your provider / mail server.

bash

```
POST /v3/emails { "template_id": "dsn_…" | "design": {…}, "to": "…", "subject": "…", "locale": "fr" }
 → 202 { status: "queued", id }
 → 200 { status: "suppressed" } when the recipient is on your opt-out list
 → 501 when no provider is configured
```

## Send an email from a design in four steps

- **Configure a sender.** Set MAILKARO_SMTP_URL (for example smtp://user:pass@host:587) or MAILKARO_EMAIL_PROVIDER_URL for an HTTP provider.
- **Authenticate with the server key.** Send Authorization: Bearer sk_live_ with your secret server key. The publishable pk_live_ key is not accepted.
- **Call POST /v3/emails.** Send a JSON body with template_id or design, plus to and subject, and optionally locale.
- **Read the result.** 202 with status queued and an id means the email was accepted. 200 with status suppressed means the recipient is on your opt-out list. 501 means no provider is configured.

Manage a customer opt-out list, the send endpoint skips suppressed recipients (your "do not send" list, not deliverability):

bash

```
POST /v3/emails/suppressions { "email": "…" }
GET /v3/emails/suppressions → { suppressions: [ … ] }
DELETE /v3/emails/suppressions/:email
```

ℹ Sending domains, SPF/DKIM and bounce/complaint deliverability stay with your provider, that's a regulated ESP concern, deliberately outside the editor. PDF/image export endpoints and webhook retry/backoff are on the roadmap. On Node, prefer [email-craft-sdk](https://docs.mailkaro.com/rendering) for rendering (no network round-trip).

Source: https://docs.mailkaro.com/cloud-api
