---
title: "Subscribe a contact to a list"
method: POST
path: "/api/lists/{id}/subscribe"
tags: ["Lists"]
---

# Subscribe a contact to a list

`POST /api/lists/{id}/subscribe`

## Request

- Base URL: `https://api.sendly.now`
- URL: `https://api.sendly.now/api/lists/{id}/subscribe`
- Auth: one of:
  - HTTP bearer
  - API key in cookie `better-auth.session_token`

Add a contact to a list, creating the contact if it does not exist. When the list has `doubleOptIn` enabled the membership is created as `PENDING` and the response carries a `confirmToken` — Sendly does NOT send the confirmation email, so the caller must deliver `/api/lists/confirm-subscription?token=<confirmToken>` to the contact itself.

Accepts SENDING_ONLY (`pk_*`) keys so it can back a public subscribe form.

**Re-subscribing after an opt-out.** If the email already holds an `UNSUBSCRIBED` membership on this list, the call fails with `409 RESUBSCRIBE_CONFIRMATION_REQUIRED` unless the body sets `allowResubscribe: true`. Reversing an opt-out is a consent decision, so it is never the default — set the flag only when the contact themselves asked to be re-subscribed.

`previousStatus` reports the membership's status before the call (`null` when it did not exist); prefer it over `created` when describing what changed, since `created: false` is equally true for an unchanged membership and for a reactivated one.

## Path parameters

- `id` string, required: List id.

## Request body

- ListSubscribe: Body for POST /api/lists/{id}/subscribe.
  - `allowResubscribe` boolean: Permission to reverse an earlier opt-out. When the email already has an UNSUBSCRIBED membership on this list, the call fails with 409 RESUBSCRIBE_CONFIRMATION_REQUIRED unless this is `true`. Send `true` only when the contact is acting for themselves — a public subscribe form they submitted is affirmative consent — never for an operator-initiated add.
  - `data` object: Custom fields to upsert onto the contact as part of subscribing.
  - `email` string, email, required

## Response `200`

Contact subscribed, or an existing membership returned unchanged

- ListSubscribeResponse: Result of a list-subscribe call.
  - `data` object, required
    - `confirmToken` string: Present only when the list has doubleOptIn enabled. Sendly does not send the confirmation email — deliver /api/lists/confirm-subscription?token=<confirmToken> to the contact. Valid for 24 hours.
    - `created` boolean, required: True when the membership row did not exist before this call.
    - `membershipId` string, uuid, required
    - `previousStatus` 'PENDING' | 'CONFIRMED' | 'UNSUBSCRIBED' | 'null', nullable, required: Status the membership held before this call; null when it did not exist. Use this rather than `created` to describe the transition to the user.
    - `status` 'PENDING' | 'CONFIRMED' | 'UNSUBSCRIBED', required
  - `success` true, required

## Other responses

- `400`: Validation error
- `401`: Unauthorized — missing or invalid auth
- `403`: Forbidden — insufficient permissions or project disabled
- `404`: Resource not found
- `409`: The contact previously unsubscribed from this list and `allowResubscribe` was not set. `error.code` is `RESUBSCRIBE_CONFIRMATION_REQUIRED` and `error.details.previousStatus` is `UNSUBSCRIBED`. Retry with `allowResubscribe: true` once the contact has consented.
- `422`: Validation failed — request body or query parameters did not match the schema
- `429`: Rate limit or billing limit exceeded
- `500`: Internal server error

---

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