Documentation menu

Libraries and tools

AI prompt

Most projects do not need a person to read this documentation before they can send their first email. Hand the briefing below to the coding assistant you already use — in a terminal, an editor or a chat window — and it will wire this API into the project: the right endpoint, a sender on a verified domain, idempotency keys, the error cases that must not be swallowed, and webhook signature checking when the project needs delivery state.

How to use it

  1. Create an API key in the console (Email API → API keys) and keep it to hand.
  2. Open the project you want to add email to and start your assistant there.
  3. Paste the prompt, or let the assistant read it straight from the web:
Shell
curl -fsSL https://tsmart.ai/ai-prompt.txt

# or hand it to an assistant that takes a prompt on stdin
curl -fsSL https://tsmart.ai/ai-prompt.txt | your-assistant
  1. Put the key in the environment variable the prompt asks for (TSMART_API_KEY) and set the sender address to your own verified domain.

Before it can send

The from address has to be on a domain this account has verified, otherwise the API answers 403 domain_not_verified. See Domains and DNS; the console shows the same prompt with your own domain already filled in, under Email API → Overview.

The prompt

Plain text, about 7 KB. Download ai-prompt.txt or copy it from here.

ai-prompt.txt
# Task: send transactional email through our own email API

Add outgoing transactional email (sign-up confirmation, password reset, receipts, notifications) to this
project using the HTTP API described below. Do not add any other email provider, SMTP library or SDK, and do
not change the project's existing style, framework or dependency manager beyond what this task needs.

## The API

- Base URL: https://send.tsmart.ai
- Auth: `Authorization: Bearer <key>` on every request. Keys look like `ts_xxxxxxxx_…`.
- Bodies and responses are JSON. Errors always come back as
  `{ "statusCode": <http status>, "name": "<machine readable>", "message": "<human readable>" }`.
- Lists answer `{ "object": "list", "has_more": <bool>, "data": [...] }` and take `limit` (1-100, default 20)
  with `after` or `before` (an object id).

### Send one email — POST /emails

```http
POST https://send.tsmart.ai/emails
Authorization: Bearer <key>
Content-Type: application/json
Idempotency-Key: signup-welcome/<user id>        # optional but recommended, 1-256 chars, remembered 24h

{
  "from": "Your Product <no-reply@your-domain.com>",
  "to": ["person@example.com"],                  // up to 50 recipients
  "subject": "Welcome",
  "html": "<p>Hello</p>",                        // html or text (or both) is required
  "text": "Hello",
  "reply_to": "support@your-domain.com",             // optional, string or array
  "cc": [], "bcc": [],                           // optional
  "tags": [{ "name": "category", "value": "welcome" }],   // optional, for filtering in the dashboard
  "scheduled_at": "in 10 min",                   // optional: ISO 8601 or a plain phrase like "in 1 hour"
  "attachments": [{ "filename": "receipt.pdf", "content": "<base64>", "content_type": "application/pdf" }]
}
```

Answer: `201 { "id": "<uuid>" }`. Keep that id — every later call about this message uses it.

### The rest

| Call | What it does |
|---|---|
| `POST /emails/batch` | Up to 100 messages in one request (no attachments). Header `x-batch-validation: strict` rejects the whole batch on the first bad entry; `permissive` sends the good ones. |
| `GET /emails`, `GET /emails/{id}` | List and read sent messages (bodies are not stored). |
| `GET /emails/{id}/events` | What happened to one message, oldest first. |
| `PATCH /emails/{id}`, `POST /emails/{id}/cancel` | Reschedule or cancel a message that has not gone out yet. |
| `GET /emails/metrics` | Counts and rates per period or domain. |
| `GET/POST /domains`, `POST /domains/{id}/verify` | Add a sending domain and check its DNS. |
| `GET/POST /suppressions`, `DELETE /suppressions/{id-or-email}` | The do-not-send list. Hard bounces and spam complaints land here automatically. |
| `GET/POST /webhooks` | Delivery events pushed to your endpoint. |
| `GET /logs` | Request log, 30 days. |

Event names: `scheduled, sent, delivered, delivery_delayed, bounced, complained, opened, clicked, failed,
suppressed, canceled`.

