Workspace API

Clients

Clients endpoints of the Zanfia Workspace API, with schemas and code samples.

17 min readLast updated Oct 16, 2026

Manage contacts and product access. Adding a client creates a contact only — access is granted per product. Marketing consent is separate for email and SMS and can be set in bulk (POST /clients/email-subscription, POST /clients/sms-subscription, up to 200 clients per call). Tags are managed under /clients/tags (create, rename/recolour, delete) and assigned in bulk with POST /clients/tags/assignments; a newly added tag fires the client.TagAdded workflow trigger. All routes require a Authorization: Bearer header — see Authentication.

List clients

GET/clients

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Query Parameters

createdFrom?string

"Date added" window start, inclusive: an ISO 8601 date-time, or YYYY-MM-DD (the start of that day, UTC). Same filter as the Customers list in the dashboard; also accepted by GET /clients/export.

createdTo?string

"Date added" window end, inclusive: an ISO 8601 date-time, or YYYY-MM-DD (the whole day, UTC). Must not be before createdFrom; a malformed value answers 400.

Response Body

application/json

application/json

curl -X GET "https://example.com/clients"
{  "items": [    {      "id": "string",      "email": "string",      "firstName": "string",      "lastName": "string",      "phoneNumber": "string",      "totalOrders": 0,      "totalSpentCents": 0,      "activeProducts": 0,      "language": "string",      "discordId": "string",      "githubId": 0,      "tagIds": [        "string"      ],      "emailSubscriptionStatus": "pending",      "attribution": {        "utmSource": "string",        "utmMedium": "string",        "utmCampaign": "string",        "utmTerm": "string",        "utmContent": "string",        "fbclid": "string",        "gclid": "string",        "metaCampaignId": "string",        "metaAdId": "string",        "metaCampaignName": "string",        "metaAdName": "string",        "capturedAt": "string"      },      "customFields": {        "property1": "string",        "property2": "string"      },      "createdAt": "string"    }  ],  "nextCursor": "string"}
{  "error": "string",  "message": "string"}

Add a client (contact only)

POST/clients

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Create a CONTACT-ONLY client — a workspace customer with no product grant and no order. Granting product access is a separate action (not exposed here yet).

Response Body

application/json

application/json

curl -X POST "https://example.com/clients" \  -H "Content-Type: application/json" \  -d '{    "email": "string"  }'
{  "clientId": "string",  "created": true,  "createdUser": true,  "marketingConsent": "granted"}
{  "error": "string",  "message": "string"}

List client tags

GET/clients/tags

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Response Body

application/json

application/json

curl -X GET "https://example.com/clients/tags"
{  "count": 0,  "tags": [    {      "id": "string",      "name": "string",      "color": "string"    }  ]}
{  "error": "string",  "message": "string"}

Create a client tag

POST/clients/tags

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

name 1-50 chars (trimmed), unique per workspace case-insensitively.

Response Body

application/json

application/json

curl -X POST "https://example.com/clients/tags" \  -H "Content-Type: application/json" \  -d '{    "name": "string"  }'
{  "ok": true,  "tag": {    "id": "string",    "name": "string",    "color": "string",    "createdAt": "string"  }}
{  "error": "string",  "message": "string"}

List custom contact fields

GET/clients/custom-fields

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Response Body

application/json

application/json

curl -X GET "https://example.com/clients/custom-fields"
{  "count": 0,  "fields": [    {      "key": "string",      "name": "string",      "type": "text",      "options": [        {          "key": "string",          "label": "string",          "archived": true        }      ],      "archived": true,      "createdAt": "string"    }  ]}
{  "error": "string",  "message": "string"}

Add or remove tags on clients

POST/clients/tags/assignments

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Add / remove tag IDS on up to 200 clients; addTagIds must exist in the registry.

Response Body

application/json

application/json

curl -X POST "https://example.com/clients/tags/assignments" \  -H "Content-Type: application/json" \  -d '{    "clientIds": [      "string"    ]  }'
{  "ok": true,  "results": [    {      "clientId": "string",      "tags": [        "string"      ]    }  ],  "skippedClientIds": [    "string"  ]}
{  "error": "string",  "message": "string"}

Rename or recolour a client tag

PATCH/clients/tags/{tagId}

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

tagId*string

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Rename and/or recolour — the tag id (and every assignment) is untouched.

Response Body

application/json

application/json

curl -X PATCH "https://example.com/clients/tags/string" \  -H "Content-Type: application/json" \  -d '{}'
{  "ok": true,  "tag": {    "id": "string",    "name": "string",    "color": "string",    "createdAt": "string"  }}
{  "error": "string",  "message": "string"}

