🏠 Home 📚 All Docs 📖 API Reference ⚙️ Platform Limits
📖
API Reference
Complete reference for authentication, sending emails, attachments, templates, webhooks, error codes, and integration examples across all major languages.
v1.0.012 SectionsREST / JSONBase URL: /api
1
🔐

Authentication

The platform supports two authentication methods depending on your use case.

MethodUse CaseHeader
API KeyApplication-to-application email sendingx-api-key: kf_xx_...
JWT BearerDashboard, admin, tenant managementAuthorization: Bearer <token>
💡

For sending emails from external applications, always use API Key authentication.

🔑 Login (Get JWT Token)
POST/api/auth/login

Request Body:

json
{
  "email": "admin@yourcompany.com",
  "password": "your-password"
}

Response: 200 OK

json
{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": "uuid",
    "email": "admin@yourcompany.com",
    "name": "Admin User",
    "role": "TENANT_ADMIN",
    "tenantId": "uuid"
  }
}
👤 Get Current User
GET/api/auth/me

Requires Authorization: Bearer <token> header. Returns the authenticated user's profile and tenant information.

2
📤

Send Email

The primary endpoint for all email sending from external applications. Accepts the email payload, validates it, stores it, and enqueues it for background delivery.

POST/api/email/send

Authentication: x-api-key header (API Key). No JWT required for sending.

📋 Headers
HeaderRequiredDescription
x-api-keyYesYour application API key (kf_xx_...)
Content-TypeYesMust be application/json
📝 Request Body Fields
FieldTypeRequiredDescription
tostringRequiredRecipient email address
subjectstringRequiredEmail subject line (max 998 chars)
serviceTypeenumRequiredOTP, TRANSACTIONAL, or MARKETING
htmlBodystringConditionalHTML email body — required if no templateId
textBodystringOptionalPlain text fallback body
templateIdstringConditionalUUID of a saved template — required if no htmlBody
templateVariablesobjectOptionalKey-value pairs substituted into template {{var}} placeholders
fromstringOptionalOverride sender email (must be a verified domain)
fromNamestringOptionalOverride sender display name
urgencyenumOptionalHIGH, NORMAL, LOW — affects queue priority tier
referenceTypestringOptionalYour internal reference type (e.g. INVOICE, OTP)
referenceIdstringOptionalYour internal reference ID — stored and returned in history
metadataobjectOptionalCustom metadata stored with the message record
attachmentsarrayOptionalFile attachments — see Section 3
💡 Examples

Transactional Email

bash
curl -X POST https://your-domain.com/api/email/send \
  -H "x-api-key: kf_em_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "user@example.com",
    "subject": "Your Invoice #INV-2024-001",
    "serviceType": "TRANSACTIONAL",
    "htmlBody": "

Invoice Ready

Your invoice for $299.00 is ready.

", "urgency": "HIGH", "referenceType": "INVOICE", "referenceId": "INV-2024-001" }'

OTP Email

bash
curl -X POST https://your-domain.com/api/email/send \
  -H "x-api-key: kf_em_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "user@example.com",
    "subject": "Your Login OTP",
    "serviceType": "OTP",
    "htmlBody": "

Your OTP is: 483921

This code expires in 5 minutes.

", "urgency": "HIGH" }'

Template-based Email

bash
curl -X POST https://your-domain.com/api/email/send \
  -H "x-api-key: kf_em_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "user@example.com",
    "subject": "Welcome to Our Platform",
    "serviceType": "TRANSACTIONAL",
    "templateId": "template-uuid-here",
    "templateVariables": {
      "name": "John Doe",
      "company": "Acme Inc",
      "activationLink": "https://app.example.com/activate/abc123"
    }
  }'

Success Response: 202 Accepted

json
{
  "messageId": "8b33601b-9c94-45f1-8613-6b10973b31b0",
  "status": "queued"
}
🚫 Error Responses
StatusCodeDescription
400GLOBAL_SUPPRESSIONRecipient is on the global block list
400TENANT_SUPPRESSIONRecipient unsubscribed or bounced for your tenant
400WARMUP_LIMITDomain warmup daily limit reached
401API_KEY_INVALIDAPI key missing, revoked, or scope mismatch
403TENANT_SUSPENDEDYour tenant account is suspended
403SENDING_PAUSEDEmail sending is paused by tenant admin
403REPUTATION_SUSPENDEDSending blocked due to low reputation score
429RATE_LIMITEDDaily, hourly, or per-minute limit exceeded
3
📎

