useSendEverJust

Contacts & Contact Books

Manage audience lists (contact books) and their contacts for broadcast campaigns on self-hosted useSend.

Contact books are useSend's audience feature: named lists of contacts that campaigns send to. A contact book holds many contacts, each with an email, optional name, subscription state, and custom properties. Double opt-in is configurable per book.

Contacts are for marketing/broadcast sending only. Transactional sends via POST /v1/emails take a raw to address and do not require a contact or contact book. Reach for this API only when you are building lists for campaigns.

All requests use the self-hosted base URL https://mail.everjust.app/api/v1 and Bearer auth. Rate limiting is disabled on this instance.

Authorization: Bearer us_xxx

Both contactBookId and contactId are strings. Contacts are nested under a contact book, so contact paths always carry the book id: /v1/contactBooks/{contactBookId}/contacts/{contactId}.

Contact books

Object shape

FieldTypeNotes
idstringContact book id.
namestringDisplay name.
teamIdnumberOwning team (tenant).
propertiesobjectCustom property schema defined for contacts in this book.
variablesobjectVariables available to campaigns using this book.
emojistringOptional emoji shown in the dashboard.
doubleOptInEnabledbooleanWhen true, new contacts must confirm before becoming subscribed.
doubleOptInFromstringFrom address for the confirmation email.
doubleOptInSubjectstringSubject of the confirmation email.
doubleOptInContentstringBody of the confirmation email.
createdAtstringISO-8601 timestamp.
updatedAtstringISO-8601 timestamp.
_countobject{ contacts } — number of contacts in the book.

Create a contact book

POST /v1/contactBooks

Body fieldTypeRequiredNotes
namestringYesDisplay name.
emojistringNoDashboard emoji.
propertiesobjectNoCustom property schema.
doubleOptInEnabledbooleanNoEnable double opt-in.
doubleOptInFromstringNoConfirmation from address.
doubleOptInSubjectstringNoConfirmation subject.
doubleOptInContentstringNoConfirmation body.
variablesobjectNoCampaign variables.

Returns the created ContactBook.

curl -X POST https://mail.everjust.app/api/v1/contactBooks \
  -H "Authorization: Bearer us_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Product updates",
    "emoji": "📣",
    "doubleOptInEnabled": true,
    "doubleOptInFrom": "EverJust <hello@send.everjust.app>",
    "doubleOptInSubject": "Confirm your subscription",
    "doubleOptInContent": "Click the link to confirm you want product updates."
  }'
import { UseSend } from "usesend-js";

const usesend = new UseSend("us_xxx", "https://mail.everjust.app/api/v1");

const { data, error } = await usesend.contactBooks.create({
  name: "Product updates",
  emoji: "📣",
  doubleOptInEnabled: true,
  doubleOptInFrom: "EverJust <hello@send.everjust.app>",
  doubleOptInSubject: "Confirm your subscription",
  doubleOptInContent: "Click the link to confirm you want product updates.",
});
from usesend import UseSend

usesend = UseSend("us_xxx", "https://mail.everjust.app/api/v1")

book = usesend.contactBooks.create({
    "name": "Product updates",
    "emoji": "📣",
    "doubleOptInEnabled": True,
    "doubleOptInFrom": "EverJust <hello@send.everjust.app>",
    "doubleOptInSubject": "Confirm your subscription",
    "doubleOptInContent": "Click the link to confirm you want product updates.",
})

List, get, update, delete

Method & pathPurposeResponse
GET /v1/contactBooksList all contact books.Array of ContactBook.
GET /v1/contactBooks/{contactBookId}Fetch one book.ContactBook.
PATCH /v1/contactBooks/{contactBookId}Update fields (same body as create, all optional).ContactBook.
DELETE /v1/contactBooks/{contactBookId}Delete a book.{ id, success, message }.
curl https://mail.everjust.app/api/v1/contactBooks \
  -H "Authorization: Bearer us_xxx"

Deleting a contact book removes its contacts as well. There is no soft-delete — the operation is irreversible.

Contacts

Contacts live inside a contact book. All paths below are relative to /v1/contactBooks/{contactBookId}/contacts.

Object shape

FieldTypeNotes
idstringContact id.
firstNamestringOptional.
lastNamestringOptional.
emailstringContact email address.
subscribedbooleanSubscription state. With double opt-in enabled, new contacts stay unsubscribed until they confirm.
propertiesobjectCustom properties for this contact.
contactBookIdstringParent contact book.
createdAtstringISO-8601 timestamp.
updatedAtstringISO-8601 timestamp.

Create a contact

POST /v1/contactBooks/{contactBookId}/contacts

Body fieldTypeRequiredNotes
emailstringYesContact email.
firstNamestringNo
lastNamestringNo
propertiesobjectNoCustom properties.
subscribedbooleanNoInitial subscription state.

