UniLink API: Contacts Endpoint (Access Your CRM via API)

UniLink API: Contacts Endpoint (Access Your CRM via API)
The /contacts endpoint exposes your UniLink CRM programmatically — sync contacts with external tools, trigger automations, and manage tags without opening the dashboard.
- Use
GET /contactswith tag, date, or source filters to pull targeted segments for external tools. - Create and update contacts programmatically — including custom fields and tag arrays — via POST and PUT.
- The email field is the unique identifier; use it to upsert contacts without creating duplicates.
Every email address collected through your UniLink forms, checkout flows, and landing pages lands in your contacts list. The /contacts endpoint gives you programmatic access to that data so you can push it to your email platform, trigger CRM workflows, or run custom segmentation logic in your own tooling. Whether you are syncing new subscribers into Mailchimp, triggering an onboarding sequence in your marketing automation platform, or building a custom reporting tool, the contacts endpoint is the read-write bridge between UniLink and your broader stack.
What the Contacts Endpoint Does
The GET /contacts endpoint returns a paginated list of all contacts in your UniLink CRM. Each contact object contains the core profile fields: id, email, name, tags (array of strings), custom_fields (key-value object), source (which UniLink form or page collected the contact), and subscribed_at (ISO 8601 timestamp). The list endpoint supports filtering by tag, source, and date range, making it possible to extract precise segments without pulling and filtering client-side.
Creating a contact with POST /contacts requires only an email field. All other fields — name, tags, custom fields — are optional at creation and can be added later via PUT. If you attempt to create a contact with an email that already exists in your account, the API returns a 409 Conflict response with the existing contact's ID. This prevents accidental duplicates. To handle "create if not exists, update if exists" logic, first attempt the POST and on 409 use the returned ID to issue a PUT instead.
The custom_fields object accepts any key-value pairs you define — for example, {"company": "Acme", "plan_tier": "pro"}. These fields are stored on the contact record and returned in all GET responses. Custom fields are not pre-defined in the API schema; you can use any string keys. Tags are simpler: they are a flat array of strings like ["subscriber", "course-buyer", "vip"]. When you PUT a contact with a new tags array, the tags are replaced entirely — if you want to add a single tag without removing existing ones, fetch the contact first, append the new tag to the existing array, and send the full updated array.
How to Get Started
- Generate an API key with write scope from Settings → API in your dashboard at app.unilink.us. Read scope is sufficient for syncing to external tools; write scope is needed for creating or updating contacts.
- Fetch your first contacts page to understand the data:
curl -H "Authorization: Bearer YOUR_KEY" "https://unilink.us/api/v1/contacts?per_page=5". Review the field names, tag formats, and custom field keys that already exist. - Test a filtered fetch using a known tag:
GET /contacts?tag=subscriber. Confirm the filter returns only contacts with that tag applied. - Create a test contact to verify your write access:
POST /contactswith body{"email": "test@example.com", "name": "Test User", "tags": ["api-test"]}. Copy theidfrom the response. - Update the test contact with a custom field:
PUT /contacts/{id}with body{"custom_fields": {"source_system": "api-test"}}. Confirm the field appears in a subsequent GET response.
How to Use the Contacts Endpoint
- List all contacts:
GET /contacts?page=1&per_page=100— use pagination to walk through the full list. The response includestotalandhas_morefor loop control. - Filter by tag:
GET /contacts?tag=vipreturns contacts with the "vip" tag. Use this to export segments to email platforms without client-side filtering. - Filter by source:
GET /contacts?source=homepage-formpulls contacts from a specific form or page. Source values match the names you assign to forms in the dashboard. - Filter by date:
GET /contacts?start_date=2026-01-01&end_date=2026-01-31filters bysubscribed_at— useful for syncing new contacts since the last run. - Add tags to a contact: Fetch the contact with
GET /contacts/{id}, append to thetagsarray, thenPUT /contacts/{id}with the full updated array — the PUT replaces tags entirely, so always include existing tags in the array.
Key Settings
| Setting | What It Does | Recommended |
|---|---|---|
email | Unique identifier for a contact; cannot be changed after creation | Always use lowercase email addresses to avoid case-sensitivity duplicates |
tags | Array of strings for segmentation; replaced entirely on PUT | Fetch before updating to preserve existing tags — never send a partial array |
custom_fields | Freeform key-value object for additional profile data | Use consistent key names across your integration; snake_case is recommended |
source | Read-only field set by UniLink when the contact is created; indicates which form/page captured the contact | Use as a filter to segment contacts by acquisition channel in external tools |
subscribed_at | ISO 8601 timestamp of when the contact was added to your CRM | Use as the cursor for incremental sync — store the latest value and use it as start_date on the next run |
subscribed_at timestamp of the last contact you processed, then on each sync run fetch GET /contacts?start_date={last_timestamp}. This is far more efficient than fetching all contacts and comparing — especially once your contact list grows into the thousands.
Get the Most Out Of the Contacts Endpoint
Design your tag taxonomy before you start creating contacts via API. Tags are the primary segmentation mechanism in UniLink CRM, and they carry through to filtering at the API level. Decide on a consistent naming convention — for example, interest tags, purchase tags, and lifecycle tags — and document it in your integration code. A flat, consistent set of tags is much easier to query and maintain than ad-hoc tags created across multiple systems.
Use the source filter to build per-acquisition-channel dashboards. If you have multiple UniLink forms — a header opt-in, a checkout form, and a lead magnet download — each creates contacts with a different source value. Pulling contacts grouped by source and comparing subscription volume over time gives you the kind of acquisition data that most email platforms do not surface natively without custom event tracking.
Custom fields are the right place to store external system IDs. When you sync a UniLink contact to your CRM, write the external CRM ID back to the contact's custom_fields: {"crm_id": "hs_123456"}. This creates a bidirectional reference so future updates from either system can find the matching record without a fragile email-matching lookup.
The DELETE endpoint for contacts requires admin scope and permanently removes the contact record. For GDPR erasure requests, this is the correct endpoint to use. For contacts who simply unsubscribe from email, update their record with a tag like unsubscribed and respect that tag in your downstream email system — do not delete the contact unless the user has specifically requested erasure, since deleting removes the consent and audit trail as well.
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
| POST returns 409 Conflict | A contact with this email already exists in your account | Use the id in the 409 response to issue a PUT /contacts/{id} update instead of creating a duplicate |
| Tags disappear after a PUT update | PUT replaces the entire tags array — sending only new tags deletes existing ones | Always fetch the current contact first, merge the new tags into the existing array, then PUT the full merged array |
| Source filter returns no results | Source value in the filter does not exactly match the source stored on the contacts | Fetch a contact from the expected source and check the exact source string value — it is case-sensitive and must match the form name in the dashboard |
| DELETE returns 403 | API key has write scope, not admin scope | Generate a new admin-scope key for deletion operations, or use a tag to mark the contact as inactive instead |
- Email as a unique key makes deduplication logic straightforward — 409 responses immediately surface conflicts
- Tags and custom fields provide flexible segmentation and profile enrichment without a rigid schema
- Date and source filters enable incremental sync patterns that scale to large contact lists
- Custom fields support bidirectional ID references, enabling clean joins between UniLink and external CRMs
- PUT replaces the full tags array — adding a single tag requires a fetch-merge-update cycle
- The
sourcefield is read-only and set by UniLink — you cannot assign a custom source when creating contacts via API - No bulk create endpoint — mass imports require looping POST requests with rate-limit awareness
Can I import a CSV of contacts via the API?
There is no bulk import endpoint. For CSV imports, use the dashboard's built-in import feature under Contacts → Import. For programmatic bulk creation, loop through your records and send individual POST requests, spacing them to stay within the rate limit.
Can the same email address appear in multiple accounts?
Yes — contacts are scoped to your account. The same email address can exist as a contact on multiple UniLink accounts independently. UniLink does not deduplicate across accounts.
What happens to contact data if I delete a contact via API?
The contact record is permanently deleted, including all tags, custom fields, and the subscribed_at timestamp. Form submission and order history references may retain the email address for accounting purposes, but the contact profile is gone. This operation is irreversible.
Is there a maximum number of custom field keys per contact?
UniLink supports up to 50 custom field keys per contact. Each key accepts a string value up to 1,000 characters. If you need to store structured data, serialize it as a JSON string in a single custom field value.
Can I filter contacts by multiple tags at once?
The current API supports filtering by a single tag at a time using the tag parameter. For multi-tag filtering (contacts with tag A AND tag B), fetch by one tag and apply the second filter client-side, or use the dashboard's segment builder which supports compound tag logic.
- The contacts endpoint base URL is
https://unilink.us/api/v1/contacts— authenticate every request with a Bearer token. - Email is the unique identifier — POSTing a duplicate email returns 409 with the existing contact's ID, which you should then use for a PUT update.
- PUT replaces the entire tags array — always fetch first, merge, then update to preserve existing tags.
- Filter by
tag,source, and date range to extract precise segments for external tool syncs. - Store external system IDs in
custom_fieldsto maintain bidirectional references across your stack.
Start syncing your UniLink contacts with your external tools — generate an API key at app.unilink.us under Settings → API and make your first GET /contacts request today.
Create Your Free Link-in-Bio Page
Join thousands of creators using UniLink. 40+ blocks, analytics, e-commerce, and AI tools — all free.
Get Started Free