### Webhooks (only if this project needs delivery state)

Register an endpoint with `POST /webhooks` and verify every delivery before trusting it: the request carries
`webhook-id`, `webhook-timestamp` and `webhook-signature`. The signature is
`base64(HMAC_SHA256(secret, "<webhook-id>.<webhook-timestamp>.<raw body>"))`, where `secret` is the part of the
signing secret after `whsec_`, base64-decoded. Compare in constant time, reject a timestamp more than five
minutes old, and answer 2xx quickly — the sender retries at 5s, 5min, 30min, 2h, 5h, 10h, 10h.

## Rules you must follow

1. **The key comes from the environment**, never from source, a config file in the repository or this prompt.
   Use `TSMART_API_KEY` unless the project already has a naming convention for secrets; add it to
   `.env.example` (empty) and to the deployment notes, and make sure `.env` is git-ignored.
2. **The From: address must be on a domain this account has verified**, otherwise the API answers 403
   `domain_not_verified`. Use `no-reply@your-domain.com` unless the project already has a sender address on
   `your-domain.com`. Set `reply_to` to an address a human reads.
3. **Send one email per request with a stable `Idempotency-Key`** when the call can be retried (queue worker,
   webhook handler, user-triggered action). Same key plus same body inside 24h returns the first result
   instead of sending twice; same key with a different body is a 409.
4. **Never block a user-facing request on sending.** Put the call behind the project's existing queue or
   background task if it has one; otherwise fire it after the response is produced and log failures.
5. **Handle the documented errors**, do not swallow them: 401/403 (key or domain), 422 (body), 409
   (idempotency), 429 (rate limit — honour `retry-after`; the limit is 10 requests/second per account plus an
   hourly recipient quota), 502 (mail server refused). Log `name` and `message`; never log the key.
6. **No fallback sender.** If the API refuses the message, report it. Retrying from some other address makes
   mail arrive from a domain the recipient does not recognise, which is worse than mail that did not go out.
7. **Respect the suppression list.** A recipient that bounced or complained is dropped by the API; treat a
   `suppressed` event as a permanent "do not send", not a transient failure.
8. **Keep content plain and accessible**: a text part next to the html, no remote scripts, no tracking pixels
   the project did not ask for.

## Helper packages (optional)

If the project is Node or Python and you prefer a typed client over `fetch`:

```bash
npm install https://tsmart.ai/sdk/node/tsmart-sdk-1.1.1.tgz
pip install https://tsmart.ai/sdk/python/tsmart-1.1.1-py3-none-any.whl
```

```js
import { Tsmart } from "@tsmart/sdk";
const tsmart = new Tsmart();                       // reads TSMART_API_KEY
const { data, error } = await tsmart.emails.send({ from: "Your Product <no-reply@your-domain.com>", to: ["person@example.com"], subject: "Welcome", html: "<p>Hello</p>" });
```

A command line tool is available too (`npm install -g https://tsmart.ai/cli/tsmart-cli-1.1.0.tgz`), useful for a smoke test before you write code.

Clients written for the common Bearer-key email API style work unchanged when their base URL is set to
`https://send.tsmart.ai`.

## What I expect from you

1. Find where this project already sends email (search for existing mail code, templates and queues) and
   either replace that transport or add one if there is none. Report what you found before changing it.
2. Implement a small module with one function per message type the project needs, not a call scattered in
   handlers. Reuse the project's logging, error types and test style.
3. Add the environment variable, the example file entry and a line in the project's README or deployment doc.
4. Write one test that proves the payload is built correctly and that a 4xx is surfaced rather than
   swallowed. Do not call the live API from tests.
5. Tell me what to set in production and how to verify it: one real test message, where to see its state, and
   what to check if it does not arrive.

Full reference: https://tsmart.ai/docs

After the assistant is done

  • Send one real message and check it in Email API → Emails: every message has an id, a status and an event history.
  • Check that the key lives in the environment and not in the repository, and that .env is ignored by git.
  • If the project needs delivery state, confirm the webhook endpoint verifies the signature — see Webhooks.
  • Read Limits and errors so the retry behaviour the assistant wrote matches the real limits.