Contact Form API
The PerspectAPI v2 SDK provides a contact form system with rate limiting, email notifications, and admin management.
Quick Start
import { PerspectApiV2Client } from 'perspectapi-ts-sdk';
const client = new PerspectApiV2Client({
baseUrl: 'https://api.example.com',
apiKey: 'your-api-key'
});
// Submit a contact form
const submission = await client.contacts.submit('my-site', {
name: 'John Doe',
email: 'john@example.com',
subject: 'Product Inquiry',
message: 'I would like to know more about your products.'
});
console.log(submission.id); // "ctc_42"
console.log(submission.object); // "contact_submission"
console.log(submission.status); // "pending"
API Reference
contacts.submit(siteName, data)
Submit a contact form to a specific site. Returns the created submission.
const submission = await client.contacts.submit('my-site', {
email: 'jane@company.com', // Required
message: 'Partnership inquiry', // Required
name: 'Jane Smith', // Optional
first_name: 'Jane', // Optional (alternative to name)
last_name: 'Smith', // Optional
subject: 'Partnership', // Optional
phone: '+1-555-0123', // Optional
company: 'Acme Corp', // Optional
metadata: { // Optional: custom fields
source: 'homepage-footer',
campaign: 'summer-2025'
}
});
Response:
{
"object": "contact_submission",
"id": "ctc_42",
"name": "Jane Smith",
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@company.com",
"subject": "Partnership",
"message": "Partnership inquiry",
"phone": "+1-555-0123",
"company": "Acme Corp",
"status": "pending",
"metadata": { "source": "homepage-footer", "campaign": "summer-2025" },
"created_at": "2025-04-01T12:00:00Z",
"processed_at": null
}
contacts.list(siteName, params?)
List contact submissions (admin). Returns a paginated list.
const submissions = await client.contacts.list('my-site', {
limit: 20,
status: 'pending'
});
// Response shape:
// {
// object: "list",
// data: [{ object: "contact_submission", id: "ctc_42", ... }],
// has_more: true,
// url: "/v2/sites/my-site/contacts"
// }
for (const sub of submissions.data) {
console.log(`${sub.id}: ${sub.email} - ${sub.subject}`);
}
contacts.get(siteName, id)
Get a single contact submission by ID.
const submission = await client.contacts.get('my-site', 'ctc_42');
console.log(submission.message);
Rate Limiting
The API enforces rate limiting: 5 submissions per 15 minutes per IP/email combination. When exceeded, the API returns a rate_limit_error:
{
"error": {
"type": "rate_limit_error",
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Please try again later."
}
}
Error Handling
import { PerspectV2Error } from 'perspectapi-ts-sdk';
try {
await client.contacts.submit('my-site', data);
} catch (err) {
if (err instanceof PerspectV2Error) {
switch (err.type) {
case 'invalid_request_error':
console.error('Validation failed:', err.message);
break;
case 'rate_limit_error':
console.error('Rate limited, retry later');
break;
case 'not_found_error':
console.error('Site not found');
break;
default:
console.error('API error:', err.message);
}
}
}
Framework Examples
React Form
import { PerspectApiV2Client, PerspectV2Error } from 'perspectapi-ts-sdk';
const client = new PerspectApiV2Client({
baseUrl: import.meta.env.VITE_API_URL,
apiKey: import.meta.env.VITE_API_KEY
});
function ContactForm() {
const [status, setStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle');
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setStatus('sending');
const form = new FormData(e.currentTarget);
try {
await client.contacts.submit('my-site', {
name: form.get('name') as string,
email: form.get('email') as string,
message: form.get('message') as string,
});
setStatus('sent');
} catch (err) {
setStatus('error');
}
}
return (
<form onSubmit={handleSubmit}>
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" required />
<button type="submit" disabled={status === 'sending'}>
{status === 'sending' ? 'Sending...' : 'Send'}
</button>
</form>
);
}
Remix Action
import { PerspectApiV2Client } from 'perspectapi-ts-sdk';
export async function action({ request }: ActionFunctionArgs) {
const client = new PerspectApiV2Client({
baseUrl: process.env.API_URL!,
apiKey: process.env.API_KEY!
});
const form = await request.formData();
const submission = await client.contacts.submit('my-site', {
name: form.get('name') as string,
email: form.get('email') as string,
subject: form.get('subject') as string,
message: form.get('message') as string,
});
return { success: true, id: submission.id };
}