Subscriber Lists ​

Full reference for the Subscriber Lists resource in the bluefox.email API. See the API overview for authentication, the response envelope, and pagination.

List subscriber lists ​

GET /v1/projectId/{projectId}/subscriber-lists

Parameters ​

NameInTypeRequiredDescription
projectIdpathstringyesThe project _id, found in the app under Project Settings.
limitqueryintegerMax items per page. Capped at 30.
skipqueryintegerNumber of items to skip, for pagination.
sortquerystringField to sort by.
orderquerystring
filter[<field>]querystringExact-match filter on a top-level field, e.g. filter[name]=Welcome. Repeatable for multiple fields.

Responses ​

StatusDescription
200OK
403Missing or invalid API key

Response body ​

FieldTypeRequiredDescription
itemsarray
countintegerTotal matching items across all pages, not just this page.
nextstringFull URL (path + query string) of the next page, or null if this is the last page.

Create a subscriber list ​

POST /v1/projectId/{projectId}/subscriber-lists

doubleOptIn.emailId (if given) must be a transactional email belonging to this same project - a valid ID from a different project 404s. When doubleOptIn.active is true, that email's content must also include {{verifyLink}}.

Parameters ​

NameInTypeRequiredDescription
projectIdpathstringyesThe project _id, found in the app under Project Settings.

Request body ​

FieldTypeRequiredDescription
namestringyes
descriptionstringyes
privateboolean
doubleOptInobjectemailId is required when active is true. When active is true, the referenced transactional email's content must include {{verifyLink}} - the API rejects the request with a 400 otherwise, since that's the only way a contact can confirm their subscription. Replaces the whole object when given - to change just one field, GET the current value first and send all five back together. Do not include the GET response's doubleOptIn._id - it is Mongoose's own subdocument id, not a real field, and PATCH rejects it as unexpected.
doubleOptIn.activeboolean
doubleOptIn.redirectLinkstringWhere a contact lands after clicking the confirmation link. A token query parameter (see the SubscriptionConfirmedToken security scheme) is appended so the page can personalize.
doubleOptIn.emailIdstringA transactional email _id used to send the confirmation email - NOT a template ID, even though a template may look interchangeable. Its content must include {{verifyLink}} if active is true. If the user wants double opt-in but hasn't said which email to use, call GET /v1/projectId/{projectId}/transactional-emails first and ask them to choose from the real names.
doubleOptIn.confirmationTitlestring
doubleOptIn.confirmationMessagestring
signupFormobjectReplaces the whole object when given, not a per-field merge - GET the current value first and send back every field you want to keep alongside your changes. Do not include the GET response's signupForm._id - it is Mongoose's own subdocument id, not a real field, and PATCH rejects it as unexpected.

Responses ​

StatusDescription
201Created
400Invalid request body or query - every violation found (missing/invalid/unexpected fields) is reported in one response, semicolon-separated, not just the first one hit.
403Missing or invalid API key

Response body ​

FieldTypeRequiredDescription
_idstring
namestring
descriptionstring
privateboolean
doubleOptInobject
doubleOptIn.activeboolean
doubleOptIn.redirectLinkstring
doubleOptIn.emailIdstringA transactional email _id used to send the confirmation email.
doubleOptIn.confirmationTitlestring
doubleOptIn.confirmationMessagestring
signupFormobject
signupForm.formLayoutstring
signupForm.showCaptchaboolean
signupForm.emailPlaceholderstring
signupForm.captchaPlaceholderstring
signupForm.formFontStylestring
signupForm.formFontColorstring
signupForm.formFontSizestring
signupForm.btnLabelstring
signupForm.btnFontstring
signupForm.btnFontColorstring
signupForm.btnColorstring
signupForm.btnFontSizestring
signupForm.successMessagestring
signupForm.successFontstring
signupForm.successFontColorstring
signupForm.successFontSizestring
signupForm.propertiesStyleobject
createdAtstring
updatedAtstring