Email Attachments

Attach files to any email by including an attachments array in the send request.

📄 Attachment Object
FieldTypeRequiredDescription
filenamestringRequiredFile name shown to recipient (e.g. invoice.pdf)
contentstringRequiredBase64-encoded file content
contentTypestringOptionalMIME type — defaults to application/octet-stream
json
{
  "attachments": [
    {
      "filename": "invoice.pdf",
      "contentType": "application/pdf",
      "content": "JVBERi0xLjQKJcfs..."
    }
  ]
}
📏 Size Limits
ConstraintLimit
Max files per email10 files
Total attachment size (all files combined)10 MB decoded
Single file sizeCounted towards 10 MB total
EncodingBase64 — content must be a valid base64 string
⚠️

The 10 MB limit covers the decoded file size. Base64 encoding adds ~33% overhead in transit, but the limit is enforced on decoded bytes. AWS SES imposes this as a hard platform limit.

Node.js: Attach a File from Disk

typescript
import fs from 'fs';

const base64Content = fs.readFileSync('./invoice.pdf').toString('base64');

await fetch('https://your-domain.com/api/email/send', {
  method: 'POST',
  headers: { 'x-api-key': 'kf_em_xxxxxxxxxxxx', 'Content-Type': 'application/json' },
  body: JSON.stringify({
    to: 'user@example.com',
    subject: 'Your Invoice',
    serviceType: 'TRANSACTIONAL',
    htmlBody: '

See attached invoice.

', attachments: [{ filename: 'invoice.pdf', contentType: 'application/pdf', content: base64Content }], }), });
🌐 Provider Support
ProviderAttachment SupportImplementation
SendGrid✅ FullNative attachments API field
SMTP✅ FullNodemailer multipart/mixed
Brevo✅ FullNative attachment API field
Mailgun✅ FullFormData multipart (auto-switched)
Mailchimp (Mandrill)✅ FullNative attachments API field
AWS SES✅ FullRaw MIME via SendRawEmail
🔄

Disaster Recovery: If the server crashes after DB write but before queue enqueue, the message sweeper re-enqueues the email within 2 minutes — including all attachments stored as JSON in the DB row.

4
📊

Email Status & History

🔍 Get Message Status
GET/api/email/status/:messageId

Requires Authorization: Bearer <token>. Returns the current delivery state for a specific message ID.

json
{
  "id": "uuid",
  "status": "DELIVERED",
  "serviceType": "TRANSACTIONAL",
  "provider": "SENDGRID",
  "retryCount": 0,
  "errorCode": null,
  "createdAt": "2026-06-24T08:46:18.929Z",
  "deliveredAt": "2026-06-24T08:46:21.102Z"
}
StatusDescription
QUEUEDAccepted and waiting in the delivery queue
SENDINGWorker picked it up, actively sending to provider
DELIVEREDProvider confirmed successful delivery
BOUNCEDRecipient mailbox rejected the message
FAILEDPermanent failure after all retries exhausted
DROPPEDSilently dropped (suppression, spam filter, etc.)
📋 Get Email History
GET/api/email/history

Returns paginated email history for the authenticated tenant.

ParamTypeDefaultDescription
pageinteger1Page number
limitinteger50Results per page (max 100)
serviceTypeenumFilter: OTP, TRANSACTIONAL, MARKETING
statusenumFilter: QUEUED, DELIVERED, BOUNCED, FAILED
json
{
  "data": [
    {
      "id": "uuid",
      "serviceType": "TRANSACTIONAL",
      "subject": "Your Invoice #INV-2024-001",
      "status": "DELIVERED",
      "provider": "SENDGRID",
      "retryCount": 0,
      "createdAt": "2026-06-24T08:46:18.929Z",
      "deliveredAt": "2026-06-24T08:46:21.102Z"
    }
  ],
  "pagination": { "page": 1, "limit": 50, "total": 142, "totalPages": 3 }
}
5
🗂️

Templates

Manage reusable email templates with {{variableName}} placeholders. Pass values via templateVariables at send time.

GET/api/templates

Query Parameters: category (OTP, WORKFLOW, INVOICE, ALERT, MARKETING, NOTIFICATION), status (DRAFT, ACTIVE, ARCHIVED)

POST/api/templates
json
{
  "templateCode": "welcome_email",
  "name": "Welcome Email",
  "subject": "Welcome to {{company}}, {{name}}!",
  "htmlBody": "

Welcome, {{name}}!

Thank you for joining {{company}}.

", "textBody": "Welcome, {{name}}! Thank you for joining {{company}}.", "category": "NOTIFICATION", "variables": { "name": "Recipient full name", "company": "Company name" } }
PATCH/api/templates/:id

Update an existing template. Only provided fields are changed.

DELETE/api/templates/:id

Archives the template (soft delete). Archived templates cannot be used for new sends but history is preserved.

💡

Template variables use {{variableName}} syntax in subject, htmlBody, and textBody. Pass values via templateVariables in the send request.

6
📈

Dashboard & Analytics

GET/api/dashboard/stats

Returns today's aggregated delivery metrics for the authenticated tenant.

json
{
  "sentToday": 42100,
  "deliveredToday": 41890,
  "bouncedToday": 148,
  "complaintsToday": 4,
  "deliveryRate": "99.50",
  "bounceRate": "0.351",
  "complaintRate": "0.0095",
  "openRate": "20.1",
  "clickRate": "2.9",
  "reputationScore": 92,
  "reputationStatus": "HEALTHY",
  "warmupActive": false,
  "dailyQuotaUsed": 42100,
  "dailyQuotaLimit": 100000,
  "otpP99Latency": 1.2
}
GET/api/dashboard/provider-stats?days=30

Returns per-provider delivery success rates and volume breakdown over the specified number of days.

GET/api/dashboard/activity?days=7

Returns a daily activity trend — sent, delivered, bounced counts per day for the last N days.

GET/api/dashboard/app-usage

Returns volume breakdown per API key / application, useful for auditing which service is sending the most.

7
🏢

Tenant Self-Service

⏸️ Pause / Resume Sending
POST/api/tenant/self/sending/pause
json
{ "reason": "Maintenance window" }
POST/api/tenant/self/sending/resume
GET/api/tenant/self/sending/status

Returns current sending state: whether sending is active or paused, the reason, and who paused it.

🔀 Routing Rules

Route specific emails to specific providers or override the sender address based on recipient domain, service type, or other criteria.

GET/api/tenant/self/routing-rules
POST/api/tenant/self/routing-rules
PATCH/api/tenant/self/routing-rules/:id
DELETE/api/tenant/self/routing-rules/:id
json
{
  "name": "Gmail recipients via Brevo",
  "description": "Route all emails to gmail.com via Brevo",
  "priority": 10,
  "recipientDomain": "gmail.com",
  "providerName": "BREVO",
  "fromEmail": "notifications@yourdomain.com"
}
FieldDescription
recipientDomainMatch emails going to this domain (e.g. gmail.com)
serviceTypeMatch by type: OTP, TRANSACTIONAL, MARKETING
providerNameOverride provider: SENDGRID, BREVO, MAILGUN, SES, SMTP
fromEmailOverride sender email for matched emails
fromNameOverride sender display name for matched emails
priorityLower number = higher priority (evaluated first)
📋 Audit Logs
GET/api/tenant/self/audit-logs?page=1&limit=50&action=EMAIL_SENT

Paginated audit trail of all actions taken by the tenant — emails sent, settings changed, keys created/revoked, etc.

🗑️ Data Purge Configuration
GET/api/tenant/self/purge-config
PATCH/api/tenant/self/purge-config
POST/api/tenant/self/purge/execute
json
{
  "dataRetentionDays": 45,
  "auditRetentionDays": 45,
  "autoPurgeEnabled": true
}
⚠️

Manual purge via POST /purge/execute is irreversible. Data older than the retention window is permanently deleted.

8
🔔

Webhooks

The platform accepts delivery event webhooks from providers. Configure these URLs in your provider dashboards to receive real-time delivery events (delivered, bounced, complained, etc.).

ProviderWebhook URLSecurity
SendGridPOST /api/webhooks/sendgrid/eventsEd25519 signature validation
BrevoPOST /api/webhooks/brevo/eventsIP allowlist + payload check
MailgunPOST /api/webhooks/mailgun/eventsHMAC-SHA256 signature
🔐

SendGrid webhooks are verified using Ed25519 signature validation. Events with invalid or missing signatures are automatically rejected with 403 Forbidden. No unsigned events are ever processed.

9
⚠️

Error Codes

HTTP Status Codes

CodeMeaning
200Success
201Created
202Accepted — email queued for delivery
400Bad request / validation error
401Authentication failed
403Forbidden — permissions, suspension, or paused
404Resource not found
429Rate limit exceeded
500Internal server error

Application Error Codes

CodeDescriptionRecommended Action
API_KEY_INVALIDKey missing, revoked, or wrong scopeCheck API key status and allowed categories in Dashboard
TENANT_NOT_FOUNDTenant does not existContact platform admin
TENANT_SUSPENDEDTenant account suspendedContact platform admin
SENDING_PAUSEDSending paused by tenant adminResume via dashboard or POST /sending/resume
REPUTATION_SUSPENDEDReputation score below 50Review bounce and complaint rates
GLOBAL_SUPPRESSIONEmail on platform-wide block listDo not retry this recipient
TENANT_SUPPRESSIONEmail bounced or unsubscribed for this tenantDo not retry this recipient
RATE_LIMITEDSending limits exceededWait and retry, or request a limit increase
WARMUP_LIMITDomain warmup daily cap reachedWait until next calendar day
INTERNAL_ERRORPlatform errorRetry with exponential backoff
10
⏱️

Rate Limits

API Rate Limits

EndpointLimit
All API endpoints100 requests / minute per IP
POST /api/email/sendSubject to tenant sending limits (see below)

Tenant Sending Limits

WindowDefaultConfigurable
Per minute60Yes
Per hour1,000Yes
Per day10,000Yes

When rate limited, all responses include:

json
{
  "error": "Daily sending limit reached",
  "code": "RATE_LIMITED"
}
📈

Limits are configurable per tenant by the platform admin. Enterprise plans support per-minute rates of 5,000+ and daily volumes of 1,000,000+. See Platform Billing Plans for the full tier breakdown.

11
💻

Integration Examples

Node.js / TypeScript
typescript
const KODEFAST_EMAIL_URL = 'https://your-domain.com/api';
const API_KEY = 'kf_em_xxxxxxxxxxxx';

async function sendTransactionalEmail(
  to: string,
  subject: string,
  htmlBody: string,
  referenceId?: string,
) {
  const response = await fetch(`${KODEFAST_EMAIL_URL}/email/send`, {
    method: 'POST',
    headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      to, subject, serviceType: 'TRANSACTIONAL', htmlBody,
      referenceType: 'NOTIFICATION', referenceId,
    }),
  });

  if (!response.ok) {
    const err = await response.json();
    throw new Error(`Email failed: ${err.code} - ${err.error}`);
  }

  const result = await response.json();
  return result.messageId; // use to track delivery status
}

await sendTransactionalEmail(
  'user@example.com',
  'Order Confirmed',
  '

Order #12345 Confirmed

Thank you!

', 'ORDER-12345', );
Python
python
import requests

KODEFAST_EMAIL_URL = "https://your-domain.com/api"
API_KEY = "kf_em_xxxxxxxxxxxx"

def send_email(to, subject, html_body, service_type="TRANSACTIONAL"):
    response = requests.post(
        f"{KODEFAST_EMAIL_URL}/email/send",
        headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
        json={"to": to, "subject": subject, "serviceType": service_type, "htmlBody": html_body},
    )
    response.raise_for_status()
    return response.json()["messageId"]

message_id = send_email(
    to="user@example.com",
    subject="Your Login OTP: 483921",
    html_body="

Your OTP is: 483921

Expires in 5 minutes.

", service_type="OTP", ) print(f"Queued: {message_id}")
Java / Spring Boot
java
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.util.Map;

public class KodefastEmailClient {
    private static final String BASE_URL = "https://your-domain.com/api";
    private final String apiKey;
    private final RestTemplate restTemplate = new RestTemplate();

    public KodefastEmailClient(String apiKey) { this.apiKey = apiKey; }

    public String sendEmail(String to, String subject, String htmlBody, String serviceType) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("x-api-key", apiKey);
        headers.setContentType(MediaType.APPLICATION_JSON);

        Map body = Map.of(
            "to", to, "subject", subject, "serviceType", serviceType, "htmlBody", htmlBody
        );

        ResponseEntity response = restTemplate.postForEntity(
            BASE_URL + "/email/send", new HttpEntity<>(body, headers), Map.class
        );
        return (String) response.getBody().get("messageId");
    }
}
PHP
php
function sendKodefastEmail(string $to, string $subject, string $htmlBody): string {
    $data = json_encode([
        'to' => $to, 'subject' => $subject,
        'serviceType' => 'TRANSACTIONAL', 'htmlBody' => $htmlBody,
    ]);

    $ch = curl_init('https://your-domain.com/api/email/send');
    curl_setopt_array($ch, [
        CURLOPT_POST => true, CURLOPT_POSTFIELDS => $data,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => ['x-api-key: kf_em_xxxxxxxxxxxx', 'Content-Type: application/json'],
    ]);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($httpCode !== 202) throw new Exception("Email send failed: " . $response);
    return json_decode($response, true)['messageId'];
}
C# / .NET
csharp
using System.Net.Http;
using System.Text;
using System.Text.Json;

public class KodefastEmailClient {
    private readonly HttpClient _client;
    private const string BaseUrl = "https://your-domain.com/api";

    public KodefastEmailClient(string apiKey) {
        _client = new HttpClient();
        _client.DefaultRequestHeaders.Add("x-api-key", apiKey);
    }

    public async Task SendEmailAsync(string to, string subject, string htmlBody, string serviceType = "TRANSACTIONAL") {
        var payload = new { to, subject, serviceType, htmlBody };
        var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
        var response = await _client.PostAsync($"{BaseUrl}/email/send", content);
        response.EnsureSuccessStatusCode();
        var result = JsonSerializer.Deserialize(await response.Content.ReadAsStringAsync());
        return result.GetProperty("messageId").GetString()!;
    }
}
cURL (Quick Test)
bash
curl -X POST https://your-domain.com/api/email/send \
  -H "x-api-key: kf_em_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "recipient@example.com",
    "subject": "Test Email",
    "serviceType": "TRANSACTIONAL",
    "htmlBody": "

Hello!

This is a test email from Kodefast.

" }'
12
📄

Platform Docs API

Two public endpoints return this platform documentation as JSON. No authentication required.

GET/api/docs/api-documentation
GET/api/docs/email-limits
json
{
  "title": "Kodefast Email Platform — API Documentation",
  "slug": "api-documentation",
  "content": "# Kodefast Email Platform...",
  "lastModified": "2026-07-16T10:00:00.000Z"
}
📖

Both endpoints also accept Accept: text/markdown header to receive the raw markdown content directly without the JSON wrapper.

🚀

Quick Start Checklist

  1. Get API Key — Tenant admin creates one via Dashboard → Settings → API Keys
  2. Set allowed categories — OTP, TRANSACTIONAL, or both (MARKETING requires separate key)
  3. Send your first emailPOST /api/email/send with x-api-key header
  4. Track deliveryGET /api/email/status/:messageId or check the Dashboard activity page
  5. Monitor health — Dashboard shows delivery rate, reputation score, and quota usage in real time
🔒
🔒

Security Notes

AreaMechanism
API Keys at restHashed with SHA-256 — never stored in plaintext
Provider credentialsEncrypted with AES-256-GCM at rest
Sensitive fields in transitRSA-OAEP encrypted from frontend to backend
Webhook eventsEd25519 signature verification (SendGrid) — unsigned events rejected
Audit trailEvery action logged — HIPAA/GDPR compliant
Recipient PIIRecipient emails stored as encrypted + hashed — never in plaintext

On this page