Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions node/send-email-with-keplars/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
.env
69 changes: 69 additions & 0 deletions node/send-email-with-keplars/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Send Email with Keplars

Send transactional emails from your Appwrite Function using the [Keplars](https://keplars.com) priority-queue API with instant, high, async, or bulk delivery.

## Usage

### POST /

Send an email.

**Request body:**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `to` | string \| string[] | Yes | Recipient email address(es) |
| `from` | string | Yes | Sender address (must be verified in Keplars) |
| `subject` | string | Yes | Email subject line |
| `body` | string | No | Email body (HTML or plain text). Required if `template_id` is not set. |
| `from_name` | string | No | Sender display name |
| `template_id` | string | No | Keplars template ID. Required if `body` is not set. |
| `params` | object | No | Template variables |

**Success response:**

```json
{
"ok": true,
"data": {
"id": "msg_...",
"status": "queued"
}
}
```

**Error response:**

```json
{
"ok": false,
"error": "Missing required fields: to, from, subject"
}
```

## Configuration

| Variable | Description | Required |
| --- | --- | --- |
| `KEPLARS_API_KEY` | Your Keplars API key (`kms_...`) | Yes |

## Deployment

1. Create a new Appwrite Function
2. Add the environment variables above
3. Deploy the function

**Example request:**

```bash
curl -X POST https://[REGION].appwrite.io/v1/functions/[FUNCTION_ID]/executions \
-H "X-Appwrite-Project: [PROJECT_ID]" \
-H "Content-Type: application/json" \
-d '{
"to": "user@example.com",
"from": "hello@yourdomain.com",
"subject": "Welcome!",
"body": "<h1>Welcome!</h1><p>Thanks for signing up.</p>"
}'
```

9 changes: 9 additions & 0 deletions node/send-email-with-keplars/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
declare global {
namespace NodeJS {
interface ProcessEnv {
KEPLARS_API_KEY: string;
}
}
}

export {};
13 changes: 13 additions & 0 deletions node/send-email-with-keplars/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "send-email-with-keplars",
"version": "1.0.0",
"description": "Send transactional emails using the Keplars priority-queue API.",
"main": "src/main.js",
"type": "module",
"scripts": {
"format": "prettier --write ."
},
"devDependencies": {
"prettier": "^3.2.5"
}
}
71 changes: 71 additions & 0 deletions node/send-email-with-keplars/src/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { throwIfMissing } from './utils.js';

const ENDPOINT = 'https://api.keplars.com/api/v1/send-email/async';

export default async ({ req, res, log, error }) => {
throwIfMissing(process.env, ['KEPLARS_API_KEY']);

if (req.method !== 'POST') {
return res.json({ ok: false, error: 'Method not allowed' }, 405);
}

const { to, from, from_name, subject, body, template_id, params } =
req.body ?? {};

if (!to || !from || !subject) {
return res.json(
{ ok: false, error: 'Missing required fields: to, from, subject' },
400
);
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.

if (!body && !template_id) {
return res.json(
{
ok: false,
error: 'Either body or template_id must be provided',
},
400
);
}

const payload = {
to: Array.isArray(to) ? to : [to],
from,
subject,
};

if (from_name) payload.from_name = from_name;
if (body) payload.body = body;
if (template_id) payload.template_id = template_id;
if (params && typeof params === 'object') payload.params = params;

try {
const response = await fetch(ENDPOINT, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KEPLARS_API_KEY}`,
'Content-Type': 'application/json',
'User-Agent': 'keplars-appwrite/1.0.0',
},
body: JSON.stringify(payload),
});

const data = await response.json();

if (!response.ok) {
const message =
typeof data?.message === 'string'
? data.message
: `Keplars API error: ${response.status}`;
error(message);
return res.json({ ok: false, error: message }, response.status);
}

log(`Email sent to ${Array.isArray(to) ? to.join(', ') : to}`);
return res.json({ ok: true, data });
} catch (err) {
error(err.message);
return res.json({ ok: false, error: 'Failed to send email' }, 500);
}
};
11 changes: 11 additions & 0 deletions node/send-email-with-keplars/src/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @param {Record<string, string>} obj
* @param {string[]} keys
* @throws {Error}
*/
export function throwIfMissing(obj, keys) {
const missing = keys.filter((key) => !obj[key]);
if (missing.length > 0) {
throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
}
}