Create a contact. 409 on duplicate external_user_id; without one, always inserts
POST
/v1/contacts
const url = 'https://api.spirby.com/v1/contacts';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"currency":"example","email":"[email protected]","externalUserId":"example","metadata":{"additionalProperty":"example"},"mrrCents":1,"name":"example","plan":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.spirby.com/v1/contacts \ --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" }'Authorizations
Section titled “Authorizations ”Request Body
Section titled “Request Body ” Media type application/json
object
currency
string | null
email
string | null format: email
externalUserId
string
metadata
object
key
additional properties
mrrCents
integer | null
name
string | null
plan
string | null
Example generated
{ "currency": "example", "externalUserId": "example", "metadata": { "additionalProperty": "example" }, "mrrCents": 1, "name": "example", "plan": "example"}Responses
Section titled “ Responses ”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
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
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" }}