Skip to content
Cowliss is in beta: features and pricing may change.

Lists

A list is a marketing.<name> consent purpose of one app: its subscribers are the app’s profiles holding that purpose granted, and there is no second list beside the purpose. A list is declared in cow.json or created here; a push adds and relabels purposes and never deletes one, so a list made in the dashboard survives every push. Subscribers join through your app’s identify with a consent grant, a CSV import, or a hosted signup form that confirms by email. All routes require session-token authentication, and creating a list requires org:admin.

Create a list

Creates the marketing.<name> purpose on the app, written by the API. A later push declaring the same key with another label asks before taking it over; consent.purposes.delete removes it once nothing sends to it and nobody is subscribed. Refused when the app already has the key.

Auth: Session token.

Request body

{
"data": {
"name": "string",
"label": "string",
"description": "string"
}
}
Field Type Required Constraints
data object yes
data.name string yes length 0–60
data.label string yes length 1–120
data.description string no length 0–500

Response 201

{
"data": {
"key": "string",
"name": "string",
"label": "string",
"description": "string",
"source": "push",
"subscribers": 42,
"change": 42,
"subscribedLast7d": 42,
"lastSentAt": "string",
"signupToken": "string",
"signupUrl": "string"
},
"meta": {
"requestId": "string",
"nextCursor": "string",
"hasMore": true,
"warnings": [
{
"code": "string",
"message": "string",
"suggestion": "string"
}
]
}
}
Field Type Required Constraints
data object yes
data.key string yes
data.name string yes
data.label string yes
data.description string | null yes
data.source “push” | “api” yes
data.subscribers integer yes
data.change integer yes
data.subscribedLast7d integer yes
data.lastSentAt string | null yes
data.signupToken string yes
data.signupUrl string yes
meta object no
meta.requestId string no
meta.nextCursor string | null no
meta.hasMore boolean no
meta.warnings object[] no
meta.warnings[] object no
meta.warnings[].code string yes
meta.warnings[].message string yes
meta.warnings[].suggestion string no

Errors follow the shared envelope and code table.

List an app’s lists

Auth: Session token.

Query parameters

{
"cursor": "string",
"limit": 50
}
Field Type Required Constraints
cursor string no
limit integer no default 50; 1 ≤ x ≤ 100

Response 200

{
"data": [
{
"key": "string",
"name": "string",
"label": "string",
"description": "string",
"source": "push",
"subscribers": 42,
"change": 42,
"subscribedLast7d": 42,
"lastSentAt": "string",
"signupToken": "string",
"signupUrl": "string"
}
],
"meta": {
"requestId": "string",
"nextCursor": "string",
"hasMore": true,
"warnings": [
{
"code": "string",
"message": "string",
"suggestion": "string"
}
]
}
}
Field Type Required Constraints
data object[] yes
data[] object yes
data[].key string yes
data[].name string yes
data[].label string yes
data[].description string | null yes
data[].source “push” | “api” yes
data[].subscribers integer yes
data[].change integer yes
data[].subscribedLast7d integer yes
data[].lastSentAt string | null yes
data[].signupToken string yes
data[].signupUrl string yes
meta object no
meta.requestId string no
meta.nextCursor string | null no
meta.hasMore boolean no
meta.warnings object[] no
meta.warnings[] object no
meta.warnings[].code string yes
meta.warnings[].message string yes
meta.warnings[].suggestion string no

Errors follow the shared envelope and code table.

List a list’s subscribers

The app’s profiles holding the purpose granted, newest profile first, with when and how each joined.

Auth: Session token.

Query parameters

{
"cursor": "string",
"limit": 50
}
Field Type Required Constraints
cursor string no
limit integer no default 50; 1 ≤ x ≤ 100

Response 200

{
"data": [
{
"profileId": "string",
"email": "[email protected]",
"joinedAt": "string",
"source": "app",
"lastOpenedAt": "string"
}
],
"meta": {
"requestId": "string",
"nextCursor": "string",
"hasMore": true,
"warnings": [
{
"code": "string",
"message": "string",
"suggestion": "string"
}
]
}
}
Field Type Required Constraints
data object[] yes
data[] object yes
data[].profileId string yes
data[].email string | null yes
data[].joinedAt string | null yes
data[].source “app” | “imported” | “form” yes
data[].lastOpenedAt string | null yes
meta object no
meta.requestId string no
meta.nextCursor string | null no
meta.hasMore boolean no
meta.warnings object[] no
meta.warnings[] object no
meta.warnings[].code string yes
meta.warnings[].message string yes
meta.warnings[].suggestion string no

Errors follow the shared envelope and code table.

Import a CSV into a list

A multipart/form-data upload: the CSV as file and attested set to true, the importer’s confirmation that these people agreed to receive this list. Matches or creates a user by the email identifier for every row and grants the list with consent reason imported. Quiet: no automation starts and no email is sent. A suppressed address is still imported and counted. Re-importing a file changes nothing. At most 5 MB and 10000 rows.

Auth: Session token.

Response 200

{
"data": {
"added": 42,
"alreadySubscribed": 42,
"unsubscribed": 42,
"suppressed": 42,
"invalid": [
{
"line": 42,
"reason": "string"
}
]
},
"meta": {
"requestId": "string",
"nextCursor": "string",
"hasMore": true,
"warnings": [
{
"code": "string",
"message": "string",
"suggestion": "string"
}
]
}
}
Field Type Required Constraints
data object yes
data.added integer yes
data.alreadySubscribed integer yes
data.unsubscribed integer yes
data.suppressed integer yes
data.invalid object[] yes
data.invalid[] object yes
data.invalid[].line integer yes
data.invalid[].reason string yes
meta object no
meta.requestId string no
meta.nextCursor string | null no
meta.hasMore boolean no
meta.warnings object[] no
meta.warnings[] object no
meta.warnings[].code string yes
meta.warnings[].message string yes
meta.warnings[].suggestion string no

Errors follow the shared envelope and code table.