Manage subscribers
Create, update, unsubscribe and delete email subscribers by address from your backend with an org API key.
Keep your email audience in step with your app: add a subscriber when someone signs up, tag them as their plan changes, and unsubscribe them when they ask. Subscribers are addressed by email and are the same contacts you see in the console audience.
Before you start
Create an API role under Settings > API roles with audience (read,
create, update, delete), plus audience_lists (update) to add people to
lists. Then an API key with that role under Settings > API keys. Tags are
created in the console.
The subscribers calls ship in @buildbase/sdk 1.0.0. If bb.subscribers is undefined, you are on an earlier version.
Quick example
import { createServerClient } from '@buildbase/sdk/server';
const bb = createServerClient({
serverUrl: 'https://api.console.buildbase.app',
apiToken: process.env.BUILDBASE_API_TOKEN!, // <orgId>:<secret>
});
export async function onSignup(user: {
email: string;
name: string;
plan: string;
}) {
const subscriber = await bb.subscribers.upsert({
email: user.email,
name: user.name,
tags: ['customer'],
attributes: { plan: user.plan },
lists: [process.env.NEWSLETTER_LIST_ID!],
});
return subscriber.created; // true the first time, false after
}Upsert
upsert creates the subscriber, or updates the one with that address. What you leave
out is kept.
| Field | Type | Description |
|---|---|---|
email | string | Required. Case does not matter |
name | string | Display name; a new subscriber without one uses the email |
tags | string[] | Tag slugs to add, up to 50 |
removeTags | string[] | Tag slugs to remove |
attributes | Record<string, string | number | boolean | null> | Merged over the current attributes, up to 100 keys |
lists | string[] | Static list ids to add them to |
timezone, language, country, currency | string | Profile fields |
An upsert never changes whether someone is subscribed. A sign-up form that upserts again does not resubscribe a person who left.
An unknown tag slug or list id is refused with UNKNOWN_REFERENCE and changes nothing.
A new subscriber counts toward your plan's contact limit; updating one never does.
Read, unsubscribe and resubscribe
const subscriber = await bb.subscribers.get('[email protected]'); // null when there is none
await bb.subscribers.unsubscribe('[email protected]', {
reason: 'asked by email',
});
await bb.subscribers.resubscribe('[email protected]');unsubscribe stops marketing and transactional sends to the address; account email
such as password resets still arrives. resubscribe is refused with
RECIPIENT_SUPPRESSED for an address that bounced or is a spam trap.
Delete
await bb.subscribers.delete('[email protected]');Delete takes the subscriber off every list (firing audience.removed_from_list for
each) and then soft-deletes them: they no longer appear in the console, in reads
or in any send, but the record is kept, marked deleted, and so is their history,
such as the email already sent to them, so a delete is not an erasure of their
data. The console has no delete; this call is
the only way to remove a contact. The address can be added again later and then
starts subscribed, so to stop email to a person who asked, unsubscribe them
instead.
Events
Every change fires the same events as the console, so your workflows and
webhooks see it: audience.member_created,
audience.member_updated, audience.tag_added, audience.tag_removed,
audience.attribute_changed, audience.added_to_list, audience.removed_from_list
(on delete), audience.unsubscribed and audience.resubscribed.
Handling refusals
Refusals throw BuildBaseApiError with status and code:
| Code | Status | Meaning |
|---|---|---|
UNKNOWN_REFERENCE | 422 | A tag slug or list id that does not exist; see field |
RECIPIENT_SUPPRESSED | 422 | resubscribe for an address that bounced |
SUBSCRIBER_NOT_FOUND | 404 | No subscriber with that address |
API_KEY_REQUIRED | 403 | Called with a console session instead of an API key |
The contact or list-size limit is a 402 with the plan's code.