Delete a client tag

DELETE/clients/tags/{tagId}

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

tagId*string

Response Body

application/json

application/json

curl -X DELETE "https://example.com/clients/tags/string"
{  "ok": true,  "clientsUntagged": 0}
{  "error": "string",  "message": "string"}

Export clients as CSV

GET/clients/export

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Query Parameters

columns?string

Comma-separated optional column groups, any subset of: tags, stats, emailSubscription, activity, accesses, invoiceData, attribution, identifiers. Omit for identity columns only.

search?string

Case-insensitive prefix match on email / first name / last name.

products?string

has = only clients with active product access, no = only clients without any.

createdFrom?string

Only clients added on/after this ISO date (inclusive).

createdTo?string

Only clients added on/before this ISO date (inclusive).

limit?string

Row cap (default and max 20000).

Response Body

application/json

application/json

curl -X GET "https://example.com/clients/export"
{  "csv": "string",  "rowCount": 0,  "truncated": true}
{  "error": "string",  "message": "string"}

Revoke access for every client matching a filter

POST/clients/bulk-filtered-action

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

POST /clients/bulk-filtered-action — revoke (archive) access in up to 10 products for EVERY client matching the Customers-list filter; the run resolves the client set server-side.

Response Body

application/json

application/json

curl -X POST "https://example.com/clients/bulk-filtered-action" \  -H "Content-Type: application/json" \  -d '{    "action": "archive",    "productIds": [      "string"    ],    "clientsFilter": {}  }'
{  "runId": "string"}
{  "error": "string",  "message": "string"}

Delete clients

POST/clients/delete

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

POST /clients/delete — up to 200 client ids per call.

Response Body

application/json

application/json

curl -X POST "https://example.com/clients/delete" \  -H "Content-Type: application/json" \  -d '{    "clientIds": [      "string"    ]  }'
{  "deletedClientIds": [    "string"  ],  "skipped": [    {      "clientId": "string",      "reason": "not-found"    }  ]}
{  "error": "string",  "message": "string"}

Client detail

GET/clients/{clientId}

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

clientId*string

Client id

Response Body

application/json

application/json

curl -X GET "https://example.com/clients/string"
{  "id": "string",  "email": "string",  "firstName": "string",  "lastName": "string",  "phoneNumber": "string",  "totalOrders": 0,  "totalSpentCents": 0,  "activeProducts": 0,  "language": "string",  "discordId": "string",  "githubId": 0,  "tagIds": [    "string"  ],  "emailSubscriptionStatus": "pending",  "attribution": {    "utmSource": "string",    "utmMedium": "string",    "utmCampaign": "string",    "utmTerm": "string",    "utmContent": "string",    "fbclid": "string",    "gclid": "string",    "metaCampaignId": "string",    "metaAdId": "string",    "metaCampaignName": "string",    "metaAdName": "string",    "capturedAt": "string"  },  "customFields": {    "property1": "string",    "property2": "string"  },  "createdAt": "string",  "previousEmails": [    "string"  ],  "firstSeen": "string",  "lastSeen": "string",  "invoiceData": {    "companyName": "string",    "nip": "string",    "street": "string",    "postalCode": "string",    "city": "string",    "country": "string"  },  "invoiceProfiles": [    {      "companyName": "string",      "nip": "string",      "street": "string",      "postalCode": "string",      "city": "string",      "country": "string"    }  ]}
{  "error": "string",  "message": "string"}

Update a client

PATCH/clients/{clientId}

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

clientId*string

Client id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Partial update of a client's profile fields. Omitted fields are left unchanged.

Response Body

application/json

application/json

curl -X PATCH "https://example.com/clients/string" \  -H "Content-Type: application/json" \  -d '{}'
{  "clientId": "string"}
{  "error": "string",  "message": "string"}

List a client's product accesses

GET/clients/{clientId}/product-accesses

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

clientId*string

Client id

Query Parameters

status?string

Filter on the derived access status. Omit for the complete list (all statuses).

Value in

  • "active"
  • "revoked"
  • "expired"

Response Body

application/json

application/json

curl -X GET "https://example.com/clients/string/product-accesses"
{  "items": [    {      "accessId": "string",      "productId": "string",      "productName": "string",      "productType": "course",      "status": "active",      "planId": "string",      "paymentType": "one_time",      "grantedAt": "string",      "updatedAt": "string",      "accessEndsAt": "string",      "cancelAtPeriodEnd": true,      "revokedAt": "string",      "revokeReason": "access_end",      "source": "purchase",      "orderId": "string",      "checkoutId": "string",      "isTest": true    }  ]}
{  "error": "string",  "message": "string"}

