Create Subscriber
Learn how to add new subscribers to your newsletter programmatically.
Create Subscriber
Penna provides a simple REST API to integrate newsletter functionality directly into your application. This guide will show you how to authenticate and add subscribers programmatically.
Authentication
All API requests must be authenticated using your newsletter's Public Key. You can find this key in your newsletter settings dashboard.
Include the key in the x-penna-public-key header of your requests.
x-penna-public-key: penn_your_public_key_hereImportant: Your Public Key is exposed to anyone who visits your website. While it creates a seamless integration, we recommend proxying these requests through your own backend API if you want to control rate limits or add additional spam protection layers before calling Penna.
Endpoints
Add New Subscriber
Adds a new subscriber to your newsletter. Subscribers are created with a "pending" status and must verify their email address through a double opt-in process before they can receive newsletters.
Endpoint: POST /api/v1/external/newsletters/subscriber/new
Headers:
Content-Type: application/jsonx-penna-public-key: <YOUR_PUBLIC_KEY>
Body:
{
"email": "user@example.com",
"name": "John Doe"
}Fields:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Subscriber's email address |
name | string | No | Subscriber's full name (useful for personalization) |
Code Examples
Here is how you can integrate the "Subscribe" functionality in various languages.
React / Next.js
import { useState } from "react";
export function NewsletterForm() {
const [email, setEmail] = useState("");
const [name, setName] = useState("");
const [status, setStatus] = useState("idle");
const subscribe = async (e) => {
e.preventDefault();
setStatus("loading");
try {
const res = await fetch(
"https://api.penna.dev/api/v1/external/newsletters/subscriber/new",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"x-penna-public-key": "penn_abc1234567890", // Replace with your public key
},
body: JSON.stringify({ email, name }),
},
);
if (!res.ok) throw new Error("Failed to subscribe");
setStatus("success");
setEmail("");
setName("");
} catch (err) {
setStatus("error");
}
};
return (
<form onSubmit={subscribe} className="flex flex-col gap-2">
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Your name (optional)"
className="border p-2 rounded"
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter your email"
required
className="border p-2 rounded"
/>
<button
type="submit"
disabled={status === "loading"}
className="bg-black text-white p-2 rounded"
>
{status === "loading" ? "Subscribing..." : "Subscribe"}
</button>
{status === "success" && <p className="text-green-500">Subscribed!</p>}
{status === "error" && (
<p className="text-red-500">Something went wrong.</p>
)}
</form>
);
}HTML / JavaScript (Vanilla)
<form id="newsletter-form">
<input type="text" id="name" placeholder="Your name (optional)" />
<input type="email" id="email" placeholder="your@email.com" required />
<button type="submit">Join Newsletter</button>
</form>
<script>
const form = document.getElementById("newsletter-form");
form.addEventListener("submit", async (e) => {
e.preventDefault();
const email = document.getElementById("email").value;
const name = document.getElementById("name").value;
try {
const response = await fetch(
"https://api.penna.dev/api/v1/external/newsletters/subscriber/new",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"x-penna-public-key": "penn_abc1234567890",
},
body: JSON.stringify({ email, name }),
},
);
if (response.ok) {
alert("Successfully subscribed!");
} else {
alert("Subscription failed.");
}
} catch (error) {
console.error("Error:", error);
}
});
</script>cURL
curl -X POST https://api.penna.dev/api/v1/external/newsletters/subscriber/new \
-H "Content-Type: application/json" \
-H "x-penna-public-key: penn_abc1234567890" \
-d '{"email": "test@example.com", "name": "John Doe"}'Python (Requests)
import requests
url = "https://api.penna.dev/api/v1/external/newsletters/subscriber/new"
headers = {
"Content-Type": "application/json",
"x-penna-public-key": "penn_abc1234567890"
}
data = {
"email": "test@example.com",
"name": "John Doe"
}
response = requests.post(url, json=data, headers=headers)
if response.status_code == 200:
print("Subscribed successfully!")
else:
print(f"Failed: {response.text}")Response
Success (200):
Subscribers are created with a "pending" status and must verify their email address before becoming active.
{
"success": true,
"message": "Verification email sent. Check your inbox to confirm.",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"newsletterId": "660e8400-e29b-41d4-a716-446655440001",
"email": "test@example.com",
"name": "John Doe",
"status": "pending",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
}Error Responses:
// Subscriber already verified and subscribed
{
"success": false,
"message": "Subscriber with this email already exists in the newsletter",
"data": null
}
// Subscriber exists but verification pending - resends verification email
{
"success": true,
"message": "Verification email sent again. Check your inbox to confirm.",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"..."
}
}
// Subscriber limit reached
{
"success": false,
"message": "Subscriber limit reached. Please upgrade your plan.",
"data": null
}
// Missing required fields
{
"success": false,
"message": "Email is required",
"data": null
}Use Cases
Collect Names for Personalization
Including the name field allows you to personalize your newsletters:
// In your signup form
const formData = {
email: userEmail,
name: userName, // Optional but recommended
};
await fetch(API_URL, {
method: "POST",
headers: { "x-penna-public-key": PUBLIC_KEY },
body: JSON.stringify(formData),
});Later in your newsletters, you can use the name for personalization:
- "Hi John," instead of "Hi there,"
- "Welcome John to our community!"
E-commerce Integration
// After checkout
await addSubscriber({
email: customer.email,
name: `${customer.firstName} ${customer.lastName}`,
});Event Registration
// After event signup
await addSubscriber({
email: attendee.email,
name: attendee.fullName,
});