Contact Management
Relevant source files
The following files were used as context for generating this wiki page:
Contact Management in the Send Again platform encompasses the lifecycle of recipient data, including ingestion via CSV or API, metadata management through arbitrary fields, and automated suppression handling via unsubscriptions and bounces. The system is designed to handle large-scale contact lists within multi-domain workspaces, ensuring that email campaigns target only valid and consenting recipients.
The architecture relies on a central database (Supabase/Postgres) to store contact records and a suppression list (unsubscribes). Key operations include bulk upserts, field-level updates, and dynamic filtering against suppression records during the send process to maintain deliverability and compliance.
Data Model and Storage
Contacts are stored within specific workspaces, identified by a domain name. Each contact is uniquely identified by an email address within that workspace.
Contact Schema
A contact consists of a primary email address and an extensible fields object. The fields object allows developers to store arbitrary key-value pairs (as strings) for personalization, such as names, company details, or custom status flags.
| Field | Type | Description |
|---|---|---|
email | String (Format: email) | The primary unique identifier for the contact within a workspace. |
fields | Object (Map<String, String>) | Arbitrary metadata used for template variables and segmentation. |
Sources: src/lib/openapi.ts:25-35, src/lib/db.ts:446-449
Database Interaction
The system uses several lower-level functions to interact with the contacts table:
getContacts(workspaceId): Retrieves all contacts for a workspace, utilizing pagination to handle large lists that might exceed standard API limits.upsertContacts(workspaceId, contacts): Performs a bulk insert or update, resolving conflicts based on theworkspace_idandemailunique constraint.updateContact(workspaceId, email, fields): Updates metadata for a specific recipient.
Sources: src/lib/db.ts:466-511, src/lib/db.ts:522-540
Ingestion and Synchronization
Contacts can be added to the system through three primary methods: manual entry, CSV uploads via the dashboard, and programmatically via the REST API.
CSV Import Logic
The dashboard implementation includes a robust CSV parser that handles various delimiters (comma, semicolon, tab) and BOM characters. It provides a column mapping interface where users can select which CSV columns map to specific contact metadata fields.
The system processes CSV imports in batches (default CONTACT_IMPORT_BATCH_SIZE = 1000) to ensure stability during large uploads.
Sources: src/app/app/page.tsx:755-900, src/app/app/page.tsx:1423-1490
API Endpoints
The Contact Management system exposes a standard RESTful interface:
| Method | Endpoint | Description |
|---|---|---|
GET | /api/contacts | List all contacts in a workspace. |
POST | /api/contacts | Create or update contacts (upsert). |
DELETE | /api/contacts | Delete all contacts in a workspace. |
GET | /api/contacts/{email} | Retrieve a single contact's details. |
PUT | /api/contacts/{email} | Update metadata fields for a contact. |
DELETE | /api/contacts/{email} | Remove a single contact. |
Sources: src/lib/openapi.ts:133-255, src/app/api/contacts/route.ts
Suppression and Unsubscribe Handling
A critical component of contact management is the automated handling of unsubscribes to ensure compliance with email regulations.
Suppression Logic
Before contacts are added or emails are sent, the system filters the recipient list against the contact_unsubscribes table. This prevents re-importing users who have previously opted out.
Unsubscribe Workflow
When a recipient clicks an unsubscribe link, the system performs the following:
- Token Verification: Validates a HMAC-signed token to prevent unauthorized unsubscriptions.
- Suppression Record: Adds the email to the
contact_unsubscribestable with a timestamp. - Contact Deletion: Removes the contact from the active
contactslist for the workspace. - Redirection: Redirects the user to a workspace-specific unsubscribe landing page or a default platform page.
Sources: src/app/api/unsubscribe/route.ts:104-165, src/lib/db.ts:568-584
Dynamic Selection and Rules
The platform allows for dynamic selection of contacts based on their metadata (fields) or their engagement history.
Selection Logic
Users can define "Recipient Conditions" to filter the contact list. These conditions support multiple match modes (all or any) and various operators for field comparison (e.g., equals, contains).
| Condition Kind | Parameters | Logic |
|---|---|---|
| Field | field, operator, value | Compares the string value in the contact's metadata. |
| History | eventType, subject, matchMode | Filters contacts based on past interactions (Opens, Clicks, Bounces) with specific email subjects. |
Sources: src/app/app/page.tsx:176-240, src/app/app/page.tsx:1150-1200
Conclusion
Contact Management provides the foundational data layer for the platform's email operations. By integrating flexible metadata storage with automated suppression and a comprehensive API, the system enables sophisticated audience targeting while maintaining high deliverability standards and legal compliance.