---
name: loops
description: Use when building email infrastructure for SaaS applications,
  sending marketing campaigns, setting up automated workflows, sending
  transactional emails, managing contacts and audience segments, or integrating
  email with external platforms via API or webhooks.
metadata:
  mintlify-proj: loops
  version: "1.0"
---

# Loops Skill

## Product summary

Loops is an email platform for sending marketing campaigns, automated workflows, and transactional emails from your SaaS. Use the REST API, CLI, or integrations to manage contacts, trigger workflows with events, and send personalized emails at scale. Key files: API key stored in `LOOPS_API_KEY` environment variable or via `loops auth login`. CLI commands: `loops contacts`, `loops events`, `loops campaigns`, `loops transactional`, `loops workflows`. Primary docs: https://loops.so/docs

## When to use

Reach for this skill when:
- Building email infrastructure for a SaaS (onboarding sequences, product updates, password resets)
- Creating or updating contacts and audience segments via API
- Sending events to trigger automated workflows
- Sending transactional emails (password resets, confirmations, receipts)
- Setting up campaigns to target specific audience segments
- Integrating email with external platforms (Stripe, Clerk, Supabase, Zapier, Make)
- Debugging email deliverability or contact sync issues
- Testing email workflows before going live

## Quick reference

### CLI authentication
```bash
loops auth login my-team          # Store API key locally
loops auth use my-team            # Set as default
loops auth list                   # List stored keys
export LOOPS_API_KEY=your_key     # Or use environment variable
```

### Core CLI commands
| Task | Command |
|------|---------|
| Create contact | `loops contacts create --email user@example.com --first-name John` |
| Update contact | `loops contacts update --email user@example.com --prop planName=Pro` |
| Send event | `loops events send --email user@example.com --event-name planUpgrade` |
| List workflows | `loops workflows list` |
| Create campaign | `loops campaigns create --subject "Product Update"` |
| Send transactional | `loops transactional send --transactional-id ID --email user@example.com` |
| Create contact property | `loops contact-properties create --name planName --type string` |

### API endpoints (base: `https://app.loops.so/api/v1`)
| Resource | Endpoint | Method |
|----------|----------|--------|
| Test API key | `/api-key` | GET |
| Create contact | `/contacts` | POST |
| Update contact | `/contacts/update` | PUT |
| Send event | `/events/send` | POST |
| Send transactional | `/transactional` | POST |
| Create campaign | `/campaigns` | POST |
| List workflows | `/workflows` | GET |
| Create audience segment | `/audience-segments` | POST |

### Email types
| Type | Use case | Tracking | Unsubscribe | Audience |
|------|----------|----------|-------------|----------|
| Campaign | One-off to many (newsletters, updates) | Opens/clicks | Required | Audience only |
| Workflow | Automated sequences (onboarding, triggers) | Opens/clicks | Required | Audience only |
| Transactional | 1:1 action-based (password reset, receipt) | None | Not required | Any email |

### Contact properties
- **Default**: email, firstName, lastName, subscribed, userGroup, source
- **Custom**: Create with `loops contact-properties create --name propertyName --type [string|number|boolean|date]`
- **Usage**: Filter audiences, personalize emails, segment contacts

### Event properties
- Sent with events to trigger workflows
- Available in email templates as dynamic content
- Types: string, number, boolean, date (ISO 8601)
- Create in workflow builder or via API

## Decision guidance

### When to use Campaign vs Workflow vs Transactional

| Scenario | Type | Why |
|----------|------|-----|
| Send product update to all users | Campaign | One-time send to many |
| Welcome sequence for new signups | Workflow | Automated, triggered by contact creation |
| Password reset email | Transactional | 1:1, action-based, no unsubscribe needed |
| Upsell to users who opened email | Workflow | Conditional, triggered by engagement |
| Invoice confirmation | Transactional | System-generated, not promotional |
| Monthly digest | Workflow with event trigger | Scheduled, event-driven |

### When to use API vs CLI vs UI

| Approach | Best for | Example |
|----------|----------|---------|
| API | Production code, integrations, automation | Sync contacts from database, send transactional emails |
| CLI | Local development, testing, agent workflows | Create test campaigns, send events, manage contacts |
| UI | One-off sends, campaign setup, workflow building | Draft and schedule campaigns, design workflows |

### Audience targeting: Filters vs Segments vs Mailing Lists

| Tool | Use case | Scope |
|------|----------|-------|
| Filters | Ad-hoc targeting in campaign/workflow | Single send |
| Audience Segments | Reusable, named groups (API-queryable) | Multiple sends |
| Mailing Lists | User preference management | Opt-in/out tracking |

## Workflow

