# emailer
## 🎨 Design System — MANDATORY
All Das Experten design principles live in [`Design/`](./Design/) — brand voice, color palette, typography, spacing, components, casing rules.
- [`Design/README.md`](./Design/README.md) — the design system source of truth
- [`Design/colors_and_type.css`](./Design/colors_and_type.css) — ready-to-use CSS design tokens
**Rule for humans and AI agents:** any UI, page, banner, document, or visual output produced from this repo MUST follow these principles. Consult `Design/` before styling anything.
A Google Apps Script web app that turns a single JSON call into a fully-formed
email — either a brand new message or a reply inside an existing Gmail thread.
It is one of the three pillars of the Das Experten ecosystem (alongside
`dasoperator` for ERP and `arams-db` for the knowledge base) and is designed
to be triggered by upstream agents with no human in the loop.
## Architecture
```
┌─────────────────┐
│ Caller (HTTP) │
└────────┬────────┘
│ JSON payload
▼
┌─────────────┐
│ Main.gs │ doPost / handleRequest
└──┬───┬───┬──┘
new mode │ │ │ reply mode
│ │ │
┌─────────────┘ │ └─────────────┐
│ │ │
▼ ▼ ▼
SkillsBridge DocBuilder / ThreadResolver
(stub) DriveManager (Gmail readonly)
│ │ │
└─────────┬───────┘ │
▼ ▼
EmailComposer ◄───── thread context
│
▼
GmailSender ──► sendNew / replyToThread
│
▼
Logger.gs ──► Google Sheet (audit trail)
```
## Call format
The web app accepts a single JSON object via HTTP POST.
```jsonc
{
"task": "string, human-readable label",
"recipient": "user@example.com", // required if no thread_id
"subject": "string", // optional, ignored on reply
"content_brief": "what to write about",
"attachment_link": "https://drive.google.com/...", // optional
"skill_call": "bannerizer", // optional
"context": "extra info for the skill", // optional
"thread_id": "17abcd...", // optional, triggers REPLY mode
"in_reply_to_message_id": "<id@mail.gmail.com>" // optional, specific message
}
```
Response shape:
```json
{
"success": true,
"mode": "new",
"message_id": "...",
"thread_id": "...",
"doc_link": "https://docs.google.com/...",
"log_id": "42",
"error": null
}
```
## Three scenarios
### Scenario A — New email with a pre-made attachment link
The caller already produced an artifact (a banner, a slide deck, a report).
Emailer skips DocBuilder/DriveManager and just sends the email with the link.
```json
{
"task": "Send banner v3 to client",
"recipient": "client@example.com",
"subject": "Banner v3 — review please",
"content_brief": "Final round of revisions on the spring campaign banner.",
"attachment_link": "https://drive.google.com/file/d/PRE_MADE_ID/view"
}
```
### Scenario B — New email, content generated by a skill + Doc built inside Emailer
No pre-made artifact. Emailer calls into `my-skills` to generate content,
hands it to DocBuilder, saves the Doc via DriveManager, and sends the
resulting link.
```json
{
"task": "Weekly tech digest for the team",
"recipient": "team@dasexperten.de",
"content_brief": "Summarize the three most important infra changes this week.",
"skill_call": "technolog",
"context": "Audience: senior engineers. Tone: terse, technical."
}
```
### Scenario C — Reply inside an existing Gmail thread
`thread_id` flips Emailer into reply mode. ThreadResolver loads the thread,
EmailComposer inherits the subject and acknowledges the last message, and
GmailSender uses Gmail's native `thread.reply()` so In-Reply-To and References
headers are set correctly. Works with or without a skill / attachment.
```json
{
"task": "Follow up with vendor on Q2 invoice",
"content_brief": "Confirm we received the corrected invoice and ask for ETA on payment portal.",
"thread_id": "17abcd1234ef5678",
"in_reply_to_message_id": "<CAJxyz@mail.gmail.com>"
}
```
## Setup
1. Install [clasp](https://github.com/google/clasp): `npm i -g @google/clasp`.
2. `clasp login` with Aram's Google account.
3. Copy `.clasp.json.example` to `.clasp.json` and fill in `scriptId` from the
Apps Script project URL.
4. `clasp push` to upload `src/*.gs` and `appsscript.json`.
5. Deploy as a web app (Execute as: me · Access: anyone).
6. Set Script Properties (see `SETUP_NOTES.md`).
## Future plans
- Wire `skills/` as a git submodule once `github.com/dasexperten/my-skills`
exists. `SkillsBridge.gs` already declares the contract; only the body
changes when the submodule lands.
- Replace the Sheet-based logger with BigQuery once volume justifies it.
- Add an idempotency key to `doPost` so retries from upstream agents do
not produce duplicate emails.
## DESIGN SSOT
The single source of truth for ANY design matter — design systems,
UI/UX rules, banners, product cards, characters, packaging,
design skills, design decisions in any project — is:
https://github.com/dasexperten/deSIGNER
Design files in this repository are working copies / backups.
On any conflict, deSIGNER wins. Before starting any design task,
read deSIGNER/MASTER_INDEX.md.