Bulk action on a product's clients

POST/products/{productId}/clients/bulk-action

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

productId*string

Product id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

POST /products/{productId}/clients/bulk-action — the product comes from the path. data is ArchiveClientsData for archive / delete and ChangeClientPeriodData for change_client_period.

Response Body

application/json

application/json

curl -X POST "https://example.com/products/string/clients/bulk-action" \  -H "Content-Type: application/json" \  -d '{    "action": "archive",    "data": {      "processAllClients": true,      "runTasks": true    }  }'
{  "runId": "string"}
{  "error": "string",  "message": "string"}

List a product's clients

GET/products/{productId}/clients

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

productId*string

Product id

Query Parameters

accessStatus?string

Filter on the derived access status; all returns every grant. Default: active.

Value in

  • "active"
  • "revoked"
  • "expired"
  • "all"
status?string

Legacy: stored grant status (archived = revoked + expired). Ignored when accessStatus is sent.

Value in

  • "active"
  • "archived"
limit?integer

Page size, max 100 (default 50).

cursor?string

The previous page's nextCursor.

Response Body

application/json

application/json

curl -X GET "https://example.com/products/string/clients"
{  "items": [    {      "accessId": "string",      "productId": "string",      "productName": "string",      "productType": "course",      "status": "active",      "planId": "string",      "paymentType": "one_time",      "grantedAt": "string",      "updatedAt": "string",      "accessEndsAt": "string",      "cancelAtPeriodEnd": true,      "revokedAt": "string",      "revokeReason": "access_end",      "source": "purchase",      "orderId": "string",      "checkoutId": "string",      "isTest": true,      "clientId": "string",      "email": "string",      "firstName": "string",      "lastName": "string",      "phoneNumber": "string",      "discordId": "string",      "priceId": "string",      "accessStartsAt": "string"    }  ],  "nextCursor": "string"}
{  "error": "string",  "message": "string"}

Grant product access

POST/products/{productId}/clients

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

productId*string