### 1. Set up domain and authentication
- Verify sending domain in Loops (Settings > Sending domain)
- Generate API key (Settings > API)
- Store key: `loops auth login my-team` or `export LOOPS_API_KEY=key`
- Test: `loops api-key` or `curl -H "Authorization: Bearer KEY" https://app.loops.so/api/v1/api-key`

### 2. Create contact properties
- Define custom properties before syncing contacts: `loops contact-properties create --name planName --type string`
- Use default properties (firstName, lastName, userGroup) when possible
- Ensure property names match your data source (camelCase)

### 3. Add contacts
- Via CSV upload (Settings > Audience > Import)
- Via API: `POST /v1/contacts` with email, firstName, custom properties
- Via CLI: `loops contacts create --email user@example.com --prop planName=Pro`
- Via integration (Stripe, Segment, Zapier)

### 4. Build email content
- **Campaigns/Workflows**: Use editor or LMX markup
- **Transactional**: Create in editor, add data variables (e.g., `{resetUrl}`), publish
- Add personalization: `{firstName}` for contacts, `{DATA_VARIABLE:resetUrl}` for transactional
- Test with `@example.com` or `@test.com` domains (no emails sent)

### 5. Create workflows or campaigns
- **Workflow**: Set trigger (Contact added, Event received, Contact updated), add email nodes, timers, branches
- **Campaign**: Draft email, select audience segment, schedule send
- Use audience filters to target specific contacts

### 6. Send events to trigger workflows
- Create event in workflow builder or Settings > Events
- Send via API: `POST /v1/events/send` with email, eventName, eventProperties
- Or CLI: `loops events send --email user@example.com --event-name planUpgrade --prop newPlan=Pro`

### 7. Send transactional emails
- Create transactional email with data variables
- Publish and copy transactional ID
- Send via API: `POST /v1/transactional` with transactionalId, email, dataVariables
- Or CLI: `loops transactional send --transactional-id ID --email user@example.com --data-var name=John`

### 8. Monitor and iterate
- Check workflow metrics (Workflows > Metrics)
- Review campaign performance (Campaigns > Metrics)
- Audit contact activity (Audience > Contact details)
- Test with test email addresses before production sends

## Common gotchas

- **API key exposed**: Never commit API keys to Git. Use environment variables or `loops auth login` for local storage.
- **Missing domain verification**: Cannot send emails without a verified sending domain. Set up DNS records first.
- **Contact data not ready**: If a workflow trigger fires before contact properties are saved, the contact won't enter. Send all data in the same request.
- **Transactional data variables required**: All non-optional data variables must be included in the send request or the API call fails.
- **Event names with colons**: Custom event names cannot contain `:` characters.
- **Unsubscribed contacts and transactional**: Unsubscribed contacts still receive transactional emails (by design). Use campaigns/workflows for marketing only.
- **Transactional emails don't track opens/clicks**: Disabled for deliverability. Don't rely on engagement metrics for transactional.
- **Contacts added via CSV don't trigger workflows**: Must use API, form, or integration. Re-upload CSV with "Trigger workflows" toggle to trigger existing contacts.
- **Rate limits**: 10 requests/second baseline (60/60s for content API). Implement exponential backoff on 429 responses.
- **Test email domains**: `@example.com` and `@test.com` emails don't send but trigger workflows normally. Use for testing.
- **Revision conflicts on email updates**: Use `--force` flag or pass `--expected-revision-id` to avoid concurrent edit conflicts.
- **Audience only contains marketing contacts**: Transactional emails to new contacts don't add them to Audience unless `addToAudience: true` is set.

## Verification checklist

Before submitting work:

- [ ] API key is stored in environment variable or via `loops auth login`, not hardcoded
- [ ] Sending domain is verified (check Settings > Sending domain)
- [ ] Contact properties are created before syncing data
- [ ] Test email addresses use `@example.com` or `@test.com` domains
- [ ] Workflow triggers are set correctly (Contact added, Event received, etc.)
- [ ] All required data variables are included in transactional email API calls
- [ ] Event names don't contain colons
- [ ] Audience filters are applied correctly (All vs Any logic)
- [ ] Transactional emails have `addToAudience: true` if contacts should be added to Audience
- [ ] Rate limit headers are checked (`x-ratelimit-remaining`)
- [ ] Workflow is paused before editing email content
- [ ] Campaign audience is reviewed before scheduling send
- [ ] LMX markup is valid (use `loops email-messages get` to check compilation)

## Resources

- **Full documentation**: https://loops.so/docs/llms.txt (comprehensive page-by-page navigation for agents)
- **API Reference**: https://loops.so/docs/api-reference/intro
- **CLI Reference**: https://loops.so/docs/cli
- **Workflows Guide**: https://loops.so/docs/workflows
- **Transactional Email**: https://loops.so/docs/transactional
- **Events & Triggers**: https://loops.so/docs/events

---

> For additional documentation and navigation, see: https://loops.so/docs/llms.txt