Get a single subscriber list ​

GET /v1/projectId/{projectId}/subscriber-lists/{id}

Parameters ​

NameInTypeRequiredDescription
projectIdpathstringyesThe project _id, found in the app under Project Settings.
idpathstringyes

Responses ​

StatusDescription
200OK
403Missing or invalid API key
404Subscriber Lists not found

Response body ​

FieldTypeRequiredDescription
_idstring
namestring
descriptionstring
privateboolean
doubleOptInobject
doubleOptIn.activeboolean
doubleOptIn.redirectLinkstring
doubleOptIn.emailIdstringA transactional email _id used to send the confirmation email.
doubleOptIn.confirmationTitlestring
doubleOptIn.confirmationMessagestring
signupFormobject
signupForm.formLayoutstring
signupForm.showCaptchaboolean
signupForm.emailPlaceholderstring
signupForm.captchaPlaceholderstring
signupForm.formFontStylestring
signupForm.formFontColorstring
signupForm.formFontSizestring
signupForm.btnLabelstring
signupForm.btnFontstring
signupForm.btnFontColorstring
signupForm.btnColorstring
signupForm.btnFontSizestring
signupForm.successMessagestring
signupForm.successFontstring
signupForm.successFontColorstring
signupForm.successFontSizestring
signupForm.propertiesStyleobject
createdAtstring
updatedAtstring

Partially update a subscriber list ​

PATCH /v1/projectId/{projectId}/subscriber-lists/{id}

Merges the given fields into the existing document - omitted fields are left unchanged. Nothing is required (unlike create) since every field is optional on a partial update.

doubleOptIn.emailId and the {{verifyLink}} content requirement are re-validated the same way as create.

Parameters ​

NameInTypeRequiredDescription
projectIdpathstringyesThe project _id, found in the app under Project Settings.
idpathstringyes

Request body ​

FieldTypeRequiredDescription
namestring
descriptionstring
privateboolean
doubleOptInobjectemailId is required when active is true. When active is true, the referenced transactional email's content must include {{verifyLink}} - the API rejects the request with a 400 otherwise, since that's the only way a contact can confirm their subscription. Replaces the whole object when given - to change just one field, GET the current value first and send all five back together. Do not include the GET response's doubleOptIn._id - it is Mongoose's own subdocument id, not a real field, and PATCH rejects it as unexpected.
doubleOptIn.activeboolean
doubleOptIn.redirectLinkstringWhere a contact lands after clicking the confirmation link. A token query parameter (see the SubscriptionConfirmedToken security scheme) is appended so the page can personalize.
doubleOptIn.emailIdstringA transactional email _id used to send the confirmation email - NOT a template ID, even though a template may look interchangeable. Its content must include {{verifyLink}} if active is true. If the user wants double opt-in but hasn't said which email to use, call GET /v1/projectId/{projectId}/transactional-emails first and ask them to choose from the real names.
doubleOptIn.confirmationTitlestring
doubleOptIn.confirmationMessagestring
signupFormobjectReplaces the whole object when given, not a per-field merge - GET the current value first and send back every field you want to keep alongside your changes. Do not include the GET response's signupForm._id - it is Mongoose's own subdocument id, not a real field, and PATCH rejects it as unexpected.

Responses ​

StatusDescription
200OK
400Invalid request body or query - every violation found (missing/invalid/unexpected fields) is reported in one response, semicolon-separated, not just the first one hit.
403Missing or invalid API key
404Subscriber Lists not found

Response body ​

