Initial push of Plunk Next

This commit is contained in:
Dries Augustyns
2025-12-01 09:56:56 +01:00
parent 07cea20262
commit ff1876d580
566 changed files with 89036 additions and 28423 deletions
@@ -0,0 +1,512 @@
---
title: Error Codes
description: API error codes and troubleshooting
---
## Overview
The Plunk API uses standardized error responses to help you quickly identify and resolve issues. All errors include:
- **Machine-readable error codes** for programmatic handling
- **Human-readable messages** explaining what went wrong
- **Helpful suggestions** to guide you toward a solution
- **Request IDs** for debugging and support requests
- **Field-level validation details** when applicable
## HTTP Status Codes
### 200 OK
Request successful.
### 201 Created
Resource created successfully.
### 400 Bad Request
Invalid request format or parameters. Check the error details for specific issues.
### 401 Unauthorized
Authentication failed or missing. Verify your API key.
### 403 Forbidden
Not authorized to access this resource. Check permissions or project status.
### 404 Not Found
The requested resource does not exist. Verify the resource ID.
### 422 Unprocessable Entity
Request validation failed. Check the `errors` array for field-level details.
### 429 Too Many Requests
Rate limit exceeded. Wait before retrying or upgrade your plan.
### 500 Internal Server Error
An unexpected server error occurred. Contact support with the request ID.
## Error Response Format
All errors follow this standardized format:
```json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"statusCode": 422,
"requestId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"errors": [
{
"field": "email",
"message": "Invalid email",
"code": "invalid_string"
}
],
"suggestion": "One or more fields have incorrect types. Check that strings are quoted, numbers are unquoted, and booleans are true/false."
},
"timestamp": "2025-11-30T10:30:00.000Z"
}
```
### Response Fields
| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | Always `false` for errors |
| `error.code` | string | Machine-readable error code (see below) |
| `error.message` | string | Human-readable error description |
| `error.statusCode` | number | HTTP status code |
| `error.requestId` | string | Unique request identifier for debugging |
| `error.errors` | array | Field-level validation errors (validation errors only) |
| `error.details` | object | Additional context about the error (optional) |
| `error.suggestion` | string | Helpful guidance for fixing the error (optional) |
| `timestamp` | string | ISO 8601 timestamp when the error occurred |
## Error Codes Reference
All errors include a machine-readable `code` field for programmatic handling. Here are all possible error codes:
### Authentication & Authorization
| Code | Status | Description |
|------|--------|-------------|
| `UNAUTHORIZED` | 401 | General authentication failure |
| `INVALID_CREDENTIALS` | 401 | Login credentials are incorrect |
| `MISSING_AUTH` | 401 | Authorization header is missing or malformed |
| `INVALID_API_KEY` | 401 | API key is invalid or not found |
| `FORBIDDEN` | 403 | Not allowed to perform this action |
| `PROJECT_ACCESS_DENIED` | 403 | No access to this project |
| `PROJECT_DISABLED` | 403 | Project has been disabled |
### Validation & Input Errors
| Code | Status | Description |
|------|--------|-------------|
| `BAD_REQUEST` | 400 | General request error |
| `VALIDATION_ERROR` | 422 | Request validation failed (includes field errors) |
| `INVALID_EMAIL` | 422 | Email format is invalid |
| `INVALID_REQUEST_BODY` | 400 | Request body is malformed |
| `MISSING_REQUIRED_FIELD` | 422 | Required field is missing |
### Resource Errors
| Code | Status | Description |
|------|--------|-------------|
| `RESOURCE_NOT_FOUND` | 404 | Generic resource not found |
| `CONTACT_NOT_FOUND` | 404 | Contact does not exist |
| `TEMPLATE_NOT_FOUND` | 404 | Template does not exist |
| `CAMPAIGN_NOT_FOUND` | 404 | Campaign does not exist |
| `WORKFLOW_NOT_FOUND` | 404 | Workflow does not exist |
| `CONFLICT` | 409 | Resource conflict (e.g., duplicate) |
### Rate Limiting & Billing
| Code | Status | Description |
|------|--------|-------------|
| `RATE_LIMIT_EXCEEDED` | 429 | Too many requests |
| `BILLING_LIMIT_EXCEEDED` | 402 | Billing limit reached |
| `UPGRADE_REQUIRED` | 402 | Feature requires plan upgrade |
### Server Errors
| Code | Status | Description |
|------|--------|-------------|
| `INTERNAL_SERVER_ERROR` | 500 | Unexpected server error |
| `DATABASE_ERROR` | 500 | Database operation failed |
| `EXTERNAL_SERVICE_ERROR` | 500 | External service unavailable |
## Common Error Examples
### Authentication Errors
#### Invalid API Key
```json
{
"success": false,
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid secret API key. This endpoint requires a secret key (sk_*), not a public key.",
"statusCode": 401,
"requestId": "abc-123",
"suggestion": "Verify your API key is correct and starts with \"sk_\" for secret keys or \"pk_\" for public keys."
},
"timestamp": "2025-11-30T10:30:00.000Z"
}
```
**Solution**: Check your API key is correct. Secret endpoints require keys starting with `sk_`, while tracking endpoints use `pk_` keys.
#### Missing Authorization Header
```json
{
"success": false,
"error": {
"code": "MISSING_AUTH",
"message": "Authorization header is required",
"statusCode": 401,
"requestId": "abc-123",
"suggestion": "Include an Authorization header with format: \"Authorization: Bearer YOUR_API_KEY\""
},
"timestamp": "2025-11-30T10:30:00.000Z"
}
```
**Solution**: Add the `Authorization` header with your API key in Bearer token format.
### Validation Errors
#### Invalid Email Format
```json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"statusCode": 422,
"requestId": "abc-123",
"errors": [
{
"field": "email",
"message": "Invalid email",
"code": "invalid_string"
}
],
"suggestion": "One or more fields have incorrect types. Check that strings are quoted, numbers are unquoted, and booleans are true/false."
},
"timestamp": "2025-11-30T10:30:00.000Z"
}
```
**Solution**: Provide a valid email address. The `errors` array shows which fields failed validation.
#### Missing Required Fields
```json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"statusCode": 422,
"requestId": "abc-123",
"errors": [
{
"field": "event",
"message": "Required",
"code": "invalid_type"
},
{
"field": "email",
"message": "Required",
"code": "invalid_type"
}
],
"suggestion": "Required fields are missing. Ensure all required fields are included in your request."
},
"timestamp": "2025-11-30T10:30:00.000Z"
}
```
**Solution**: Include all required fields in your request body.
### Resource Errors
#### Template Not Found
```json
{
"success": false,
"error": {
"code": "TEMPLATE_NOT_FOUND",
"message": "Template with ID \"tpl_abc123\" was not found",
"statusCode": 404,
"requestId": "abc-123",
"details": {
"resource": "Template",
"id": "tpl_abc123"
},
"suggestion": "Ensure the template ID is correct and belongs to your project. You can list available templates via the API."
},
"timestamp": "2025-11-30T10:30:00.000Z"
}
```
**Solution**: Verify the template ID exists and belongs to your project.
### Rate Limiting
#### Rate Limit Exceeded
```json
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Please try again later.",
"statusCode": 429,
"requestId": "abc-123",
"suggestion": "You have exceeded the rate limit. Wait a moment before retrying, or upgrade your plan."
},
"timestamp": "2025-11-30T10:30:00.000Z"
}
```
**Solution**: Implement exponential backoff and retry logic. Consider upgrading your plan for higher limits.
## Success Response Format
Successful API requests return a standardized format with `success: true` and a `data` object:
```json
{
"success": true,
"data": {
"contact": "cnt_abc123",
"event": "evt_xyz789",
"timestamp": "2025-11-30T10:30:00.000Z"
}
}
```
### Response Fields
| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | Always `true` for successful requests |
| `data` | object | Response data specific to the endpoint |
## Handling Errors in Your Code
### JavaScript/TypeScript Example
```typescript
try {
const response = await fetch('https://api.useplunk.com/v1/track', {
method: 'POST',
headers: {
'Authorization': `Bearer ${publicKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
event: 'purchase',
email: '[email protected]'
})
});
const data = await response.json();
if (!data.success) {
// Handle error
console.error(`Error [${data.error.code}]:`, data.error.message);
// Show suggestion to user
if (data.error.suggestion) {
console.log('Suggestion:', data.error.suggestion);
}
// Log request ID for support
console.log('Request ID:', data.error.requestId);
// Handle specific error types
switch (data.error.code) {
case 'VALIDATION_ERROR':
// Show field-level errors
data.error.errors?.forEach(err => {
console.log(`${err.field}: ${err.message}`);
});
break;
case 'INVALID_API_KEY':
// Prompt user to check their API key
break;
case 'RATE_LIMIT_EXCEEDED':
// Implement retry with backoff
break;
}
return;
}
// Handle success
console.log('Event tracked:', data.data);
} catch (error) {
console.error('Network error:', error);
}
```
### Python Example
```python
import requests
response = requests.post(
'https://api.useplunk.com/v1/track',
headers={
'Authorization': f'Bearer {public_key}',
'Content-Type': 'application/json'
},
json={
'event': 'purchase',
'email': '[email protected]'
}
)
data = response.json()
if not data.get('success'):
error = data['error']
print(f"Error [{error['code']}]: {error['message']}")
# Show suggestion
if 'suggestion' in error:
print(f"Suggestion: {error['suggestion']}")
# Log request ID
print(f"Request ID: {error['requestId']}")
# Handle validation errors
if error['code'] == 'VALIDATION_ERROR':
for err in error.get('errors', []):
print(f"{err['field']}: {err['message']}")
else:
print(f"Event tracked: {data['data']}")
```
## Troubleshooting Guide
### Using Request IDs
Every error includes a unique `requestId` that traces the request through our entire system. Request IDs enable:
**For Developers:**
- Every log entry includes the request ID
- You can grep logs to find all entries for a specific request
- Trace a request from API → Database → Queue → Worker
**For Support:**
1. Include the request ID in your message
2. Describe what you were trying to do
3. Share the full error response if possible
**Example:** If you receive request ID `f47ac10b-58cc-4372-a567-0e02b2c3d479`, we can search our logs for that ID and see:
- The exact request body you sent
- Which database queries were executed
- Any background jobs that were triggered
- The full error stack trace (if applicable)
This helps us quickly locate and diagnose the issue without asking you for additional information.
#### How to Find Request IDs
Request IDs are included in:
- **Error responses**: `error.requestId` field
- **Response headers**: `X-Request-ID` header (also included in successful responses)
- **Your application logs**: Include the header in your logs for correlation
```javascript
// Example: Logging request ID in your application
const response = await fetch('https://api.useplunk.com/v1/send', {
// ... your request
});
const requestId = response.headers.get('X-Request-ID');
console.log('Request ID:', requestId); // Log for correlation
const data = await response.json();
if (!data.success) {
console.error('Error:', data.error.message);
console.error('Request ID:', data.error.requestId); // Same as header
}
```
### Common Issues and Solutions
#### Authentication Issues (401)
**Problem**: `INVALID_API_KEY` or `MISSING_AUTH`
**Solutions**:
- Verify your API key is copied correctly (no extra spaces)
- Check you're using the right key type (`sk_` for secret, `pk_` for public)
- Ensure the `Authorization` header uses Bearer token format
- Verify the key hasn't been revoked or regenerated
#### Validation Issues (422)
**Problem**: `VALIDATION_ERROR` with field errors
**Solutions**:
- Check the `errors` array for specific field issues
- Verify all required fields are included
- Ensure field types match (strings quoted, numbers unquoted)
- Review the API reference for correct request format
#### Not Found Issues (404)
**Problem**: `TEMPLATE_NOT_FOUND`, `CONTACT_NOT_FOUND`, etc.
**Solutions**:
- Verify the resource ID is correct
- Check the resource belongs to your project
- Ensure the resource hasn't been deleted
- List available resources via the API to confirm IDs
#### Rate Limit Issues (429)
**Problem**: `RATE_LIMIT_EXCEEDED`
**Solutions**:
- Implement exponential backoff (wait 1s, 2s, 4s, 8s between retries)
- Reduce request frequency
- Consider upgrading your plan for higher limits
- Batch operations when possible
#### Server Errors (500)
**Problem**: `INTERNAL_SERVER_ERROR`
**Solutions**:
- Note the request ID from the error response
- Wait a moment and retry the request
- Check [status.useplunk.com](https://status.useplunk.com) for incidents
- Contact support with the request ID if the issue persists
### Best Practices
1. **Always check the `success` field** before processing responses
2. **Log request IDs** for debugging and support requests
3. **Handle errors gracefully** with user-friendly messages
4. **Implement retry logic** with exponential backoff for transient errors
5. **Monitor error rates** to detect issues early
6. **Use error codes** for programmatic error handling, not just status codes
## Getting Help
If you continue experiencing issues:
1. Review the error `suggestion` field for guidance
2. Check the [API Reference](/api-reference/overview) for correct usage
3. Search our documentation for your specific error code
4. Contact support with your request ID
5. Join our community for help from other developers
@@ -0,0 +1,16 @@
{
"title": "API Reference",
"pages": [
"overview",
"---Public API---",
"public-api/sendEmail",
"public-api/trackEvent",
"---Resources---",
"contacts",
"templates",
"campaigns",
"segments",
"---Reference---",
"errors"
]
}
@@ -0,0 +1,340 @@
---
title: API Reference
description: Complete Plunk API documentation
---
## Base URL
```
{{API_URL}}
```
All API requests use this base URL.
## Authentication
Include your API key in the `Authorization` header:
```bash
Authorization: Bearer YOUR_API_KEY
```
- **Secret Key (sk_*)** — Required for all endpoints except `/v1/track`
- **Public Key (pk_*)** — Only works with `/v1/track` for client-side event tracking
## Making requests
### Send transactional email
```bash
curl -X POST {{API_URL}}/v1/send \
-H "Authorization: Bearer sk_your_secret_key" \
-H "Content-Type: application/json" \
-d '{
"to": "[email protected]",
"subject": "Hello",
"body": "<p>Your message here</p>"
}'
```
### Track event
```bash
curl -X POST {{API_URL}}/v1/track \
-H "Authorization: Bearer pk_your_public_key" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"event": "signed_up"
}'
```
### Create contact
```bash
curl -X POST {{API_URL}}/contacts \
-H "Authorization: Bearer sk_your_secret_key" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"subscribed": true,
"data": {
"firstName": "John",
"plan": "pro"
}
}'
```
## Response format
All API responses follow a standardized format for easy parsing and error handling.
### Success response
Public API endpoints (`/v1/send`, `/v1/track`):
```json
{
"success": true,
"data": {
"contact": "cnt_abc123",
"event": "evt_xyz789",
"timestamp": "2025-11-30T10:30:00.000Z"
}
}
```
Dashboard API endpoints (contacts, templates, campaigns):
```json
{
"success": true,
"data": {
"id": "cnt_abc123",
"email": "[email protected]",
"createdAt": "2025-11-30T10:30:00.000Z"
}
}
```
List endpoints with pagination:
```json
{
"success": true,
"data": {
"items": [...],
"nextCursor": "abc123",
"hasMore": true,
"total": 1000
}
}
```
### Error response
All errors include detailed information to help you debug issues:
```json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"statusCode": 422,
"requestId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"errors": [
{
"field": "email",
"message": "Invalid email",
"code": "invalid_string"
}
],
"suggestion": "One or more fields have incorrect types. Check that strings are quoted, numbers are unquoted, and booleans are true/false."
},
"timestamp": "2025-11-30T10:30:00.000Z"
}
```
**Error fields:**
- `code` — Machine-readable error code for programmatic handling
- `message` — Human-readable description
- `statusCode` — HTTP status code
- `requestId` — Unique ID for debugging (include when contacting support)
- `errors` — Field-level validation details (when applicable)
- `suggestion` — Helpful guidance for fixing the error
See the [Error Codes documentation](/api-reference/errors) for complete details and examples.
## Pagination
List endpoints support cursor-based pagination:
```bash
GET /contacts?limit=100&cursor=abc123
```
**Parameters:**
- `limit` — Number of items per page (default: 20, max: 100)
- `cursor` — Pagination cursor from previous response
**Response:**
```json
{
"items": [...],
"nextCursor": "def456",
"hasMore": true,
"total": 10000
}
```
Use `nextCursor` for the next page. When `hasMore` is false, you've reached the end.
## Rate limits
Plunk enforces reasonable rate limits to ensure service quality:
- **Email sending** — 14 emails/second (AWS SES default)
- **API requests** — 1000 requests/minute per project
- **Bulk operations** — Automatically queued for processing
If you exceed limits, you'll receive a `429 Too Many Requests` response.
## Error codes
The API uses standard HTTP status codes along with machine-readable error codes:
**400 Bad Request** — Invalid request parameters or malformed request body
**401 Unauthorized** — Missing or invalid API key
**403 Forbidden** — Not authorized to access this resource or project disabled
**404 Not Found** — Resource doesn't exist
**422 Unprocessable Entity** — Request validation failed (see `errors` array for details)
**429 Too Many Requests** — Rate limit exceeded
**500 Internal Server Error** — An unexpected error occurred (contact support with request ID)
For a complete list of error codes and troubleshooting guidance, see the [Error Codes documentation](/api-reference/errors).
## API endpoints
### Public API (transactional)
**POST /v1/send** — Send transactional email(s)
- Accepts single or multiple recipients
- Template or inline content
- Variable substitution
**POST /v1/track** — Track event for contact
- Creates/updates contact
- Tracks custom event
- Can use public key
### Contacts
**GET /contacts** — List all contacts
**POST /contacts** — Create new contact
**GET /contacts/:id** — Get contact details
**PATCH /contacts/:id** — Update contact
**DELETE /contacts/:id** — Delete contact
### Templates
**GET /templates** — List all templates
**POST /templates** — Create new template
**GET /templates/:id** — Get template details
**PATCH /templates/:id** — Update template
**DELETE /templates/:id** — Delete template
### Campaigns
**GET /campaigns** — List all campaigns
**POST /campaigns** — Create new campaign
**GET /campaigns/:id** — Get campaign details
**PATCH /campaigns/:id** — Update campaign
**POST /campaigns/:id/send** — Send or schedule campaign
**POST /campaigns/:id/cancel** — Cancel scheduled campaign
**POST /campaigns/:id/test** — Send test email
**GET /campaigns/:id/stats** — Get campaign analytics
### Segments
**GET /segments** — List all segments
**POST /segments** — Create new segment
**GET /segments/:id** — Get segment details
**PATCH /segments/:id** — Update segment
**DELETE /segments/:id** — Delete segment
**GET /segments/:id/contacts** — List segment members
### Workflows
**GET /workflows** — List all workflows
**POST /workflows** — Create new workflow
**GET /workflows/:id** — Get workflow details
**PATCH /workflows/:id** — Update workflow
**DELETE /workflows/:id** — Delete workflow
**GET /workflows/:id/executions** — List workflow executions
### Events
**GET /events** — List all events
**GET /events/names** — List unique event names
### Domains
**GET /domains** — List verified domains
**POST /domains** — Add domain for verification
**DELETE /domains/:id** — Remove domain
## Client libraries
### Node.js
```javascript
const PLUNK_SECRET_KEY = process.env.PLUNK_SECRET_KEY;
async function sendEmail(to, subject, body) {
const response = await fetch('{{API_URL}}/v1/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${PLUNK_SECRET_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ to, subject, body })
});
const data = await response.json();
if (!data.success) {
throw new Error(`[${data.error.code}] ${data.error.message}`);
}
return data.data;
}
```
### Python
```python
import os
import requests
PLUNK_SECRET_KEY = os.environ['PLUNK_SECRET_KEY']
def send_email(to, subject, body):
response = requests.post(
'{{API_URL}}/v1/send',
headers={
'Authorization': f'Bearer {PLUNK_SECRET_KEY}',
'Content-Type': 'application/json'
},
json={'to': to, 'subject': subject, 'body': body}
)
data = response.json()
if not data.get('success'):
error = data['error']
raise Exception(f"[{error['code']}] {error['message']}")
return data['data']
```
### cURL
```bash
curl -X POST {{API_URL}}/v1/send \
-H "Authorization: Bearer $PLUNK_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"to": "[email protected]", "subject": "Hello", "body": "Message"}'
```
## What's next
- [Send your first email](/getting-started/quick-start)
- [View detailed endpoint docs](/api-reference/public-api)
- [Check error codes](/api-reference/errors)