Newsletter API
The Newsletter API allows users to subscribe and unsubscribe from the Artbase Gallery newsletter. Newsletter subscribers receive updates about new artists, collections, and featured work.
Endpoints
Subscribe to Newsletter
Adds an email address to the gallery newsletter.
Endpoint: POST /api/gallery/newsletter/subscribe
Authentication: None
Request Body:
{
"email": "user@example.com",
"firstName": "John",
"lastName": "Doe"
}
Request Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Email address (validated) |
firstName | string | No | Subscriber's first name |
lastName | string | No | Subscriber's last name |
Response:
{
"success": true,
"subscriber": {
"id": "sub_abc123",
"email": "user@example.com",
"isActive": true,
"subscribedAt": "2024-01-15T10:30:00Z"
}
}
Response Fields:
| Field | Type | Description |
|---|---|---|
success | boolean | Always true on success |
subscriber.id | string | Subscriber UUID |
subscriber.email | string | Email address |
subscriber.isActive | boolean | Subscription status |
subscriber.subscribedAt | string | ISO 8601 timestamp |
Example Request:
curl -X POST https://gallery.artbase.studio/api/gallery/newsletter/subscribe \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"firstName": "John",
"lastName": "Doe"
}'
Error Responses:
| Status | Error | Description |
|---|---|---|
| 400 | Email is required | Missing email |
| 400 | Invalid email address | Email validation failed |
Special Cases:
- Already subscribed - Returns existing subscriber, updates
isActive: true - Previously unsubscribed - Reactivates subscription
Unsubscribe from Newsletter
Removes an email address from the gallery newsletter.
Endpoint: POST /api/gallery/newsletter/unsubscribe
Authentication: None
Request Body:
{
"email": "user@example.com"
}
Request Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Email address to unsubscribe |
Response:
{
"success": true
}
Example Request:
curl -X POST https://gallery.artbase.studio/api/gallery/newsletter/unsubscribe \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com"
}'
Error Responses:
| Status | Error | Description |
|---|---|---|
| 400 | Email is required | Missing email |
| 404 | Email not found | Email not in database |
Note: Sets isActive: false rather than deleting record (for compliance and resubscription handling).
Email Validation
Emails are validated using standard regex:
function isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
Valid examples:
- ✅
user@example.com - ✅
john.doe@company.co.uk - ✅
artist+gallery@studio.art
Invalid examples:
- ❌
user@example(no TLD) - ❌
user@.com(no domain) - ❌
@example.com(no local part)
Newsletter Content
Subscribers receive periodic emails about:
- New artists - When new artists join the gallery
- Featured collections - Monthly curated collections
- Artist spotlights - Stories and features
- Seasonal updates - Holiday collections, sales
- Gallery news - Platform updates, improvements
Frequency: 1-2 emails per month (not daily)
Client Integration
Newsletter Signup Form
import { useState } from 'react';
export function NewsletterSignup() {
const [email, setEmail] = useState('');
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
const [message, setMessage] = useState('');
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setStatus('loading');
try {
const res = await fetch('/api/gallery/newsletter/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, firstName, lastName }),
});
if (res.ok) {
setStatus('success');
setMessage('Successfully subscribed to the newsletter!');
setEmail('');
setFirstName('');
setLastName('');
} else {
const data = await res.json();
setStatus('error');
setMessage(data.error || 'Failed to subscribe');
}
} catch (error) {
setStatus('error');
setMessage('Network error. Please try again.');
}
};
return (
<form onSubmit={handleSubmit}>
<h3>Subscribe to Gallery Newsletter</h3>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<input
type="text"
placeholder="First Name"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
<input
type="text"
placeholder="Last Name"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
/>
<button type="submit" disabled={status === 'loading'}>
{status === 'loading' ? 'Subscribing...' : 'Subscribe'}
</button>
{message && (
<p className={status === 'success' ? 'success' : 'error'}>
{message}
</p>
)}
</form>
);
}
Footer Newsletter Form
export function Footer() {
return (
<footer>
<div className="newsletter-section">
<h4>Stay Updated</h4>
<p>Get monthly updates on new artists and collections</p>
<NewsletterSignup />
</div>
</footer>
);
}
Unsubscribe Link in Emails
Every newsletter email should include an unsubscribe link:
<p>
No longer interested?
<a href="https://gallery.artbase.studio/unsubscribe?email={{email}}">
Unsubscribe
</a>
</p>
Database Schema
CREATE TABLE gallery_newsletter_subscribers (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email TEXT UNIQUE NOT NULL,
first_name TEXT,
last_name TEXT,
is_active BOOLEAN DEFAULT TRUE,
subscribed_at TIMESTAMPTZ DEFAULT NOW(),
unsubscribed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_newsletter_email ON gallery_newsletter_subscribers(email);
CREATE INDEX idx_newsletter_active ON gallery_newsletter_subscribers(is_active);
GDPR & Privacy Compliance
Data Stored
Newsletter subscription stores:
- ✅ Email address
- ✅ First and last name (optional)
- ✅ Subscription timestamps
- ✅ Active status
User Rights
- Right to access: Users can request their subscription data
- Right to deletion: Unsubscribe or request full deletion
- Right to portability: Export subscription data
- Right to correction: Update email or name
Consent
- Explicit opt-in: Users must actively subscribe
- Clear purpose: Explain what emails they'll receive
- Easy opt-out: Unsubscribe link in every email
Privacy Policy
Include newsletter details in privacy policy:
Newsletter Subscriptions
When you subscribe to our newsletter, we collect:
- Email address (required)
- First and last name (optional)
We use this information to send periodic updates about:
- New artists and collections
- Gallery features and news
- Special events and sales
You can unsubscribe anytime using the link in any email.
We will never sell or share your email address with third parties.
Email Service Integration
The Newsletter API stores subscriptions but doesn't send emails directly. Integrate with an email service:
Recommended Services
| Service | Pricing | Features |
|---|---|---|
| SendGrid | Free tier: 100/day | Templates, analytics |
| Mailchimp | Free tier: 500 subscribers | Campaigns, automation |
| AWS SES | $0.10/1000 emails | Scalable, low cost |
| ConvertKit | Starts at $29/mo | Creator-focused, landing pages |
Integration Example (SendGrid)
import sgMail from '@sendgrid/mail';
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
export async function sendNewsletterEmail(
subscribers: Array<{ email: string; firstName?: string }>,
subject: string,
htmlContent: string
) {
const messages = subscribers
.filter(sub => sub.is_active)
.map(sub => ({
to: sub.email,
from: 'newsletter@artbase.studio',
subject: subject,
html: htmlContent.replace('{{firstName}}', sub.firstName || 'Friend'),
}));
await sgMail.send(messages);
}
Sending a Campaign
// Fetch active subscribers
const { data: subscribers } = await supabase
.from('gallery_newsletter_subscribers')
.select('email, first_name')
.eq('is_active', true);
// Send email
await sendNewsletterEmail(
subscribers,
'New Artists This Month',
newsletterTemplate
);
Testing
Test Subscription
# Subscribe
curl -X POST http://localhost:3000/api/gallery/newsletter/subscribe \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"firstName": "Test",
"lastName": "User"
}'
# Verify in database
# SELECT * FROM gallery_newsletter_subscribers WHERE email = 'test@example.com';
# Unsubscribe
curl -X POST http://localhost:3000/api/gallery/newsletter/unsubscribe \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com"}'
# Verify is_active = false