> ## Documentation Index
> Fetch the complete documentation index at: https://ahasend.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> AhaSend stores all message data in the EU. See /facts.
> The API base URL is https://api.ahasend.com. Send the API key in the Authorization: Bearer header. See /api-reference/authentication.
> Use send.ahasend.com for SMTP. Ports 25, 587 and 2525 use STARTTLS. Port 465 is not supported. See /smtp.
> For send-only integrations, create an API Key v2 under Credentials → Add and scope it to messages:send:{your-domain}. See /send-api/credentials and /api-reference/scopes.

# Contacts, Custom Fields and Lists

> Import contacts from a CSV or your app, add custom fields, build lists, filter them with advanced search and understand contact status versus list status.

A campaign sends to lists. A list is a group of contacts, and a contact is one person in your account, found by email address. This page explains how contacts come in, what the custom fields are for, how lists work and what the different statuses mean.

## Contacts

A contact exists once in your account, whatever the number of lists it is on. The email address is the key: AhaSend trims it and lowercases it, so `Ann@Example.com` and `ann@example.com` are the same contact.

Contacts come in three ways:

* **CSV import** on the Contacts page, on a list, or while you create a new list. A file can be up to 100 MB. See [importing a CSV](#importing-a-csv).
* **By hand**, one contact at a time, on the Contacts page. To put an existing contact on a list, use **Add to List** on the contact's page.
* **From your own app or website**, through the [Management API](/docs/api-reference/contacts/overview): one contact per request, or up to 1,000 per request with [Batch Upsert Contacts](/docs/api-reference/contacts/batch-upsert-contacts).

Every contact has its own page with its fields, the lists it is on, its opens and clicks, and every campaign it received.

### Importing a CSV

The import matches the columns of your file to the contact fields by their header: `email`, `first_name`, `last_name`, `status` and the name or key of each of your [custom fields](#custom-fields). Columns that match no field are ignored. There is no preview: the file is imported when you upload it. Rules that apply to every import:

* A contact that already exists is updated, not duplicated.
* An empty cell never wipes a value you already have.
* A contact that unsubscribed from everything stays unsubscribed. A contact that unsubscribed from a list is not put back on that list by a new import.
* The address column can be named `email` or `Email Address`, so a Mailchimp export imports as it is. From Mailchimp, import only the subscribed contacts file: the unsubscribed and cleaned files have no status column, so their contacts would come in as subscribed.
* Save the file as UTF-8. A file saved in another encoding imports names with accents or non-Latin letters garbled; export it again as CSV UTF-8.

Import into a list when the file is a list, for example your newsletter subscribers. Import on the Contacts page when you only want the contacts in your account, and add them to lists later.

### Contact Status

| Status | Meaning |
| - | - |
| Enabled | A normal contact. Campaigns reach it, on the lists where it is subscribed. |
| Disabled | Campaigns skip it on every list, and its memberships stay as they are. AhaSend disables a contact when a campaign to it hard-bounces; you can also set it. |
| Blocked | You blocked the contact. Campaigns skip it on every list. |

You set the status when you add a contact, or with a `status` column in a CSV import of new contacts. To change the status of an existing contact, use [Update Contact](/docs/api-reference/contacts/update-contact).

Next to the status, a contact can be **unsubscribed from everything**. That is the account-wide opt-out: campaigns skip the contact on every list, and its memberships stay as they are.

Email validation adds a third signal: a contact whose address was found invalid is left out of campaigns.

## Custom Fields

Add the fields your campaigns need, such as company, plan, city, VIP or customer since, under **Marketing > Contact fields**. A field has a name, a key and a type: text, number, yes/no, date or date/time. The type is fixed when you create the field, so a number stays a number. Renaming a field changes only its name, not its key, so it does not break the campaigns, searches or API calls that use it.

Custom fields are used in two places:

* **Filtering**: find the contacts that match, with [advanced search](#advanced-search-and-saved-lists).
* **Personalization**: write `{{ first_name }}` or the key of any field of your own in the email, and each recipient sees their own value. Give a default for contacts where the field is empty. See [personalization](/docs/campaigns/create#personalization).

Through the API, custom fields are the `attributes` of a contact. The API accepts only fields that you defined in the dashboard, with a value of the right type. See [attributes](/docs/api-reference/contacts/overview#attributes).

## Lists

A list is a static group of contacts and the unit a campaign targets. A campaign sends to one or more lists; through the API, up to 50. List names do not have to be unique, so give lists names that tell them apart.

The Lists page shows two numbers for each list: how many contacts are on it, and how many a campaign would reach. The second number counts a contact when its membership is confirmed, the contact is enabled, it has not unsubscribed from everything, and email validation did not find its address invalid. The API returns the same number as `contact_count`.

### List Status

Each contact on a list has its own status on that list, separate from the contact status above:

| List status | Meaning |
| - | - |
| Confirmed | Subscribed. A campaign to the list reaches the contact. |
| Unconfirmed | On the list, but not subscribed. A campaign does not reach the contact. |
| Unsubscribed | The contact unsubscribed from this list. |
| Complained | The contact reported a campaign to this list as spam. AhaSend sets this status from the mailbox provider's report; it cannot be changed. |

A list page can therefore show a contact as **Confirmed** on the list and **Blocked** as a contact at the same time. The two columns answer different questions: is the contact subscribed to this list, and can AhaSend send to this contact at all. A campaign reaches the contact only when both answers are yes.

### Unsubscribe, Remove or Delete

| Goal | What to do | Effect |
| - | - | - |
| Stop email from one list | Set the list status to unsubscribed with [Add or Update a List Contact](/docs/api-reference/lists/upsert-list-contact) | Can be undone: **Add to List** on the contact's page subscribes the contact again. The record of the unsubscribe stays |
| Stop email from every list | Set `unsubscribed: true` with [Update Contact](/docs/api-reference/contacts/update-contact) | Can be undone through the API; the memberships stay |
| Take a contact off a list | **Remove** on the list page or on the contact's page | The membership and its unsubscribe record are deleted, so a later import adds the contact again, silently. A complained membership cannot be removed |
| Delete a list | On the list's page, **Actions > Delete List** | The list is gone; the contacts and their unsubscribes and complaints are kept. A list that an unfinished campaign uses cannot be deleted |
| Delete a contact | Delete it on the Contacts page | The contact and its history are gone |

The dashboard has no button to unsubscribe a contact; recipients unsubscribe themselves with the link in a campaign. To stop marketing email to a contact yourself, unsubscribe it through the API rather than removing it.

Contacts and [suppressions](/docs/bounces) are separate records. A suppression you add by hand, or one that a bounce of your transactional mail creates, does not change any contact or list status.

## Advanced Search and Saved Lists

On a list page, the search box finds contacts by email, first name or last name, and advanced search filters on any field, with AND and OR rules and groups, relative dates such as "7 days ago", and checks for empty fields. The page shows how many contacts match out of the total. To send to just those contacts, click **Save as new list** and the matching contacts are copied to a new list; this works for a plain search as well as for an advanced search.

The new list is a copy, made when you save it. The contacts are copied in the background, and the new list's page shows when the copy is done. Contacts that match later are not added, and every contact keeps its status: a contact who unsubscribed stays unsubscribed on the new list too.

The **Send Campaign** button on a list page starts a campaign for the whole list, also when a search narrows what you see. To send to the matching contacts only, save them as a new list first.

## Export

**Export CSV** on the Contacts page exports all contacts of your account. The file has the standard fields, the status and your custom fields, in the order you set under **Marketing > Contact fields**.

## Related Guides

* [Campaign Builder overview](/docs/campaigns), [Creating a campaign](/docs/campaigns/create), [Reports](/docs/campaigns/reports).
* [Contacts API](/docs/api-reference/contacts/overview), [Lists API](/docs/api-reference/lists/overview), [Bounces and suppressions](/docs/bounces).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.