FieldTypeRequiredDescription
_idstring
namestring
descriptionstring
privateboolean
doubleOptInobject
doubleOptIn.activeboolean
doubleOptIn.redirectLinkstring
doubleOptIn.emailIdstringA transactional email _id used to send the confirmation email.
doubleOptIn.confirmationTitlestring
doubleOptIn.confirmationMessagestring
signupFormobject
signupForm.formLayoutstring
signupForm.showCaptchaboolean
signupForm.emailPlaceholderstring
signupForm.captchaPlaceholderstring
signupForm.formFontStylestring
signupForm.formFontColorstring
signupForm.formFontSizestring
signupForm.btnLabelstring
signupForm.btnFontstring
signupForm.btnFontColorstring
signupForm.btnColorstring
signupForm.btnFontSizestring
signupForm.successMessagestring
signupForm.successFontstring
signupForm.successFontColorstring
signupForm.successFontSizestring
signupForm.propertiesStyleobject
createdAtstring
updatedAtstring

Delete a subscriber list ​

DELETE /v1/projectId/{projectId}/subscriber-lists/{id}

Parameters ​

NameInTypeRequiredDescription
projectIdpathstringyesThe project _id, found in the app under Project Settings.
idpathstringyes

Responses ​

StatusDescription
200Deleted
403Missing or invalid API key
404Subscriber Lists not found
405The operation is blocked by a business rule (e.g. the resource is still in use)

Response body ​

FieldTypeRequiredDescription
_idstring
namestring
descriptionstring
privateboolean
doubleOptInobject
doubleOptIn.activeboolean
doubleOptIn.redirectLinkstring
doubleOptIn.emailIdstringA transactional email _id used to send the confirmation email.
doubleOptIn.confirmationTitlestring
doubleOptIn.confirmationMessagestring
signupFormobject
signupForm.formLayoutstring
signupForm.showCaptchaboolean
signupForm.emailPlaceholderstring
signupForm.captchaPlaceholderstring
signupForm.formFontStylestring
signupForm.formFontColorstring
signupForm.formFontSizestring
signupForm.btnLabelstring
signupForm.btnFontstring
signupForm.btnFontColorstring
signupForm.btnColorstring
signupForm.btnFontSizestring
signupForm.successMessagestring
signupForm.successFontstring
signupForm.successFontColorstring
signupForm.successFontSizestring
signupForm.propertiesStyleobject
createdAtstring
updatedAtstring

Get stats for a single subscriber list ​

GET /v1/projectId/{projectId}/subscriber-lists/{id}/stats

Aggregate counts only, never a per-recipient list - response size and query cost are bounded regardless of how much activity actually happened, since every count is a single indexed query scoped to this one resource. For the actual list of recipients (who received/opened/clicked/bounced/unsubscribed), see GET /v1/projectId/{projectId}/subscriber-lists/{id}/recipients.

Parameters ​

NameInTypeRequiredDescription
projectIdpathstringyesThe project _id, found in the app under Project Settings.
idpathstringyesThe subscriber list _id.
fromquerystringOnly count events at or after this date/time.
toquerystringOnly count events at or before this date/time. If both from and to are given, the range between them cannot exceed 366 days.

Responses ​

StatusDescription
200OK
400Invalid request body or query - every violation found (missing/invalid/unexpected fields) is reported in one response, semicolon-separated, not just the first one hit.
403Missing or invalid API key
404Subscriber Lists not found

Response body ​

FieldTypeRequiredDescription
activeinteger
pausedinteger
unsubscribedinteger
unverifiedinteger
subscribedintegersubscribe events in range.
unsubscribedEventsinteger
pausedEventsinteger
unpausedEventsinteger
bounceinteger
complaintinteger

List subscribers on a list, with their contact data ​

GET /v1/projectId/{projectId}/subscriber-lists/{id}/subscribers

Project-scoped equivalent of GET /v1/subscriber-lists/{id} (which is a legacy flat URL shape kept for hosted signup
forms and one-click-unsubscribe links). Each item merges the subscriber's per-list status with their contact data -
custom field values come back nested under a "customFields" object here, not flattened onto top-level keys like
GET /v1/projectId/{projectId}/contacts.

Parameters ​

