MailLandDocs

Subscribers

Manage your subscriber contacts, import lists, and track engagement

Manage your email subscriber contacts. Each subscriber has a profile with engagement tracking, custom fields, and status management.

Subscriber Statuses

StatusValueDescription
Active0Subscribed and receiving emails
Unsubscribed1Opted out of emails
Bounced2Hard bounce recorded
Complained3Filed a spam complaint
Pending4Reserved: MailLand has no double opt-in flow, so nothing ever sets this status

optInMethod and the consent* fields on a subscriber are recorded as evidence only: they do not drive any confirmation workflow.

Create a Subscriber

POST https://api.mail.land/v1/marketing/subscribers
Create subscriber request
{
  "email": "[email protected]",
  "firstName": "John",
  "lastName": "Doe",
  "phone": "+1234567890",
  "company": "Acme Corp",
  "mailingListId": "ddv6ou6kz4wbto",
  "source": "api",
  "birthday": "1990-05-14",
  "customFields": {
    "plan": "enterprise",
    "region": "us-west"
  }
}
FieldTypeRequiredDescription
emailstringYesEmail address
firstNamestringNoFirst name
lastNamestringNoLast name
phonestringNoPhone number
companystringNoCompany name
mailingListIdstringNoMailing list to add to
sourcestringNoSubscription source (e.g., api, form, import)
birthdaystringNoYYYY-MM-DD. Read by the DateBased automation trigger
customFieldsobjectNoKey-value custom fields
Create a subscriber
curl -X POST https://api.mail.land/v1/marketing/subscribers \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "firstName": "John",
    "lastName": "Doe",
    "mailingListId": "ddv6ou6kz4wbto",
    "source": "api"
  }'

Response

Subscriber response
{
  "id": "exz31qoqlf01sj",
  "email": "[email protected]",
  "firstName": "John",
  "lastName": "Doe",
  "phone": "+1234567890",
  "company": "Acme Corp",
  "status": "active",
  "statusName": "Active",
  "source": "api",
  "mailingListId": "ddv6ou6kz4wbto",
  "mailingListName": "Product Updates",
  "customFields": {
    "plan": "enterprise",
    "region": "us-west"
  },
  "subscribedAt": "2026-02-17T12:00:00Z",
  "unsubscribedAt": null,
  "createdAt": "2026-02-17T12:00:00Z",
  "updatedAt": "2026-02-17T12:00:00Z"
}

List Subscribers

GET https://api.mail.land/v1/marketing/subscribers
ParameterTypeDefaultDescription
cursorstring-Opaque cursor from the previous page's nextCursor. Omit for the first page.
limitint20Items per page
searchstring-Search by email or name (not company)
mailingListIdstring-Filter by mailing list
statusint-Filter by subscriber status
sortBystring-Sort field
sortDescboolfalseSort descending
List subscribers
curl "https://api.mail.land/v1/marketing/subscribers?limit=20&mailingListId=ddv6ou6kz4wbto" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Get Subscriber Details

GET https://api.mail.land/v1/marketing/subscribers/{id}

Returns an extended profile with engagement metrics:

Get subscriber details
curl https://api.mail.land/v1/marketing/subscribers/exz31qoqlf01sj \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
Subscriber detail response
{
  "id": "exz31qoqlf01sj",
  "email": "[email protected]",
  "firstName": "John",
  "lastName": "Doe",
  "phone": "+1234567890",
  "company": "Acme Corp",
  "status": "active",
  "source": "api",
  "mailingListId": "ddv6ou6kz4wbto",
  "mailingListName": "Product Updates",
  "customFields": { "plan": "enterprise" },
  "birthday": "1990-05-14",
  "emailsSent": 50,
  "emailsOpened": 25,
  "emailsClicked": 10,
  "lastEmailSentAt": "2026-02-17T09:30:00Z",
  "lastEmailOpenedAt": "2026-02-16T15:20:00Z",
  "lastEmailClickedAt": "2026-02-15T11:10:00Z",
  "openRate": 50.0,
  "clickRate": 40.0,
  "subscribedAt": "2026-01-15T08:00:00Z",
  "createdAt": "2026-01-15T08:00:00Z",
  "updatedAt": "2026-02-17T12:00:00Z"
}

Update a Subscriber

PUT https://api.mail.land/v1/marketing/subscribers/{id}
Update subscriber request
{
  "firstName": "Jane",
  "lastName": "Smith",
  "phone": "+1234567890",
  "company": "TechCorp",
  "mailingListId": "ddv6ou6kz4wbto",
  "customFields": { "plan": "pro" },
  "birthday": "1990-05-14",
  "status": "Active"
}

PUT replaces the whole profile, it does not merge. firstName, lastName, phone, company and mailingListId are all overwritten with null (or list removal) when omitted, not left unchanged. Omitting mailingListId removes the subscriber's list membership. Send every field you want to keep, including mailingListId. customFields and status are left unchanged when omitted. birthday follows its own presence rule: omit the field to leave it unchanged, send "birthday": null to clear it, or send a YYYY-MM-DD value to set it, an empty string ("") is refused with 400. If the recipient already has a contact that is in no list, the membership you remove is kept only as history: it no longer appears in contacts, and its unsubscribes, consent records and campaign history are preserved.

Update a subscriber
curl -X PUT https://api.mail.land/v1/marketing/subscribers/exz31qoqlf01sj \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Jane",
    "lastName": "Smith",
    "company": "TechCorp",
    "mailingListId": "ddv6ou6kz4wbto",
    "customFields": { "plan": "pro" },
    "status": "Active"
  }'

