Create or update a contact

POST
/v1/contacts

Creates the contact with this address in the API key's environment, or updates the existing one: names are replaced when given, properties are merged (null removes one), audience_ids are added and topics choices recorded. status only changes when passed.

Authorization

bearerAuth
headerAuthorizationBearer <token>

A secret API key from the dashboard: coss_live_… for production environments, coss_test_… for sandbox environments.

Header Parameters

Coss-Version?"2026-10-10"

The API version (a date) to use for this request. Defaults to your organization's version, pinned to the latest one when it first used the API and upgraded from the dashboard. The SDK always sends the version it was built for.

Value in"2026-10-10"

Request Body

application/json
  1. body
email*string

The contact's address. Display names are ignored; matching is case-insensitive.

first_name?|
Length1 <= length <= 256
last_name?|
Length1 <= length <= 256
properties?

Custom properties by key (define them first, see contact properties). Merged into the contact's existing properties; null removes one.

status?string

Sets the contact's consent. Omitted, a new contact is subscribed and an existing one keeps its status.

Value in"subscribed""unsubscribed"
audience_ids?array<string>

Audiences to add the contact to (it stays in the others).

Itemsitems <= 20
topics?array<>

Choices per topic; topics left out keep the contact's choice.

Itemsitems <= 100
double_opt_in?boolean

Ask the contact to confirm first (default: the marketing settings' double_opt_in_default): a new or unsubscribed contact becomes pending and gets a confirmation email; it is subscribed once they click its link. Can't be combined with status: "subscribed", nor can the default (pass false to subscribe directly).

Response Body

The existing contact, updated.

application/json
  1. response
object*"contact"
Value in"contact"
id*string
email*string
first_name*string|null
last_name*string|null
status*string

Consent to marketing email: subscribed, unsubscribed (never receives marketing email; transactional email is unaffected) or pending (waiting for a double opt-in confirmation).

Value in"subscribed""unsubscribed""pending"
properties*
source*string

How the contact was created.

Value in"api""dashboard""import""preference_center"
subscribed_at*|
Formatdate-time
unsubscribed_at*|
Formatdate-time
confirmed_at*|

When the contact confirmed a double opt-in.

Formatdate-time
created_at*string
Formatdate-time
updated_at*string
Formatdate-time
import { Coss } from "coss";const coss = new Coss({  apiKey: process.env.COSS_API_KEY,  baseUrl: "https://api.coss.com",});const { data, error } = await coss.email.contacts.upsert({  email: "ada@example.com",  first_name: "Ada",  properties: {    plan: "pro",  },  topics: [    {      topic: "newsletter",      status: "opted_in",    },  ],});
{  "object": "contact",  "id": "contact_01k7c8m4q2f3e9v6t5r1n0p8yz",  "email": "ada@example.com",  "first_name": "Ada",  "last_name": "Lovelace",  "status": "subscribed",  "properties": {    "plan": "pro",    "seats": 5  },  "source": "api",  "subscribed_at": "2026-10-10T12:00:00.000Z",  "unsubscribed_at": null,  "confirmed_at": null,  "created_at": "2026-10-10T12:00:00.000Z",  "updated_at": "2026-10-10T12:00:00.000Z"}