---
title: "Create or update subscriber"
method: POST
path: "/subscribers"
tags: ["Subscribers"]
---

# Create or update subscriber

`POST /subscribers`

Creates a new subscriber or handles existing ones based on the `duplicateStrategy` parameter.

**Duplicate Strategies:**
- `skip` (default): Don't update existing subscribers
- `merge`: Only fill in missing fields, never overwrite existing values
- `overwrite`: Replace all fields (but never reactivate unsubscribed users)

## Request body

- object
  - `email` string, email — Required when creating a new subscriber. Optional when externalId identifies an existing subscriber.
  - `externalId` string — Customer-owned app/customer/user ID. Unique per company when provided.
  - `firstName` string
  - `lastName` string
  - `phone` string, nullable — Phone number in E.164 format or US national format. Stored normalized to E.164. Invalid values fail with a 400 validation error. Does not affect SMS consent.
  - `smsConsent` boolean — SMS marketing consent. true sets smsStatus to subscribed with consent source api, false sets unsubscribed, omitted leaves SMS status unchanged. Never inferred from phone presence.
  - `status` 'active' | 'unsubscribed' | 'bounced' — Initial subscriber status.
  - `optInMode` 'default' | 'confirmed' | 'double_opt_in' — Consent handling for this request: - `default`: obey the company double opt-in setting for new active subscribers; existing unsubscribed contacts are not sent confirmation email - `confirmed`: create or keep active immediately when you have verified consent - `double_opt_in`: send a confirmation email and keep the contact unsubscribed until they confirm
  - `tags` string[]
  - `lists` string[] — List IDs to add subscriber to. If not provided, subscriber follows the workspace default lists setting. If empty array, subscriber is added to NO lists.
  - `customAttributes` object
  - `enrollInSequences` boolean — Whether to enroll the subscriber in matching sequences. Defaults to true for API calls.
  - `duplicateStrategy` 'skip' | 'merge' | 'overwrite' — How to handle existing subscribers: - `skip`: Don't update existing subscribers (default) - `merge`: Only fill in missing fields, never overwrite existing values - `overwrite`: Replace all fields (but never reactivate unsubscribed users)

## Response `200`

Subscriber created or handled based on duplicate strategy

- object
  - `success` boolean
  - `subscriber` object
    - `id` string
    - `email` string, email
    - `externalId` string, nullable — Customer-owned app/customer/user ID for this subscriber
    - `firstName` string, nullable
    - `lastName` string, nullable
    - `phone` string, nullable — Phone number in E.164 format
    - `smsStatus` 'not_subscribed' | 'pending' | 'subscribed' | 'unsubscribed' — SMS marketing consent status, independent of the email status
    - `status` 'active' | 'unsubscribed' | 'bounced'
    - `emailProvider` string, nullable
    - `tags` string[]
    - `customAttributes` object
    - `createdAt` string, date-time
    - `updatedAt` string, date-time
    - `created` boolean — Whether the subscriber was newly created
    - `updated` boolean — Whether the subscriber was updated (merge/overwrite strategies)
    - `skipped` boolean — Whether the subscriber was skipped (skip strategy on existing)
  - `optIn` object — Present when the subscriber is awaiting double opt-in confirmation.
    - `required` boolean
    - `emailQueued` boolean

## Other responses

- `400` — Validation error, for example an invalid phone number
- `401` — Unauthorized
- `500` — Internal server error

## Changes

- **2026-07-10** `1b0d5fa83cd8` — 5 info
  - added the new optional request property `phone`
  - added the new optional request property `smsConsent`
  - added the non-success response with the status `400`
  - added the optional property `subscriber/allOf[#/components/schemas/Subscriber]/phone` to the response with the `200` status
  - …1 more
- **2026-05-19** `1a51a400be50` — 3 info
  - added the new optional request property `optInMode`
  - added the new `bounced` enum value to the request property `status`
  - added the optional property `optIn` to the response with the `200` status
- **2026-05-05** `5449b620579f` — 3 info
  - added the new optional request property `externalId`
  - the request property `email` became optional
  - added the optional property `subscriber/allOf[#/components/schemas/Subscriber]/externalId` to the response with the `200` status
- **2026-04-11** `b6d39ca47951` — 1 warning, 1 info
  - added the new `bounced` enum value to the `subscriber/allOf[#/components/schemas/Subscriber]/status` response property for the response status `200`
  - added the optional property `subscriber/allOf[#/components/schemas/Subscriber]/emailProvider` to the response with the `200` status

[Change history](https://skmtc.dev/sequenzy/apis/sequenzy-api/changes/subscribers/post.md)

---

[API](https://skmtc.dev/sequenzy/apis/sequenzy-api.md) · [All operations](https://skmtc.dev/sequenzy/apis/sequenzy-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/sequenzy/sequenzy-api/revisions/ad2202945c5b/schema)