Unsubscribe a Subscriber

POST https://api.mail.land/v1/marketing/subscribers/{id}/unsubscribe

Records an opt-out scoped to the subscriber's list, or to all marketing sends if the subscriber has no list, and sets the subscriber status to Unsubscribed with an unsubscribedAt timestamp. Transactional mail is not affected by this call.

Unsubscribe a subscriber
curl -X POST https://api.mail.land/v1/marketing/subscribers/exz31qoqlf01sj/unsubscribe \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Delete a Subscriber

DELETE https://api.mail.land/v1/marketing/subscribers/{id}

Permanently removes the subscriber and their engagement data. Opt-outs and suppressions are kept, so re-adding the same address does not re-subscribe it.

Delete a subscriber
curl -X DELETE https://api.mail.land/v1/marketing/subscribers/exz31qoqlf01sj \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Import Subscribers

POST https://api.mail.land/v1/marketing/subscribers/import

Import subscribers from CSV content:

Import request
{
  "mailingListId": "ddv6ou6kz4wbto",
  "csvContent": "email,firstName,lastName\n[email protected],John,Doe\n[email protected],Jane,Smith",
  "source": "csv_import",
  "updateExisting": true
}
FieldTypeRequiredDescription
mailingListIdstringNoTarget mailing list
csvContentstringYesCSV data: see the format callout below, the header row is not used as a header
sourcestringNoImport source label
updateExistingboolNoUpdate existing subscribers (default: false)

The first row is treated as a header and skipped, regardless of its contents: it is never read to map columns 1–3 to fields. Columns are positional: column 1 is always email, column 2 is always firstName, column 3 is always lastName; any other column is ignored except one headed birthday (case-insensitive, any position after column 1), which is read by its header name, not its position. Its value must be YYYY-MM-DD; an invalid value is reported per row in errors and that contact is still imported, just without a birthday. A file with no birthday header behaves exactly as before. Values are split on plain commas, so a value containing a comma inside quotes is not supported and will be split incorrectly. The response also adds importedNewMembershipForExistingRecipient, updatedMembership, alreadyMember, suppressed and skippedDuplicateInFile counters beyond what is shown below.

Import subscribers
curl -X POST https://api.mail.land/v1/marketing/subscribers/import \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "mailingListId": "ddv6ou6kz4wbto",
    "csvContent": "email,firstName,lastName\[email protected],John,Doe\[email protected],Jane,Smith",
    "source": "csv_import",
    "updateExisting": true
  }'
Import response
{
  "imported": 50,
  "updated": 10,
  "skipped": 5,
  "errors": [
    "Invalid email: not-an-email",
    "Could not resolve a recipient identity for: [email protected]"
  ]
}

Imported subscribers do trigger the SubscriberJoins automation, for every membership the import actually commits: the same way a single POST /v1/marketing/subscribers create does. A contact already on the target list, an in-file repeat, or a row lost to a (TenantId, Email) conflict is not enrolled. If the target list has an active SubscriberJoins automation (or the tenant has one that is not list-bound), importing a file of new contacts now sends that automation to every one of them, size the import accordingly. Suppression is still enforced: enrolling never bypasses a suppressed recipient.

Export Subscribers

GET https://api.mail.land/v1/marketing/subscribers/export
ParameterTypeDescription
mailingListIdstringFilter by mailing list
statusintFilter by status

Returns a CSV file download with filename subscribers_yyyyMMdd_HHmmss.csv.

Export subscribers
curl "https://api.mail.land/v1/marketing/subscribers/export?mailingListId=ddv6ou6kz4wbto" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -o subscribers_export.csv

Subscriber Stats

GET https://api.mail.land/v1/marketing/subscribers/stats
ParameterTypeDescription
mailingListIdstringFilter by mailing list
Get subscriber stats
curl "https://api.mail.land/v1/marketing/subscribers/stats?mailingListId=ddv6ou6kz4wbto" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
Subscriber stats response
{
  "total": 1500,
  "active": 1200,
  "unsubscribed": 200,
  "bounced": 80,
  "complained": 10,
  "pending": 10
}

Subscriber Count

GET https://api.mail.land/v1/marketing/subscribers/count
ParameterTypeDescription
mailingListIdstringFilter by mailing list
Get subscriber count
curl "https://api.mail.land/v1/marketing/subscribers/count?mailingListId=ddv6ou6kz4wbto" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
Count response
{
  "count": 1500,
  "mailingListId": "ddv6ou6kz4wbto"
}

Subscriber Growth

GET https://api.mail.land/v1/marketing/subscribers/growth
ParameterTypeDefaultDescription
daysint30Lookback period (max 90)
mailingListIdstring-Filter by mailing list
Get subscriber growth
curl "https://api.mail.land/v1/marketing/subscribers/growth?days=30&mailingListId=ddv6ou6kz4wbto" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
Growth response
{
  "dataPoints": [
    {
      "date": "2026-02-17",
      "newSubscribers": 50,
      "unsubscribes": 5,
      "cumulativeTotal": 1500
    }
  ],
  "totalNew": 500,
  "totalUnsubscribes": 50,
  "netGrowth": 450,
  "currentTotal": 1500
}

Error Responses

StatusCodeDescription
400INVALID_EMAIL_FORMATInvalid email format
400INVALID_REQUESTSubscriber email already exists
403FORBIDDENInsufficient permissions
404RESOURCE_NOT_FOUNDSubscriber not found

Next Steps

  • Lists: organize subscribers into mailing lists
  • Segments: create dynamic segments based on subscriber data
  • Campaigns: send campaigns to your subscribers

On this page