NameInTypeRequiredDescription
projectIdpathstringyesThe project _id, found in the app under Project Settings.
idpathstringyesThe subscriber list _id.
limitqueryintegerMax items per page. Capped at 30.
skipqueryintegerNumber of items to skip, for pagination.
sortquerystringField to sort by.
orderquerystring
filter[<field>]querystringExact-match filter on a top-level field, e.g. filter[name]=Welcome. Repeatable for multiple fields.

Responses ​

StatusDescription
200OK
403Missing or invalid API key
404Subscriber list not found

Response body ​

FieldTypeRequiredDescription
itemsarray of object
items[].emailstring
items[].statusstring (unverified | active | unsubscribed | paused)
items[].customFieldsobjectCustom contact field values, keyed by field name.
countintegerTotal matching items across all pages, not just this page.
nextstringFull URL (path + query string) of the next page, or null if this is the last page.

Subscribe a contact to a list ​

POST /v1/projectId/{projectId}/subscriber-lists/{id}/subscribers

Project-scoped equivalent of POST /v1/subscriber-lists/{id} (which is a legacy flat URL shape kept for hosted
signup forms - it also accepts a whitelisted Origin header and CAPTCHA, neither of which apply here). Creates the
contact if it doesn't exist yet. Accepts a SubscriptionToken in place of an api key - see that security scheme's
description for how a custom subscription-preferences page uses this to let a subscriber re-join a list they had left.

Double opt-in: when it is on for the list and the request uses an API key (or a whitelisted Origin), a new contact - or one who is unverified or unsubscribed on the list - is not activated. They get the verification email and stay as they are (new contacts become "unverified") until they click its link, and the response is { success: true, info } instead of the subscriber. Sending status "active" does not skip this. A paused subscriber is reactivated directly, without an email. With a SubscriptionToken the subscriber has already proven they own the address, so they are activated directly.

Parameters ​

NameInTypeRequiredDescription
projectIdpathstringyesThe project _id, found in the app under Project Settings.
idpathstringyesThe subscriber list _id.

Request body ​

FieldTypeRequiredDescription
emailstringyes
namestring
statusstring (unverified | active)
tagsarray of string

Responses ​

StatusDescription
201Created
400Invalid request body or query - every violation found (missing/invalid/unexpected fields) is reported in one response, semicolon-separated, not just the first one hit.
403Missing or invalid API key
404Subscriber list not found
405The operation is blocked by a business rule (e.g. the resource is still in use)

Response body ​

FieldTypeRequiredDescription
emailstring
statusstring (unverified | active | unsubscribed | paused)
customFieldsobjectCustom contact field values, keyed by field name.

Import contacts into a list ​

POST /v1/projectId/{projectId}/subscriber-lists/{id}/import

Bulk-adds up to 1000 contacts to a list in one call, like the app's CSV import. Double opt-in is skipped: contacts
not yet on the list are added as "active" and no verification email is sent, so only import contacts who already
agreed to receive your emails. Contacts already on the list (with any status, including "unsubscribed") are left
untouched and counted in alreadyOnList. Each row creates the contact if it doesn't exist yet, or updates its fields
(name, tags, custom fields) if it does. A row that fails (invalid, suppressed, or disposable email) is reported in
errors and does not stop the rest. API key only - a SubscriptionToken is not accepted.

Parameters ​

NameInTypeRequiredDescription
projectIdpathstringyesThe project _id, found in the app under Project Settings.
idpathstringyesThe subscriber list _id.

Request body ​

FieldTypeRequiredDescription
contactsarray of objectyes
contacts[].emailstringyes
contacts[].namestring
contacts[].tagsarray of string

Responses ​

StatusDescription
200OK
400Invalid request body or query - every violation found (missing/invalid/unexpected fields) is reported in one response, semicolon-separated, not just the first one hit.
403Missing or invalid API key
404Subscriber list not found

Response body ​

FieldTypeRequiredDescription
importedintegerContacts added to the list as active.
alreadyOnListintegerContacts already on the list, left untouched.
errorsarray of object
errors[].emailstring
errors[].messagestring

Get a single subscriber's status and contact data on a list ​

