# Gaspar API Skill
Operational runbook for working with [Gaspar](https://gaspar.hidagama.com), an email-campaign platform with a REST API at `https://api.hidagama.com/api/gaspar/`. **Read this before creating any campaign.** Every section maps to a failure mode hit in production — the rules exist because someone skipped them.
> If you only have 60 seconds: read the [Pre-flight checklist](#3-pre-flight-checklist-before-any-campaign-fires) and follow it religiously.
---
## Table of contents
1. [API quickstart — auth, base URL, key concepts](#1-api-quickstart)
2. [Time conversion — the #1 mistake](#2-time-conversion--the-1-mistake)
3. [Pre-flight checklist (before any campaign fires)](#3-pre-flight-checklist-before-any-campaign-fires)
4. [Full campaign lifecycle](#4-full-campaign-lifecycle)
5. [Campaign body shape (POST /campaigns)](#5-campaign-body-shape)
6. [HTML email design — table-based, mobile-safe, brand-perfect](#6-html-email-design)
7. [Subject lines — craft + limits](#7-subject-lines)
8. [Recipient management — manual, CSV, Sheets, audiences](#8-recipient-management)
9. [Template syntax (merge fields, fallbacks, special variables)](#9-template-syntax)
10. [The watchdog — and how to unpause without re-pause](#10-the-watchdog)
11. [Click-tracking gotcha + UTM workaround](#11-click-tracking)
12. [Frequency rules + sequence staggering](#12-frequency-rules)
13. [Quick-send vs full campaign vs test-send](#13-quick-send-vs-full-campaign-vs-test-send)
14. [Sequences (multi-step follow-ups)](#14-sequences)
15. [Forms (lead capture)](#15-forms)
16. [Suppressions + replies + inbox](#16-suppressions-replies-inbox)
17. [Analytics — opens, clicks, GA4, deliverability](#17-analytics)
18. [Complete API endpoint reference](#18-complete-endpoint-reference)
19. [MCP tools mapping](#19-mcp-tools-mapping)
20. [Common campaign patterns (cold outreach, T-1 reminder, recovery)](#20-common-patterns)
21. [Five mistakes worth memorizing](#21-five-mistakes)
22. [When to invoke this skill](#22-when-to-invoke)
---
## 1. API quickstart
**Base URL:** `https://api.hidagama.com/api/gaspar/`
**App URL (UI only — do not call):** `https://gaspar.hidagama.com/app`
**Auth:** Bearer token in `Authorization` header.
```bash
export GASPAR_API_KEY=gsk_... # mint from gaspar.hidagama.com → Settings → API
curl -sS -H "Authorization: Bearer $GASPAR_API_KEY" \
https://api.hidagama.com/api/gaspar/auth/check
```
**Key shape:** Gaspar API keys start with `gsk_`. They're scoped — recommended scopes for AI-assistant integrations:
- `campaigns:read` — list campaigns, recipients, stats
- `campaigns:write` — create + edit drafts (no send)
- `campaigns:launch` — actually fire campaigns (leave OFF when an AI controls the key)
**Never** commit the key. **Never** print it in chat output. Cache in `GASPAR_API_KEY` env var, macOS Keychain, 1Password CLI, GitHub Actions secret — never inline.
**First call to confirm everything works:**
```bash
curl -sS -H "Authorization: Bearer $GASPAR_API_KEY" \
https://api.hidagama.com/api/gaspar/auth/check | jq
```
Returns `{ ok: true, user_id, via, scopes, plan, active, ... }`. If `ok: false` or 401, the key is wrong or expired.
**Wrong URLs that return "Not found":**
- `gaspar.hidagama.com/api/...` — that's the app frontend
- `api.hidagama.com/api/gaspar/v1/...` — no version prefix
- `api.hidagama.com/gaspar/...` — missing `/api/` segment
---
## 2. Time conversion — the #1 mistake
Gaspar stores `scheduled_start_at` as **UTC milliseconds since epoch**. Operators always think in local time. Confusing the two ships campaigns 3 hours late.
| Operator says | What you compute | Sanity check |
|---|---|---|
| "Send Friday at 6pm IL" | IL is UTC+3 in summer → UTC 15:00 Fri → ms epoch | `date -u -r $((TS_MS/1000))` should print Fri 15:00 UTC |
| "Send Monday morning Chicago time" | Chicago is UTC-5 in summer → UTC ~13:00 Mon → ms | Don't confuse operator-locale vs recipient-locale |
| "Send tomorrow at 9am" | **ALWAYS ASK: 9am whose time?** | Never guess |
**Always echo both timezones before save:**
> "Friday 18:00 IL = Friday 15:00 UTC = epoch ms 1780521060000. Confirm?"
Helpers:
```bash
TZ=Asia/Jerusalem date -d "2026-06-08 09:00" # local → readable
TZ=UTC date -d "@1780521060" # epoch → UTC readable
TZ=America/Chicago date -d "@1780521060" # epoch → recipient-local
# Compute ms epoch from local time:
TS_MS=$(TZ=UTC date -d "2026-06-08 13:30" +%s%3N)
echo "$TS_MS"
```
---
## 3. Pre-flight checklist (before any campaign fires)
Run every box. Skipping any has cost real campaigns.
- [ ] **1. Audience scope confirmed in writing** — "all ~N non-suppressed contacts from list X" not "all contacts"
- [ ] **2. Throttle rate sane for sender** — Gmail OAuth caps at ~30/hr safely. Verified custom domains allow more. Verify the connected account at `GET /accounts`
- [ ] **3. `scheduled_start_at` echoed in BOTH operator-local AND UTC** before save
- [ ] **4. Subject ≤60 chars** — Gmail mobile truncates at ~60. Run `echo -n "subject" | wc -c`
- [ ] **5. Pre-header text set** — the hidden text after the subject in inbox preview. Use a `
` block as the first body element. Without it, inbox shows the first line of code or a leftover word.
- [ ] **6. Body uses `{{firstName | fallback}}` syntax** — see section 9
- [ ] **7. Unsubscribe link present in footer**: `https://api.hidagama.com/api/gaspar/u?e={{email}}&c={{campaign_id}}`. Required by CAN-SPAM + GDPR.
- [ ] **8. CTA link includes UTM**: `?utm_source=gaspar&utm_medium=email&utm_campaign=
` — critical, see section 11.
- [ ] **9. Test-send to yourself** via `POST /test-send`. Inspect rendered output. Note: test-send skips merge-field substitution — verify the production template, not the test render.
- [ ] **10. Audience size ≥ 25** if you want the auto-generated PDF completion report.
- [ ] **11. Frequency check** — has this list received an email in the past 48h? Section 12.
- [ ] **12. Render check at mobile width** — open HTML in a browser at 375px. If it horizontally scrolls or text overflows, the email is broken on mobile (the majority of opens).
If you cannot tick every box, do not press Schedule.
---
## 4. Full campaign lifecycle
```
1. Mint API key → POST /keys (or Settings UI)
2. Connect a sender account → POST /oauth/start (or /outlook/start, /custom-domains)
3. Verify auth → GET /auth/check
4. (Optional) Add recipients to list → POST /audiences, /sheets-import, etc.
5. Design HTML body → Local — see section 6
6. Create draft campaign → POST /campaigns (status: 'draft')
7. Test-send to yourself → POST /test-send
8. Schedule the real send → PATCH /campaigns/{id}/schedule
9. Monitor in-flight → GET /campaigns/{id} (poll stats)
10. Pause if needed → POST /campaigns/{id}/pause
11. Investigate bounces / replies → GET /campaigns/{id}/recipients, /replies
12. Resume after addressing issues → POST /campaigns/{id}/resume (see section 10)
13. Completion → auto PDF report → arrives at reply_to address (≥25 recipients)
14. Pull analytics → GET /campaigns/{id}/stats, /ga/metrics/{id}
```
Every step is an API call. Every step has gotchas. Sections 5-17 cover each in depth.
---
## 5. Campaign body shape
```json
POST /api/gaspar/campaigns
Content-Type: application/json
Authorization: Bearer $GASPAR_API_KEY
{
"name": "Q4 Trade Show — T-1 reminder",
"subject_template": "One last note before the show opens",
"body_html_template": "...",
"body_text_template": "Plain-text fallback for non-HTML clients",
"from_name": "Your Brand",
"reply_to": "you@yourdomain.com",
"gaspar_account_id": "",
"throttle_per_hour": 30,
"jitter_seconds": 0,
"scheduled_start_at": 1780713600000,
"track_opens": 1,
"track_clicks": 1,
"campaign_type": "broadcast",
"recipients": [
{
"email": "recipient@example.com",
"company": "Example Co",
"firstName": "First",
"lastName": "Last",
"country": "US",
"website": "https://example.com"
}
]
}
```
**Field reference:**
| Field | Type | Notes |
|---|---|---|
| `name` | string | Internal label, shown in dashboard. Not seen by recipients. |
| `subject_template` | string | Goes in the email subject. ≤60 chars. Supports `{{merge_fields}}`. |
| `body_html_template` | string | Full HTML email. See section 6 for design rules. |
| `body_text_template` | string | Plain-text fallback. Optional but recommended. Some spam filters penalize HTML-only emails. |
| `from_name` | string | The display name shown in recipient's inbox ("Your Brand "). |
| `reply_to` | string | Where replies go. Often differs from sender. |
| `gaspar_account_id` | string | Pre-connected sender account. Get from `GET /accounts`. |
| `throttle_per_hour` | number | Sends per hour. Default 30. Gmail OAuth: keep ≤30 unless you've verified higher. Custom domains: higher allowed. |
| `jitter_seconds` | number | Random delay (0-N seconds) added to each send to avoid looking robotic. Default 0. |
| `scheduled_start_at` | number (ms) | UTC milliseconds. See section 2. |
| `track_opens` | 0\|1 | Inserts 1px tracking pixel. |
| `track_clicks` | 0\|1 | Should wrap CTA links — see section 11 for the gotcha. |
| `campaign_type` | string | `broadcast` (one-shot to list), `sequence` (multi-step). |
| `recipients` | array | Flat objects. See section 8 for the schema. |
| `attachment_name` | string | Optional. PDF/image attached to every send. |
| `attachment_mime` | string | E.g. `application/pdf`. |
| `attachment_b64` | string | Base64-encoded attachment body. |
| `ab_enabled` | 0\|1 | Enable A/B subject testing. |
| `ab_subject_b` | string | Variant B subject when A/B is on. |
| `ab_body_html_b` | string | Variant B HTML body. |
| `ab_pick_after_mins` | number | After N minutes, auto-pick the winner and send the rest of the list with it. |
**Returns:**
```json
{
"ok": true,
"campaign": {
"id": "uuid",
"status": "draft",
"total_recipients": 280,
...
}
}
```
The campaign starts in `status: 'draft'`. Set `scheduled_start_at` to launch. Status flow: `draft → scheduled → running → completed` (or `paused` mid-flight).
---
## 6. HTML email design
Email rendering is a 1998-era nightmare. Gmail strips `
Your inbox-preview line — make it count.
|
YOUR BRAND
|
| |
|
Eyebrow / context
Your headline — 8 words max.
Hi {{firstName | there}},
Body copy. Two or three short paragraphs. One idea per paragraph.
Verb-first sentences. Active voice.
Trust micro-line under the button (optional).
|
|
— Your Brand
|
|
Your Company
Your address
Unsubscribe
·
yourdomain.com
|
|