428 lines
11 KiB
Plaintext
428 lines
11 KiB
Plaintext
---
|
|
title: Troubleshooting
|
|
description: Common issues and solutions
|
|
---
|
|
|
|
## Authentication issues
|
|
|
|
### Invalid API key error
|
|
|
|
**Error:**
|
|
```json
|
|
{
|
|
"code": 401,
|
|
"error": "Unauthorized",
|
|
"message": "Invalid API key"
|
|
}
|
|
```
|
|
|
|
**Solutions:**
|
|
|
|
1. **Verify key format** — Secret keys start with `sk_`, public keys start with `pk_`
|
|
2. **Check Authorization header** — Must use `Bearer` format: `Authorization: Bearer sk_your_secret_key`
|
|
3. **Ensure key hasn't been regenerated** — If you regenerated keys in dashboard, update your application
|
|
4. **Verify project access** — Key must belong to the project you're accessing
|
|
|
|
**Test your key:**
|
|
```bash
|
|
curl -X GET {{API_URL}}/contacts?limit=1 \
|
|
-H "Authorization: Bearer sk_your_secret_key"
|
|
```
|
|
|
|
### Wrong key type for endpoint
|
|
|
|
**Error:**
|
|
```json
|
|
{
|
|
"code": 401,
|
|
"error": "Unauthorized",
|
|
"message": "This endpoint requires a secret key (sk_*)"
|
|
}
|
|
```
|
|
|
|
**Solution:**
|
|
|
|
You're using a public key (`pk_*`) for an endpoint that requires a secret key.
|
|
|
|
- **Public keys** — Only work with `/v1/track` endpoint
|
|
- **Secret keys** — Required for all other endpoints
|
|
|
|
Use your secret key from **Settings > API Keys** in the dashboard.
|
|
|
|
## Email sending issues
|
|
|
|
### Emails not sending
|
|
|
|
**Check these common causes:**
|
|
|
|
1. **Billing limit reached** - Check your billing limits in Settings → Billing. If you hit your monthly limit, increase it or wait for monthly reset.
|
|
|
|
2. **Template not found**
|
|
```json
|
|
{
|
|
"code": 404,
|
|
"error": "Not Found",
|
|
"message": "Template not found"
|
|
}
|
|
```
|
|
|
|
Verify template ID exists and belongs to your project.
|
|
|
|
3. **Contact unsubscribed**
|
|
|
|
Marketing templates skip unsubscribed contacts. Check contact subscription status:
|
|
```bash
|
|
curl -X GET {{API_URL}}/contacts/contact_id \
|
|
-H "Authorization: Bearer sk_your_secret_key"
|
|
```
|
|
|
|
If `subscribed: false`, use a transactional template for critical emails.
|
|
|
|
4. **Project disabled**
|
|
|
|
If your project is disabled, all email sends will fail. Contact support.
|
|
|
|
### Emails going to spam
|
|
|
|
**Common causes:**
|
|
|
|
1. **No custom domain** — Emails from default domain may have lower trust
|
|
2. **Low engagement** — Recipients not opening/clicking emails
|
|
3. **High bounce rate** — Too many invalid email addresses
|
|
4. **Spam complaints** — Recipients marking as spam
|
|
|
|
**Solutions:**
|
|
|
|
1. **Set up custom domain** — [Add your domain](/guides/custom-domains) for better deliverability
|
|
2. **Clean your list** — Remove bounced and inactive contacts
|
|
3. **Improve content** — Avoid spam trigger words, include clear unsubscribe link
|
|
4. **Warm up your domain** — Start with small volumes, gradually increase
|
|
5. **Segment your audience** — Only send relevant content to engaged users
|
|
|
|
### Tracking not working
|
|
|
|
**Open tracking:**
|
|
|
|
Requires:
|
|
- `trackingEnabled: true` on project
|
|
- Recipient email client loads images
|
|
- HTML email (not plain text)
|
|
|
|
Some email clients block tracking pixels. Open rates are estimates, not exact.
|
|
|
|
**Click tracking:**
|
|
|
|
Requires:
|
|
- Tracking enabled on project (check your project settings in dashboard)
|
|
- Links in email body (not subject)
|
|
- HTML email format
|
|
|
|
## Campaign issues
|
|
|
|
### Campaign won't send
|
|
|
|
**Error:**
|
|
```json
|
|
{
|
|
"code": 400,
|
|
"error": "Bad Request",
|
|
"message": "Campaign must be in DRAFT or SCHEDULED status"
|
|
}
|
|
```
|
|
|
|
**Solution:**
|
|
|
|
Can only send campaigns with status `DRAFT`. If campaign is `SENT` or `CANCELLED`, duplicate it:
|
|
|
|
```bash
|
|
curl -X POST {{API_URL}}/campaigns/campaign_id/duplicate \
|
|
-H "Authorization: Bearer sk_your_secret_key"
|
|
```
|
|
|
|
### No recipients for campaign
|
|
|
|
**Cause:**
|
|
|
|
Campaign targets a segment or filter with zero matching contacts.
|
|
|
|
**Solutions:**
|
|
|
|
1. **Check segment size**
|
|
```bash
|
|
curl -X GET {{API_URL}}/segments/segment_id \
|
|
-H "Authorization: Bearer sk_your_secret_key"
|
|
```
|
|
|
|
Look at `memberCount` field.
|
|
|
|
2. **Verify filters** — Test filters on segments page to see matches
|
|
|
|
3. **Check subscription status** — Marketing templates only send to subscribed contacts
|
|
|
|
### Scheduled campaign didn't send
|
|
|
|
**Check:**
|
|
|
|
1. **Verify schedule time** — Must be in future when scheduled
|
|
2. **Campaign status** — Should be `SCHEDULED`, not `CANCELLED`
|
|
3. **Project limits** — Billing limits may block sends
|
|
|
|
View campaign details:
|
|
```bash
|
|
curl -X GET {{API_URL}}/campaigns/campaign_id \
|
|
-H "Authorization: Bearer sk_your_secret_key"
|
|
```
|
|
|
|
## Workflow issues
|
|
|
|
### Workflow not triggering
|
|
|
|
**Common causes:**
|
|
|
|
1. **Workflow not enabled**
|
|
|
|
Check `enabled: true`:
|
|
```bash
|
|
curl -X GET {{API_URL}}/workflows/workflow_id \
|
|
-H "Authorization: Bearer sk_your_secret_key"
|
|
```
|
|
|
|
2. **Wrong event name**
|
|
|
|
Event names are case-sensitive. `user_signed_up` ≠ `User_Signed_Up`
|
|
|
|
Verify event name exactly matches workflow trigger.
|
|
|
|
3. **Contact already entered (allowReentry: false)**
|
|
|
|
If `allowReentry: false`, contact can only enter once. Check execution history:
|
|
```bash
|
|
curl -X GET {{API_URL}}/workflows/workflow_id/executions?contactId=contact_id \
|
|
-H "Authorization: Bearer sk_your_secret_key"
|
|
```
|
|
|
|
4. **Segment membership not tracked**
|
|
|
|
For `SEGMENT_ENTRY` triggers, segment must have `trackMembership: true`.
|
|
|
|
### Workflow emails not sending
|
|
|
|
**Check each email step execution:**
|
|
|
|
```bash
|
|
curl -X GET {{API_URL}}/workflows/workflow_id/executions/execution_id \
|
|
-H "Authorization: Bearer sk_your_secret_key"
|
|
```
|
|
|
|
Look for step failures in response.
|
|
|
|
**Common causes:**
|
|
|
|
1. **Template not found** — Template was deleted
|
|
2. **Contact unsubscribed** — Marketing templates skip unsubscribed contacts
|
|
3. **Billing limit reached** — Monthly workflow email limit exceeded
|
|
4. **Workflow execution stopped** — Contact was deleted or execution was cancelled
|
|
|
|
### Workflow stuck on delay step
|
|
|
|
Delays are processed by background jobs. Check:
|
|
|
|
1. **scheduledFor** time — When should it execute?
|
|
2. **Current time** — Has the scheduled time passed?
|
|
|
|
Delays process every minute. Wait a few minutes and check again.
|
|
|
|
## Contact issues
|
|
|
|
### Contact not found
|
|
|
|
**Error:**
|
|
```json
|
|
{
|
|
"code": 404,
|
|
"error": "Not Found",
|
|
"message": "Contact not found"
|
|
}
|
|
```
|
|
|
|
**Solutions:**
|
|
|
|
1. **Verify contact ID** — Check for typos in the ID
|
|
2. **Check project** — Contact may belong to different project
|
|
3. **Contact deleted** — Contact may have been deleted
|
|
|
|
Search by email instead:
|
|
```bash
|
|
curl -X GET "{{API_URL}}/contacts?search=user@example.com" \
|
|
-H "Authorization: Bearer sk_your_secret_key"
|
|
```
|
|
|
|
### Duplicate contacts
|
|
|
|
Plunk automatically prevents duplicates. Creating a contact with existing email updates that contact instead of creating a new one.
|
|
|
|
If you see duplicates:
|
|
1. Check email addresses carefully (they may differ slightly)
|
|
2. Verify you're viewing the same project
|
|
|
|
### Contact data not updating
|
|
|
|
**Verify data format:**
|
|
|
|
```bash
|
|
curl -X PATCH {{API_URL}}/contacts/contact_id \
|
|
-H "Authorization: Bearer sk_your_secret_key" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"data": {
|
|
"plan": "premium"
|
|
}
|
|
}'
|
|
```
|
|
|
|
**Common issues:**
|
|
|
|
1. **Missing `data` wrapper** — Custom fields must be inside `data` object
|
|
2. **Wrong data types** — Numbers should be `99`, not `"99"`
|
|
3. **Nested too deeply** — Keep data relatively flat
|
|
|
|
## Segment issues
|
|
|
|
### Segment shows wrong count
|
|
|
|
Segment counts update every 5 minutes. Wait a few minutes and refresh.
|
|
|
|
For real-time count, query members directly:
|
|
```bash
|
|
curl -X GET "{{API_URL}}/segments/segment_id/contacts?limit=1" \
|
|
-H "Authorization: Bearer sk_your_secret_key"
|
|
```
|
|
|
|
Check `total` field in response.
|
|
|
|
### Segment filter not working
|
|
|
|
**Test your filter:**
|
|
|
|
1. **Check field names** — Use `data.fieldName` for custom fields
|
|
2. **Verify operators** — `equals` for strings, `greaterThan` for numbers
|
|
3. **Match data types** — Don't compare string `"99"` with number `99`
|
|
|
|
**Example filters:**
|
|
|
|
```javascript
|
|
// ❌ Wrong
|
|
{ "field": "plan", "operator": "equals", "value": "pro" }
|
|
|
|
// ✓ Correct
|
|
{ "field": "data.plan", "operator": "equals", "value": "pro" }
|
|
```
|
|
|
|
### Segment entry/exit events not firing
|
|
|
|
Requires `trackMembership: true` on segment:
|
|
|
|
```bash
|
|
curl -X PATCH {{API_URL}}/segments/segment_id \
|
|
-H "Authorization: Bearer sk_your_secret_key" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"trackMembership": true}'
|
|
```
|
|
|
|
Membership is computed every 5 minutes. Events fire on the next computation cycle after a contact's segment membership changes.
|
|
|
|
## Domain verification issues
|
|
|
|
### Domain won't verify
|
|
|
|
**Common causes:**
|
|
|
|
1. **DNS not propagated** — Can take up to 48 hours
|
|
2. **Wrong DNS records** — Double-check DKIM tokens
|
|
3. **Conflicting records** — Remove old DKIM records from other email services
|
|
|
|
**Check DNS propagation:**
|
|
```bash
|
|
dig TXT _domainkey.yourdomain.com
|
|
```
|
|
|
|
Records should match the DKIM tokens provided by Plunk.
|
|
|
|
**Force verification check:**
|
|
```bash
|
|
curl -X POST {{API_URL}}/domains/domain_id/verify \
|
|
-H "Authorization: Bearer sk_your_secret_key"
|
|
```
|
|
|
|
### Emails not sending from custom domain
|
|
|
|
1. **Verify domain is verified** — Check `verified: true` in domain settings
|
|
2. **Use correct `from` address** — Must be `@yourdomain.com`
|
|
3. **Check SPF and DKIM** — Ensure DNS records are correct
|
|
|
|
## Rate limiting
|
|
|
|
### 429 Too Many Requests
|
|
|
|
**Error:**
|
|
```json
|
|
{
|
|
"code": 429,
|
|
"error": "Too Many Requests",
|
|
"message": "Rate limit exceeded"
|
|
}
|
|
```
|
|
|
|
**Solutions:**
|
|
|
|
1. **Implement exponential backoff** — Wait and retry with increasing delays
|
|
2. **Batch operations** — Group multiple operations when possible
|
|
3. **Spread requests** — Distribute load over time instead of bursts
|
|
|
|
**Example retry logic:**
|
|
```javascript
|
|
async function sendWithRetry(data, maxRetries = 3) {
|
|
for (let i = 0; i < maxRetries; i++) {
|
|
try {
|
|
const response = await fetch('{{API_URL}}/v1/send', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${apiKey}`,
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(data)
|
|
});
|
|
|
|
if (response.status === 429) {
|
|
const waitTime = Math.pow(2, i) * 1000; // Exponential backoff
|
|
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
continue;
|
|
}
|
|
|
|
return response;
|
|
} catch (error) {
|
|
if (i === maxRetries - 1) throw error;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Getting help
|
|
|
|
If you're still experiencing issues:
|
|
|
|
1. **Check API status** — Is there a known outage?
|
|
2. **Review error message** — Error messages usually indicate the problem
|
|
3. **Search documentation** — Look for specific error codes or messages
|
|
4. **Check GitHub issues** — Similar issues may already be reported
|
|
5. **Join Discord community** — Ask for help from other users
|
|
6. **Contact support** — Provide error details, request IDs, and steps to reproduce
|
|
|
|
**Include in support requests:**
|
|
- Error message and status code
|
|
- API endpoint and method
|
|
- Request body (remove sensitive data)
|
|
- Timestamp of the issue
|
|
- Project ID (if applicable)
|
|
|