Product id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Grant a customer access to a product without a purchase (the dashboard's "New client access"). Default = free lifetime access; accessUntil makes a time-limited comp (free grants only); priceId enrolls under an existing price without billing. The welcome/sign-in email is sent only when sendWelcomeEmail is true.

Response Body

application/json

application/json

curl -X POST "https://example.com/products/string/clients" \  -H "Content-Type: application/json" \  -d '{    "email": "string"  }'
{  "granted": true,  "isFree": true}
{  "error": "string",  "message": "string"}

List a product's plan-bundled access holders

GET/products/{productId}/clients/bundled-access

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

productId*string

Product id

Response Body

application/json

application/json

curl -X GET "https://example.com/products/string/clients/bundled-access"
{  "items": [    {      "clientId": "string",      "email": "string",      "firstName": "string",      "lastName": "string",      "viaProductId": "string",      "viaProductName": "string",      "viaPlanName": "string",      "sinceAt": "string"    }  ]}
{  "error": "string",  "message": "string"}

Revoke product access

POST/products/{productId}/clients/revoke-access

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

productId*string

Product id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Revoke a customer's product access. NO refund is issued. Access backed by a subscription is revoked once the subscription is over in Stripe (checked live, KI#1448); a subscription that is still live is refused (409 with its status) unless cancelSubscription is set, which cancels it now and revokes in one step. Reversible via the grant route.

Response Body

application/json

application/json

curl -X POST "https://example.com/products/string/clients/revoke-access" \  -H "Content-Type: application/json" \  -d '{    "email": "string"  }'
{  "revoked": true,  "subscription": "none"}
{  "error": "string",  "message": "string"}

Change an access period

POST/products/{productId}/clients/access-period

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

productId*string

Product id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Re-window a TIME-LIMITED (one-time) access period. Provide exactly one field.

Response Body

application/json

application/json

curl -X POST "https://example.com/products/string/clients/access-period" \  -H "Content-Type: application/json" \  -d '{    "email": "string"  }'
{  "accessEndsAt": "string"}
{  "error": "string",  "message": "string"}

Cancel a client's subscription

POST/products/{productId}/clients/cancel-subscription

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

productId*string

Product id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Creator-side cancel of ONE client's live subscription to ONE product (POST /products/:productId/clients/cancel-subscription). Stops billing; access runs to the end of the paid period (or ends immediately when the latest invoice is unpaid). Idempotent: a subscription already set to cancel at period end answers 200 again; one Stripe already ended answers alreadyCancelled: true and the access record is brought in line with it.

Response Body

application/json

application/json

curl -X POST "https://example.com/products/string/clients/cancel-subscription" \  -H "Content-Type: application/json" \  -d '{    "email": "string"  }'
{  "cancelled": true,  "alreadyCancelled": true}
{  "error": "string",  "message": "string"}

List client notes

GET/clients/{clientId}/notes

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

clientId*string

Client id

Response Body

application/json

application/json

curl -X GET "https://example.com/clients/string/notes"
{  "notes": [    {      "id": "string",      "text": "string",      "authorId": "string",      "authorName": "string",      "createdAt": "string"    }  ]}
{  "error": "string",  "message": "string"}

Add a client note

POST/clients/{clientId}/notes

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

clientId*string

Client id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

curl -X POST "https://example.com/clients/string/notes" \  -H "Content-Type: application/json" \  -d '{    "text": "string"  }'
{  "note": {    "id": "string",    "text": "string",    "authorId": "string",    "authorName": "string",    "createdAt": "string"  }}
{  "error": "string",  "message": "string"}

Client email delivery log

GET/clients/{clientId}/email-log

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

clientId*string

Client id

Query Parameters

limit?integer

Newest entries to return (default 20, max 50).

Response Body

application/json

application/json

curl -X GET "https://example.com/clients/string/email-log"
{  "email": "string",  "entries": [    {      "sentAt": "string",      "statusUpdatedAt": "string",      "subject": "string",      "template": "string",      "status": "submitted",      "statusDetails": "string",      "orderId": "string",      "campaignId": "string",      "automationId": "string",      "tracked": true,      "openedAt": "string",      "clickedAt": "string"    }  ]}
{  "error": "string",  "message": "string"}

Change a client's email

POST/clients/{clientId}/change-email

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

clientId*string

Client id

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

curl -X POST "https://example.com/clients/string/change-email" \  -H "Content-Type: application/json" \  -d '{    "newEmail": "string"  }'
{  "clientId": "string",  "grantsUpdated": 0}
{  "error": "string",  "message": "string"}

List a client's devices

GET/clients/{clientId}/devices

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

clientId*string

Client id

Response Body

application/json

application/json

curl -X GET "https://example.com/clients/string/devices"
{  "devices": [    {      "deviceId": "string",      "clientKind": "web",      "deviceName": "string",      "createdAt": "string",      "lastSeen": "string",      "activeUntil": "string",      "isCurrent": true    }  ]}
{  "error": "string",  "message": "string"}

Revoke a client device

POST/clients/{clientId}/devices/{deviceId}/revoke

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

clientId*string

Client id

deviceId*string

Customer device slot id

Response Body

application/json

application/json

curl -X POST "https://example.com/clients/string/devices/string/revoke"
{  "status": "revoked",  "deviceId": "string"}
{  "error": "string",  "message": "string"}

Reset a client's devices

POST/clients/{clientId}/devices/reset

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Path Parameters

clientId*string

Client id

Response Body

application/json

application/json

curl -X POST "https://example.com/clients/string/devices/reset"
{  "revokedCount": 0}
{  "error": "string",  "message": "string"}

Bulk-import contacts

POST/clients/import

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Bulk contact import — one bounded batch of contact-only client creates (no product grant, no order), the row shape a CSV maps onto naturally. Each row goes through the same create path as POST /clients (real Auth identity, dedup by email); tagIds are applied to every resolved row. Larger files are chunked by the caller (max 100 rows per request).

Response Body

application/json

application/json

curl -X POST "https://example.com/clients/import" \  -H "Content-Type: application/json" \  -d '{    "contacts": [      {        "email": "string"      }    ]  }'
{  "results": [    {      "email": "string",      "status": "created",      "clientId": "string",      "skippedCustomFields": [        "string"      ]    }  ],  "created": 0,  "existing": 0,  "invalid": 0,  "overLimit": 0,  "customFieldValuesSkipped": 0}
{  "error": "string",  "message": "string"}

Preview a client merge

GET/clients/merge-preview

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Response Body

application/json

application/json

curl -X GET "https://example.com/clients/merge-preview"
{  "preview": {    "workspaceId": "string",    "survivorWcid": "string",    "loserWcid": "string",    "survivorEmail": "string",    "loserEmail": "string",    "resultingPreviousEmails": [      "string"    ],    "money": {      "survivorTotalSpentMinor": 0,      "loserTotalSpentMinor": 0,      "sumMinor": 0,      "note": "string",      "ordersMovingCount": 0    },    "grants": [      {        "productId": "string",        "productName": "string",        "action": "move",        "reason": "string"      }    ],    "blocked": {      "reason": "active-subscription",      "subscriptions": [        {          "productId": "string",          "subscriptionId": "string",          "side": "survivor"        }      ]    },    "profileOverwrites": [      {        "field": "firstName",        "survivorValueKept": "string",        "loserValueDiscarded": "string",        "downstreamUnlink": true      }    ],    "invoiceProfiles": {      "survivorCount": 0,      "loserCount": 0,      "note": "string"    },    "community": {      "computed": true,      "communities": [        {          "communityId": "string",          "communityName": "string",          "action": "merge",          "authoredRePointCount": 0        }      ],      "communitiesTouchedCount": 0,      "authoredRePointCount": 0,      "note": "string"    },    "irreversible": true,    "previewHash": "string"  }}
{  "error": "string",  "message": "string"}

Merge duplicate clients

POST/clients/merge

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

⚠️ DESTRUCTIVE AND IRREVERSIBLE: merges the duplicate client into the survivor — grants move/dedupe, community membership and authored content re-point, profiles coalesce, and the duplicate client is TOMBSTONED. There is no undo route (recovery is an operator-level restore from the merge snapshot). Requires a fresh previewHash from GET /clients/merge-preview; execute aborts with 409 if the recomputed plan differs. A merge never touches Stripe — a live subscription on either side blocks it (409).

Response Body

application/json

application/json

curl -X POST "https://example.com/clients/merge" \  -H "Content-Type: application/json" \  -d '{    "survivorClientId": "string",    "duplicateClientId": "string",    "previewHash": "string"  }'
{  "survivorClientId": "string",  "grantsProcessed": 0,  "merged": true}
{  "error": "string",  "message": "string"}

Bulk subscribe/unsubscribe clients (email)

POST/clients/email-subscription

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Set the marketing-email subscription of MANY clients at once (the Customers-list "Subscribe" / "Unsubscribe" bulk actions). Source manual. Subscribing requires the creator to attest the consent basis via reason; unsubscribing takes no reason. Clients already in the target status are reported as unchanged.

Set the marketing-email subscription of MANY clients at once (the Customers-list "Subscribe" / "Unsubscribe" bulk actions). Source manual. Subscribing requires the creator to attest the consent basis via reason; unsubscribing takes no reason. Clients already in the target status are reported as unchanged.

Response Body

application/json

application/json

curl -X POST "https://example.com/clients/email-subscription" \  -H "Content-Type: application/json" \  -d '{    "status": "subscribed",    "reason": "imported-from-other-list",    "clientIds": [      "string"    ]  }'
{  "ok": true,  "updatedClientIds": [    "string"  ],  "unchangedClientIds": [    "string"  ],  "skippedClientIds": [    "string"  ]}
{  "error": "string",  "message": "string"}

Bulk subscribe/unsubscribe clients (SMS)

POST/clients/sms-subscription

Authorization

bearerAuth
AuthorizationBearer <token>

API key from Dashboard → Integrations → API, MCP and CLI.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Set the marketing-SMS consent of MANY clients at once (the Customers-list "Subscribe to SMS" / "Unsubscribe from SMS" bulk actions). Source manual. Separate from the e-mail twin on purpose: SMS is its own consent under PL/EU rules, so an e-mail opt-in is never a basis here and the reason list is narrower (ClientSmsSubscriptionReason). Subscribing requires the creator to attest the basis; unsubscribing takes no reason.

Set the marketing-SMS consent of MANY clients at once (the Customers-list "Subscribe to SMS" / "Unsubscribe from SMS" bulk actions). Source manual. Separate from the e-mail twin on purpose: SMS is its own consent under PL/EU rules, so an e-mail opt-in is never a basis here and the reason list is narrower (ClientSmsSubscriptionReason). Subscribing requires the creator to attest the basis; unsubscribing takes no reason.

Response Body

application/json

application/json

curl -X POST "https://example.com/clients/sms-subscription" \  -H "Content-Type: application/json" \  -d '{    "status": "subscribed",    "reason": "explicit-sms-consent",    "clientIds": [      "string"    ]  }'
{  "ok": true,  "updatedClientIds": [    "string"  ],  "unchangedClientIds": [    "string"  ],  "skippedClientIds": [    "string"  ]}
{  "error": "string",  "message": "string"}

Was this article helpful?

Related articles

Spotted something off? Tell us at support@zanfia.com.