Taifa MailTaifa Mail Docs
Sending Emails

Send via REST API

Send service notices, receipts, alerts, and other official email programmatically using the Taifa Mail REST API.

The REST API is the most flexible way to send official email through Taifa Mail. Use it for government portals, case-management systems, payment receipts, appointment reminders, and service-status notices. All requests go to https://govconnect.ke/v1/ and require an API key.

Authentication

Include your API key in the Authorization header on every request. API keys are prefixed with tfm_k_:

Authorization: Bearer tfm_k_YOUR_API_KEY

Generate API keys at Developer -> API Keys.

POST /v1/emails/

Send a single email. Note the trailing slash on the path.

Request body

FieldTypeRequiredDescription
from_objectYesSender address. Must be an allocated, active mailbox on a verified domain.
from_.emailstringYesSender email address.
from_.namestringNoSender display name.
toarrayYesArray of recipient {email, name} objects. At least one required.
to[].emailstringYesRecipient email address.
to[].namestringNoRecipient display name.
subjectstringYesEmail subject line.
htmlstringNoHTML body. At least one of html or text is required.
textstringNoPlain text body. At least one of html or text is required.
ccarrayNoArray of {email, name} objects for CC recipients.
bccarrayNoArray of {email, name} objects for BCC recipients.
reply_toobjectNo{email, name} object for the Reply-To header.
headersobjectNoKey-value pairs of custom email headers.
tagsarrayNoArray of strings for categorization and filtering.
send_atstringNoISO 8601 datetime to schedule delivery. Omit to send immediately. See Scheduling.
attachmentsarrayNoArray of attachment objects (see below).

Attachments

Each attachment is an object with the file body base64-encoded:

FieldTypeRequiredDescription
filenamestringYesFile name. Must not contain path separators (/, \) or NUL.
content_base64stringYesFile body as base64. No data: URL prefix.
content_typestringNoMIME type. Defaults to application/octet-stream.

Each file is capped at 24 MiB, and the combined size of all attachments is also capped at 24 MiB. Oversized attachments return 413.

Example: cURL

curl -X POST https://govconnect.ke/v1/emails/ \
  -H "Authorization: Bearer tfm_k_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from_": {
      "email": "info@health.go.ke",
      "name": "Ministry of Health"
    },
    "to": [
      {
        "email": "citizen@example.com",
        "name": "Jane Doe"
      }
    ],
    "subject": "Your service request has been received",
    "html": "<p>Your service request has been received and is being reviewed.</p>",
    "text": "Your service request has been received and is being reviewed.",
    "tags": ["service-request", "citizen-notice"]
  }'

Example: Node.js (fetch)

const response = await fetch("https://govconnect.ke/v1/emails/", {
  method: "POST",
  headers: {
    "Authorization": "Bearer tfm_k_YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from_: {
      email: "info@health.go.ke",
      name: "Ministry of Health",
    },
    to: [{ email: "citizen@example.com", name: "Jane Doe" }],
    subject: "Your service request has been received",
    html: "<p>Your service request has been received and is being reviewed.</p>",
    text: "Your service request has been received and is being reviewed.",
    tags: ["service-request", "citizen-notice"],
  }),
});
 
const data = await response.json();
console.log(data.id); // email ID

Example: Python (requests)

import requests
 
response = requests.post(
    "https://govconnect.ke/v1/emails/",
    headers={"Authorization": "Bearer tfm_k_YOUR_API_KEY"},
    json={
        "from_": {
            "email": "info@health.go.ke",
            "name": "Ministry of Health",
        },
        "to": [{"email": "citizen@example.com", "name": "Jane Doe"}],
        "subject": "Your service request has been received",
        "html": "<p>Your service request has been received and is being reviewed.</p>",
        "text": "Your service request has been received and is being reviewed.",
        "tags": ["service-request", "citizen-notice"],
    },
)
 
data = response.json()
print(data["id"])

Response

A successful request returns 202 Accepted:

{
  "id": "3f8c1e2a-9b4d-4c7e-8a1f-2d6b5e9c0a3d",
  "status": "queued",
  "message_id": "<...@govconnect.ke>",
  "rejection_reason": null
}
FieldDescription
idThe email's UUID. Use this to query status, events, or retry.
statusqueued for an immediate send, scheduled if send_at was provided.
message_idRFC 5322 Message-ID header value.
rejection_reasonReason the email was rejected, or null if accepted.

A 202 means the email was accepted for delivery, not that it was delivered. Use webhooks or the events endpoint to track delivery status.

POST /v1/emails/validate

Dry-run validation. Checks whether an email would send without actually sending it. It validates the sender address, domain verification, recipients, content, and account restrictions. The request body is identical to POST /v1/emails/.

curl -X POST https://govconnect.ke/v1/emails/validate \
  -H "Authorization: Bearer tfm_k_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from_": {"email": "info@health.go.ke"},
    "to": [{"email": "citizen@example.com"}],
    "subject": "Test",
    "html": "<p>Hello</p>"
  }'

Response:

{
  "valid": true,
  "can_send": true,
  "issues": []
}

If a check fails, issues contains one or more {field, error} objects and both valid and can_send are false.

GET /v1/emails/:id

Retrieve a single email by its UUID, including its bodies, headers, and event history.

curl https://govconnect.ke/v1/emails/3f8c1e2a-9b4d-4c7e-8a1f-2d6b5e9c0a3d \
  -H "Authorization: Bearer tfm_k_YOUR_API_KEY"

GET /v1/emails/:id/events

List every tracked event for an email (queued, sent, delivered, opened, clicked, bounced, cancelled, and so on). Each event has an event_type, optional metadata, and a created_at timestamp.

curl https://govconnect.ke/v1/emails/3f8c1e2a-9b4d-4c7e-8a1f-2d6b5e9c0a3d/events \
  -H "Authorization: Bearer tfm_k_YOUR_API_KEY"

POST /v1/emails/:id/retry

Retry a previously failed email. Only emails with status bounced, rejected, or failed can be retried; any other status returns 409 Conflict. The retry creates a new email row linked back to the original; the original is preserved. The response shape matches POST /v1/emails/.

curl -X POST https://govconnect.ke/v1/emails/3f8c1e2a-9b4d-4c7e-8a1f-2d6b5e9c0a3d/retry \
  -H "Authorization: Bearer tfm_k_YOUR_API_KEY"

GET /v1/emails/search

Search emails by recipient, sender, subject, status, domain, or tag. The free-text q parameter accepts inline tokens that translate to structured filters:

TokenEffect
to:gmail.comSubstring match on the recipient list.
from:hello@govconnect.keSubstring match on the sender address.
status:bouncedExact status match.
domain:govconnect.keSubstring match on the sending domain.
tag:welcomeExact tag match.

Any remaining words become a free-text search across sender, subject, and recipients. Status and tag can also be passed as the separate query parameters status and tag. Results are paginated with page (default 0) and limit (default 20, max 100).

curl "https://govconnect.ke/v1/emails/search?q=status:bounced+to:gmail.com" \
  -H "Authorization: Bearer tfm_k_YOUR_API_KEY"

GET /v1/emails/

List emails with an optional status filter and page / limit pagination.

Error handling

StatusMeaning
400 Bad RequestInvalid attachment encoding, bad send_at value, or batch size over the 500-email cap.
401 UnauthorizedMissing or invalid API key.
403 ForbiddenAccount is restricted or sending is paused.
404 Not FoundThe referenced email does not exist.
409 ConflictThe email cannot be retried in its current status.
413 Payload Too LargeAttachment exceeds the per-file or combined size cap.
422 Unprocessable EntityValidation error in the request body.

On this page