Create or update contact
v2Create a new contact, or update one that already exists, in a single call. The HTTP status itself tells you what happened — 201 for created, 200 for updated or unchanged, 409 for a duplicate or suppressed contact — so a plain response.ok check is all you need. To sync multiple contacts in a single API call, use the batch endpoint instead.
https://api.scorehq.co/api/v2/contactsSend your secret key in the x-api-key header. Never call this endpoint directly from a browser. Authentication details
Request body
A single contact object — sending an array here returns 400 INVALID_REQUEST.
emailstringrequiredlist_idsstring[]requiredoverwritebooleanfieldsobjectStandard and custom fields
These keys inside fields map directly to built-in contact properties.
first_name · stringlast_name · stringphone · stringcity · stringcountry · stringEvery other key is a custom field and must already exist under Settings → Custom Fields. Unknown names fail with UNKNOWN_FIELD; the API never creates definitions. Custom fields support text, number, date, and boolean. Dates accept 2026-08-31 or a full timestamp such as 2026-08-31T14:30:00Z, with or without milliseconds.
Clearing fields
Omit the key from fields.
Send null.
Send "" (an empty string).
Not allowed; returns REQUIRED_FIELD_MISSING.
Custom fields are merged into the stored set, never replaced wholesale. Sending one custom field never removes the others.
Overwrite behavior
How existence, status, and overwrite determine the result and the HTTP status you get back.
true or falseCreated and added to every list in list_ids. Returns 201.
falseNothing changes. Returns 409 DUPLICATE_EMAIL.
trueSent fields are merged and new list memberships are added. Returns 200.
falseNothing changes. Returns 409 SUPPRESSED_EMAIL.
trueProfile fields update, but no lists or journeys are added. Returns 200.
Even with overwrite: true, this endpoint never re-subscribes a bounced, complained, or unsubscribed contact. It does not add them to a list or enroll them in a journey. Re-enabling email must be done deliberately through suppression management in the dashboard.
Adding an active existing contact to a new list can enroll them in a published journey with a matching list-entry trigger, just as a dashboard or CSV import can.
Response
Exactly one outcome, reported as the HTTP status itself.
The response body is the contact's result directly — email, status, and either list_ids_added/fields_changed on success or code/message on failure. There's no wrapper array and no summary counts, since there's only ever one contact to describe.
A caller that only checks response.ok gets the right answer: 201 and 200 mean the write happened, anything 4xx means it didn't.
Errors
Request-level errors return before any contact logic runs; everything else is the contact's own outcome.
Request-level errors
Nothing is written when one of these occurs.
INVALID_REQUESTAn array was sent to this endpoint — send an object with exactly one contact, or use the batch endpoint for multiple.
INVALID_API_KEYThe API key is missing, invalid, expired, or revoked.
PAYLOAD_TOO_LARGEThe decoded JSON request body is larger than 1 MB.
RATE_LIMIT_EXCEEDEDThe organization exceeded 300 requests in 60 seconds — shared with the batch endpoint.
INTERNAL_ERRORScore could not complete the request.
Request-level error shape
{
"request_id": "req_01J7X8W3E4N8",
"error": {
"code": "INVALID_API_KEY",
"message": "The supplied API key is invalid"
}
}
Contact outcome errors
Each of these is the response — the HTTP status shown below is what you'll actually receive, not a code buried in a body you have to check.
INVALID_EMAILThe email isn't valid after trimming and lowercasing.
DUPLICATE_EMAILThe contact exists and overwrite was not set to true.
SUPPRESSED_EMAILThe contact is suppressed and overwrite was not set to true.
EMPTY_LIST_IDSNo list ID was provided.
TOO_MANY_LIST_IDSMore than 20 list IDs were provided.
INVALID_LISTA list doesn't exist, is archived, or belongs to another organization.
UNKNOWN_FIELDA fields key doesn't match a standard or existing custom field.
FIELD_TYPE_MISMATCHA value doesn't match its field's configured type.
REQUIRED_FIELD_MISSINGA required custom field has no value or was cleared with null.
One code from the full error reference doesn't apply here: DUPLICATE_IN_REQUEST can only occur when the same email appears twice in one request, which requires the batch endpoint.
INVALID_LIST response detail
{
"request_id": "req_...",
"email": "alex@example.com",
"status": "failed",
"code": "INVALID_LIST",
"message": "One or more lists were not found in this organization",
"invalidListIds": ["000000000000000000000000"]
}