Skip to content

Identify (upsert) a contact by external_user_id. Naturally idempotent: same payload → same final state. Returns 201 on first call, 200 on subsequent.

POST
/v1/contacts/identify
curl --request POST \
--url https://api.spirby.com/v1/contacts/identify \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "currency": "example", "email": "[email protected]", "externalUserId": "example", "metadata": { "additionalProperty": "example" }, "mrrCents": 1, "name": "example", "plan": "example" }'
Media type application/json
object
currency
string | null
/^[A-Z]{3}$/
email
string | null format: email
<= 320 characters
externalUserId
required
string
>= 1 characters <= 255 characters
metadata
object
key
additional properties
mrrCents
integer | null
<= 100000000
name
string | null
>= 1 characters <= 200 characters
plan
string | null
>= 1 characters <= 100 characters
Example generated
{
"currency": "example",
"email": "[email protected]",
"externalUserId": "example",
"metadata": {
"additionalProperty": "example"
},
"mrrCents": 1,
"name": "example",
"plan": "example"
}

Successful response

Media type application/json
object
data
required

A contact row with provenance + consent metadata.

object
consentBasis
required

Lawful basis the row was created under. sdk_identify = your app already had consent; public_interaction_opt_in = end-user ticked the opt-in box; admin_created = workspace admin attested; legacy_inferred = pre-opt-in backfill (treat as suppressed for marketing, DSAR-eligible).

string
Allowed values: sdk_identify public_interaction_opt_in admin_created legacy_inferred
createdAt
required
string format: date-time
currency
required
string | null
email
required
string | null
externalUserId
required
string | null
firstSeenAt
required
string format: date-time
id
required
string
lastSeenAt
required
string format: date-time
metadata
required
object
key
additional properties
mrrCents
required
integer | null
name
required
string | null
organizationId
required
string
plan
required
string | null
source
required

How this contact row entered the system. identify = SDK widget / REST identify; admin = manual create; public_* = end-user opted in on the public board.

string
Allowed values: identify admin public_post public_vote public_comment
updatedAt
required
string format: date-time
Example
{
"data": {
"consentBasis": "sdk_identify",
"source": "identify"
}
}

Missing or invalid API key

Media type application/json
object
error
required
object
code
required
string
details
object
key
additional properties
message
required
string
Example generated
{
"error": {
"code": "example",
"details": {
"additionalProperty": "example"
},
"message": "example"
}
}

Scope insufficient (ERR_SCOPE_INSUFFICIENT) or org in readonly billing state (ERR_FORBIDDEN)

Media type application/json
object
error
required
object
code
required
string
details
object
key
additional properties
message
required
string
Example generated
{
"error": {
"code": "example",
"details": {
"additionalProperty": "example"
},
"message": "example"
}
}

Validation failed

Media type application/json
object
error
required
object
code
required
string
details
object
key
additional properties
message
required
string
Example generated
{
"error": {
"code": "example",
"details": {
"additionalProperty": "example"
},
"message": "example"
}
}

Rate limit exceeded

Media type application/json
object
error
required
object
code
required
string
details
object
key
additional properties
message
required
string
Example generated
{
"error": {
"code": "example",
"details": {
"additionalProperty": "example"
},
"message": "example"
}
}

Internal error

Media type application/json
object
error
required
object
code
required
string
details
object
key
additional properties
message
required
string
Example generated
{
"error": {
"code": "example",
"details": {
"additionalProperty": "example"
},
"message": "example"
}
}