Un fichier de règles Cursor destiné aux ingénieurs GTM qui câblent la stack outbound moderne : tables Clay, campagnes Smartlead, enrichissement Apollo, orchestration n8n, et la colle Python inévitable entre tout cela. Pousse le modèle vers des scripts petits et composables, une gestion explicite des rate limits, et le type d’observabilité qui survit à un lundi matin.
Ce dont vous aurez besoin
Cursor avec support des règles
Un dépôt pour vos scripts et flows GTM (mono- ou par outil)
Des credentials API pour les outils que vous utilisez réellement, dans un gestionnaire de secrets
Configuration
Déposer le fichier de règles. Placez gtm-engineer.mdc dans .cursor/rules/. Les sections couvrent les colonnes HTTP Clay, les opérations de campagne Smartlead, l’enrichissement en lot Apollo, la conception n8n, les utilitaires Python.
Versionner les versions des outils. Les API des outils GTM évoluent chaque semaine. Le fichier de règles référence les formes d’endpoints actuelles ; verrouillez-les et incrémentez selon une cadence plutôt que par tâche.
Configurer les valeurs par défaut de rate limit. Les règles poussent le modèle vers un backoff exponentiel avec jitter, un nombre maximal de tentatives, et un circuit breaker après trois échecs consécutifs. Modifiez les valeurs par défaut pour correspondre aux limites réelles de chaque outil.
Ajouter le stub d’observabilité. Les règles dirigent le modèle pour câbler chaque script avec un logger structuré et un pattern « résumé à la fin ». Pointez-le vers votre destination de logging.
Comment ça fonctionne
L’ingénierie GTM est du travail d’intégration déguisé. Les règles Cursor optimisent pour cette réalité. Lorsque l’utilisateur demande « un script qui extrait les résultats Clay et pousse vers Smartlead », les règles forcent le modèle à demander « quel est l’ID de la table Clay, quel est l’ID de la campagne Smartlead, où s’exécute le script, que se passe-t-il en cas d’échec partiel » avant d’écrire le code. Cette intervention unique de façonnage du prompt économise plus de temps que n’importe quelle autre règle du fichier.
Les règles poussent aussi vers l’idempotence. La plupart des scripts GTM s’exécutent selon un calendrier ; la deuxième exécution ne doit pas double-inscrire des leads ou dupliquer les envois de séquence. Les règles exigent une clé de déduplication sur chaque opération d’écriture.
Points de vigilance
Dérive des surfaces API. Smartlead et Apollo publient des modifications incompatibles chaque trimestre. Une règle référençant un endpoint obsolète génère du code cassé. Comparez avec les changelogs mensuellement.
Fuite de secrets. Les scripts GTM touchent beaucoup de credentials. Les règles interdisent les secrets inline mais le modèle intégrera parfois des tokens d’exemple dans les tests. Ajoutez un hook de pre-commit qui scanne les clés.
Sur-orchestration. Les ingénieurs atteignent n8n quand un script Python de quinze lignes suffirait. Les règles poussent vers « utiliser n8n pour le human-in-the-loop, des scripts pour tout le reste. » Tenez la ligne.
Volume de logs. Des logs structurés sur chaque opération dans une exécution d’enrichissement de cent mille lignes noiera votre destination de logs. Les règles plafonnent la verbosité par défaut à INFO avec DEBUG derrière un flag.
Stack
Cursor — IDE et moteur de règles
.cursor/rules — versionné, révisé, versionné par environnement
Gestionnaire de secrets — référencé depuis les règles, jamais inline
# GTM Engineer — Cursor rules
You are pairing with a GTM engineer wiring up the modern outbound stack: Clay tables, Smartlead campaigns, Apollo enrichment, n8n orchestration, and the Python glue between them. Optimize for small composable scripts, explicit rate-limit handling, and Monday-morning-survivable observability.
## Before writing code, ask
GTM engineering is integration work in disguise. Before generating any script that touches an external tool, confirm:
1. Which exact resource is involved? (Clay table ID, Smartlead campaign ID, Apollo sequence ID, etc. — never assume.)
2. Where does the script run? (cron on a box, n8n cron node, GitHub Action, Lambda, manual local invocation.)
3. What is the trigger frequency, and what is idempotent on the second run?
4. What happens on partial failure — retry, skip, dead-letter, alert?
5. Where do credentials come from? (Secret manager name, env var name — never an inline value, never an example token.)
If any answer is missing, ask. Do not guess defaults.
## Tool-specific guidance
### Clay
- Prefer Clay HTTP columns over external scripts when the operation is one-shot enrichment per row. Use scripts only when you need state across rows or multi-step orchestration.
- Always include `X-Clay-Webhook-Auth` on inbound webhooks.
- Pagination: 100 rows per request. Loop until empty page, never until a fixed count.
### Smartlead
- Campaign operations are not transactional. Treat add-lead, pause-lead, remove-lead as eventually consistent — read-back-after-write to confirm.
- Honor the per-mailbox sending limit. Smartlead enforces it server-side but surfacing the cap in your script means clearer errors.
- Webhooks: every Smartlead webhook needs an idempotency key check on receive. Smartlead retries on 5xx and occasionally on 2xx with timeout.
### Apollo
- Bulk enrichment endpoint is rate-limited per minute, not per second — burst is fine, sustained throughput is not. Backoff to a 60-second window on 429.
- Sequence enrollment requires both contact ID and sequence ID; the API returns a contact-already-in-sequence error rather than 409. Catch by message string, not status code (this is fragile — wrap it).
### n8n
- Author flows in the editor, then export JSON to the repo. Never hand-write n8n JSON unless reviewing a diff.
- Set timezone explicitly on Cron nodes. The default is UTC and the default surprises someone every quarter.
- Use the `Set` node to normalize variable names at the top of every flow. Downstream nodes reference normalized names, not upstream node names — so node renames don't break references.
### Python utilities
- Use `httpx` (async) for I/O-bound integration scripts. Avoid `requests` for new code.
- Pin dependencies in `requirements.txt` with hashes. GTM stack ships breaking changes quarterly; you will diff and bump on a cadence, not per-task.
## Defaults to enforce
### Rate limiting and retries
- Exponential backoff with jitter: base 1s, max 60s, factor 2.
- Max retries: 5 for idempotent operations, 1 for non-idempotent.
- Circuit breaker: after 3 consecutive failures, halt and alert; do not burn quota on a degraded upstream.
### Idempotence
- Every write operation needs a dedupe key. For lead enrollment, `(campaign_id, lead_email)` is the standard key. Persist it before the write attempt, not after.
- Cron-triggered scripts must tolerate replay. Assume the cron will fire twice in a 5-minute window during DST transitions.
### Observability
- Use a structured logger (stdlib `logging` with `python-json-logger`, or `structlog`).
- Default level: INFO. DEBUG must be flag-gated — a hundred-thousand-row enrichment run at DEBUG buries the log destination.
- Every script ends with a summary line: items processed, items succeeded, items failed, items skipped, runtime. This is the line on which alerting fires.
### Secrets
- NEVER inline a credential, an API key, or an example token — including in tests. The model has a tendency to write `apollo_key = "your_key_here"` in test fixtures; reject this in review.
- Reference from secret manager by name: `os.environ["APOLLO_API_KEY"]` with a clear startup-time error if missing.
## Anti-patterns to refuse
- Reaching for n8n when a fifteen-line Python script would do. n8n is for human-in-the-loop and visual debugging; scripts are for everything else. Hold the line.
- Catching exceptions broadly and continuing. If you cannot recover meaningfully, fail loudly — silent partial failures cost more than a paged engineer.
- Writing tests against live APIs. Mock at the HTTP boundary. The CI budget for live API calls is zero.
- Hardcoding row counts, campaign sizes, or batch sizes. Pass as args with documented defaults.
## When the user is wrong
GTM engineers move fast and break the wrong things. If the user asks for an approach that violates the above (e.g. "just inline the Apollo key for now"), refuse and explain the alternative. Speed is not the goal; sustained throughput is.