GET /v1/projectId/{projectId}/subscriber-lists/{id}/subscribers/{email}

Project-scoped equivalent of GET /v1/subscriber-lists/{id}/{email} (which is a legacy flat URL shape kept for hosted
signup forms and one-click-unsubscribe links). Custom field values come back nested under a "customFields" object
here, not flattened onto top-level keys like GET /v1/projectId/{projectId}/contacts/{email}.

Parameters ​

NameInTypeRequiredDescription
projectIdpathstringyesThe project _id, found in the app under Project Settings.
idpathstringyesThe subscriber list _id.
emailpathstringyes

Responses ​

StatusDescription
200OK
403Missing or invalid API key
404Subscriber not found

Response body ​

FieldTypeRequiredDescription
emailstring
statusstring (unverified | active | unsubscribed | paused)
customFieldsobjectCustom contact field values, keyed by field name.

Update a subscriber (status, contact fields, pause/resubscribe) ​

PATCH /v1/projectId/{projectId}/subscriber-lists/{id}/subscribers/{email}

Project-scoped equivalent of PATCH /v1/subscriber-lists/{id}/{email} (which is a legacy flat URL shape kept for
hosted signup forms). Setting status to "paused" requires pausedUntil (a future date). Accepts a SubscriptionToken
in place of an api key - see that security scheme's description for how a custom subscription-preferences page
uses this to let a subscriber pause or unsubscribe.

Double opt-in: when it is on for the list and the request uses an API key, setting status to "active" on an unverified or unsubscribed subscriber does not activate them - they keep their status, get the verification email, and the response includes verificationEmailSent: true. They become active only by clicking its link. A paused subscriber is reactivated directly. With a SubscriptionToken the change applies directly.

Parameters ​

NameInTypeRequiredDescription
projectIdpathstringyesThe project _id, found in the app under Project Settings.
idpathstringyesThe subscriber list _id.
emailpathstringyes

Request body ​

FieldTypeRequiredDescription
emailstring
statusstring (active | paused | unsubscribed)
pausedUntilstring

Responses ​

StatusDescription
200OK
400Invalid request body or query - every violation found (missing/invalid/unexpected fields) is reported in one response, semicolon-separated, not just the first one hit.
403Missing or invalid API key
404Subscriber not found
405The operation is blocked by a business rule (e.g. the resource is still in use)

Response body ​

FieldTypeRequiredDescription
emailstring
statusstring (unverified | active | unsubscribed | paused)
customFieldsobjectCustom contact field values, keyed by field name.

List every subscriber list an email can manage, with its current status on each ​

GET /v1/projectId/{projectId}/subscriber-lists/public/{email}

The endpoint behind a subscriber-facing subscription-preferences page: every public list in the project, plus -
if authenticating with a SubscriptionToken minted for one - the single private list it names, each carrying this
email's current status (active/paused/unsubscribed) where a subscription exists. Building a custom preferences
page for this project? See the SubscriptionToken security scheme below for the full flow: take the token query
parameter off the unsubscribe/pause-subscription link, call this endpoint with it to render the page, then
PATCH/POST the individual subscriber-lists endpoints (same token) as the subscriber makes changes.
Also accepts the read-only SubscriptionConfirmedToken from a double opt-in redirect (it includes the private lists it names).

Parameters ​

NameInTypeRequiredDescription
projectIdpathstringyesThe project _id, found in the app under Project Settings.
emailpathstringyes

Responses ​

StatusDescription
200OK
403Missing or invalid API key
404Contact, or there are no available subscriber lists not found

Response body ​

FieldTypeRequiredDescription
itemsarray of object
items[]._idstring
items[].namestring
items[].descriptionstring
items[].privateboolean
items[].subscriberobjectPresent only when this email has a subscription (of any status) on this list.
items[].subscriber.emailstring
items[].subscriber.statusstring (unverified | active | unsubscribed | paused)
items[].subscriber.pausedUntilstring
countinteger
logoUrlstring