---
title: "Add or update a subscriber"
method: POST
path: "/api/v1/subscribers"
tags: ["Subscribers"]
---

# Add or update a subscriber

`POST /api/v1/subscribers`

## Request

- Base URL: `https://mailcheer.com`
- URL: `https://mailcheer.com/api/v1/subscribers`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-Api-Key`

Permission required: `subscribers:write`.

By default the person enters `pending` and receives a confirmation email. `double_opt_in: false` subscribes them straight away — to be used only if consent was collected elsewhere, and the caller then bears responsibility for it.

An address on the suppression list, unsubscribed, or returned as a hard bounce is refused.

## Request body

- SubscriberRequest
  - `email` string, email, required: **Required.** Lower-cased on save.
  - `firstName` string
  - `lastName` string
  - `prenom` string: Alias of `firstName`. Fill in only one of the two.
  - `nom` string: Alias of `lastName`. Fill in only one of the two.
  - `tags` string[]: Workspace tags. The ones that do not exist yet are created.
  - `fields` object: Custom fields, by their technical key. Unknown keys create nothing: they come back in `ignored_fields`.
  - `double_opt_in` boolean: `false` subscribes straight away, with no confirmation email. The caller then bears responsibility for the consent.

## Response `200`

The subscriber already existed and has been updated.

- SubscriberResult
  - `subscriber` Subscriber
    - `id` string
    - `object` 'subscriber'
    - `email` string
    - `first_name` string, nullable
    - `last_name` string, nullable
    - `status` 'pending' | 'subscribed' | 'unsubscribed' | 'bounced' | 'complained'
    - `source` string, nullable
    - `tags` string[]
    - `fields` object
    - `confirmed_at` string, date-time, nullable
    - `unsubscribed_at` string, date-time, nullable
    - `created_at` string, date-time
    - `updated_at` string, date-time: The last change to the record: status, name, fields, tags. Keep the largest value you receive and pass it as `updated_since` next time, minus one minute.
  - `updated` boolean: `true` if the subscriber already existed.
  - `confirmation_sent` boolean: `true` if a confirmation email went out with THIS call. Exactly when `confirmation_not_sent_reason` is `null`.
  - `confirmation_not_sent_reason` 'not_needed' | 'recently_sent' | 'daily_limit' | 'send_failed' | 'null', nullable: Why no confirmation went out with this call: `not_needed` (already confirmed, or `double_opt_in: false`), `recently_sent` (one went out less than 2 minutes ago), `daily_limit` (3 per address per 24 hours, all channels combined), `send_failed` (the provider refused it; call again later). `null` when one went out.
  - `confirmation_retry_at` string, date-time, nullable: When a new call can send one again (ISO 8601): set only for `recently_sent` and `daily_limit`, otherwise `null`.
  - `ignored_fields` string[]: Keys of `fields` unknown to this workspace, written nowhere.

## Other responses

- `201`: Created.
- `401`: Key missing, unknown or revoked.
- `403`: The key does not have the requested permission, or the workspace cannot send: `sending_blocked` (suspended, blocked or banned — nothing goes out, through any channel), `commitment_required` (the anti-spam commitment is not accepted), `reputation_blocked` (too many bounces or complaints).
- `409`: Incompatible state, idempotency key reused with a different body, or an identical request still in progress.
- `422`: Malformed field, unverified domain, or recipient on the suppression list.

---

[API](https://skmtc.dev/mailcheer/apis/mailcheer-api.md) · [All operations](https://skmtc.dev/mailcheer/apis/mailcheer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mailcheer/apis/mailcheer-api/revisions/ba64b7ef2170?raw)