Returns { contactId }.

curl -X POST https://mail.everjust.app/api/v1/contactBooks/cb_123/contacts \
  -H "Authorization: Bearer us_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "ada@example.com",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "subscribed": true,
    "properties": { "plan": "pro" }
  }'
const { data } = await usesend.contacts.create("cb_123", {
  email: "ada@example.com",
  firstName: "Ada",
  lastName: "Lovelace",
  subscribed: true,
  properties: { plan: "pro" },
});
// data => { contactId: "..." }
result = usesend.contacts.create("cb_123", {
    "email": "ada@example.com",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "subscribed": True,
    "properties": {"plan": "pro"},
})
# result => { "contactId": "..." }

List contacts

GET /v1/contactBooks/{contactBookId}/contacts

Query paramTypeNotes
emailsstringComma-separated list of emails to filter by.
idsstringComma-separated list of contact ids.
pagenumberPage number (default 1).
limitnumberPage size (default 50).

Returns an array of Contact.

curl "https://mail.everjust.app/api/v1/contactBooks/cb_123/contacts?page=1&limit=50" \
  -H "Authorization: Bearer us_xxx"

Get, update, upsert, delete

Method & pathPurposeBodyResponse
GET …/contacts/{contactId}Fetch one contact.Contact.
PATCH …/contacts/{contactId}Update fields.firstName, lastName, properties, subscribed{ contactId }.
PUT …/contacts/{contactId}Upsert by id.email (required), plus any updatable fields{ contactId }.
DELETE …/contacts/{contactId}Delete a contact.{ success }.
# Unsubscribe a contact
curl -X PATCH https://mail.everjust.app/api/v1/contactBooks/cb_123/contacts/ct_456 \
  -H "Authorization: Bearer us_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "subscribed": false }'
await usesend.contacts.update("cb_123", "ct_456", { subscribed: false });

// Upsert (PUT) — email is required
await usesend.contacts.upsert("cb_123", "ct_456", {
  email: "ada@example.com",
  subscribed: true,
});
usesend.contacts.update("cb_123", "ct_456", {"subscribed": False})

# Upsert (PUT) — email is required
usesend.contacts.upsert("cb_123", "ct_456", {
    "email": "ada@example.com",
    "subscribed": True,
})

Bulk add and delete

For importing or removing many contacts at once:

Method & pathBodyResponse
POST …/contacts/bulkJSON array of contact objects (same fields as create).{ message, count }.
DELETE …/contacts/bulk{ contactIds: string[] }.{ success, count }.
# Bulk add
curl -X POST https://mail.everjust.app/api/v1/contactBooks/cb_123/contacts/bulk \
  -H "Authorization: Bearer us_xxx" \
  -H "Content-Type: application/json" \
  -d '[
    { "email": "grace@example.com", "firstName": "Grace", "subscribed": true },
    { "email": "alan@example.com", "firstName": "Alan", "subscribed": true }
  ]'

# Bulk delete
curl -X DELETE https://mail.everjust.app/api/v1/contactBooks/cb_123/contacts/bulk \
  -H "Authorization: Bearer us_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "contactIds": ["ct_456", "ct_789"] }'
await usesend.contacts.bulkCreate("cb_123", [
  { email: "grace@example.com", firstName: "Grace", subscribed: true },
  { email: "alan@example.com", firstName: "Alan", subscribed: true },
]);

await usesend.contacts.bulkDelete("cb_123", {
  contactIds: ["ct_456", "ct_789"],
});
usesend.contacts.bulk_create("cb_123", [
    {"email": "grace@example.com", "firstName": "Grace", "subscribed": True},
    {"email": "alan@example.com", "firstName": "Alan", "subscribed": True},
])

usesend.contacts.bulk_delete("cb_123", {
    "contactIds": ["ct_456", "ct_789"],
})

Double opt-in

When doubleOptInEnabled is true on a contact book, useSend sends a confirmation email to newly added contacts using the book's doubleOptInFrom, doubleOptInSubject, and doubleOptInContent. A contact remains subscribed: false until they confirm.

The double opt-in email is a normal send through Amazon SES. While this instance's SES is in sandbox, confirmation emails only reach verified addresses or the SES mailbox simulator — so opt-in flows for arbitrary recipients will not complete until AWS grants production access. See Sending email for the sandbox details.

Errors

Errors use the standard envelope:

{ "error": { "code": "NOT_FOUND", "message": "Contact not found" } }

Common codes for this API: BAD_REQUEST (400), UNAUTHORIZED (401), FORBIDDEN (403), NOT_FOUND (404), NOT_UNIQUE (409 — e.g. duplicate email in a book), INTERNAL_SERVER_ERROR (500).

Next steps