ReferenceReference by module

Customers API

Every Customers action (27) and data model (6): endpoints, scopes, input, preconditions, events and examples.

One record for every customer, shared by every module.

Module code customers · 27 actions · 6 data models · 18 events. A key or token never does more here than the person behind it can; row-level security applies exactly as in the app. Scopes: customers (everything), customers:read (read only) or customers:<capability>.

Resources

ResourceTableList and readPOSTPATCHDELETE
addressesaddress/api/v1/addressescustomers.add_address
customer_addressescustomer_address/api/v1/customer_addresses
customer_identitiescustomer_identity/api/v1/customer_identities
customer_login_codescustomer_login_code/api/v1/customer_login_codes
customer_loginscustomer_login/api/v1/customer_logins
customerscustomer/api/v1/customerscustomers.capturecustomers.updatecustomers.archive

Lists take limit (up to 200), cursor, order (e.g. -created_at), fields and q; every other query parameter is a column filter: ?status=paid, ?total[gte]=100, ?id[in]=a,b, ?x=null.

Actions

ActionNeedsWhat it does
customers.add_addresscustomers:writeAdd an address
customers.add_identitycustomers:writeAdd a way to recognise this person
customers.add_my_addresscustomers:writeAdd an address to my account
customers.archivecustomers:deleteArchive a customer
customers.capturecustomers:writeAdd or recognise a customer
customers.capture_site_guestcustomers:writeRecognise a website buyer
customers.confirm_email_changecustomers:writeConfirm my new email
customers.disable_logincustomers:manageSwitch off site account
customers.enable_logincustomers:manageSwitch site account back on
customers.exportcustomers:exportExport customers
customers.mergecustomers:manageMerge two customers
customers.remove_addresscustomers:deleteArchive an address
customers.remove_identitycustomers:writeRemove a way to recognise this person
customers.remove_my_addresscustomers:writeRemove an address from my account
customers.request_email_changecustomers:writeChange my sign-in email
customers.request_sign_in_codecustomers:writeSend a sign-in code
customers.restorecustomers:deleteRestore a customer
customers.send_sign_in_linkcustomers:writeSend a site sign-in link
customers.set_default_addresscustomers:writeSet a default address
customers.set_my_passwordcustomers:writeSet my password
customers.set_parentcustomers:writeAttach to a company
customers.sign_in_with_passwordcustomers:writeSign in with a password
customers.sign_outcustomers:writeSign out
customers.updatecustomers:writeEdit a customer
customers.update_addresscustomers:writeEdit an address
customers.update_my_profilecustomers:writeUpdate my details
customers.verify_sign_in_codecustomers:writeSign in with a code

customers.add_address

Add an address — Puts a delivery or billing address in this person’s book.

Needs customers:write · changes data

POSThttps://app.getera.io/api/v1/addresses/actions/add_address
API key or OAuth token · customers:write
  • REST alias: POST /api/v1/addresses
  • Canonical address: POST /api/v1/actions/customers.add_address

Input

FieldTypeRequiredNotes
iduuidyesCustomer · id of customer
labelstringLabel · Home, Office — what the picker shows. · max 60
firstNamestringFirst name · max 256
lastNamestringLast name · max 256
companyNamestringCompany · max 256
streetAddress1stringStreet · max 256
streetAddress2stringStreet (line 2) · max 256
cityAreastringDistrict · max 128
citystringCity · max 256
countryAreastringProvince / state · max 128
postalCodestringPostal code · max 20
countrystringCountry · ISO code, e.g. TR. · max 2
phonephonePhone
rawstringAddress as written · The bridge for an address nobody has split yet, and for countries with no schema. · max 2000
validationSkippedbooleanAccepted without validation
isDefaultShippingbooleanDefault for delivery
isDefaultBillingbooleanDefault for billing

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe record must exist.
already_mergedA merged record is edited through its survivor.
empty_addressAn address with nothing in it is not a record.

Events it publishes: customers.address_changed

What it writes: address (insert) — Writes the address; customer_address (upsert) — Links it to the person

curl -X POST https://app.getera.io/api/v1/addresses/actions/add_address \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"id":"CUSTOMER_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.address_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.addresses.create({
  id: "CUSTOMER_ID"
})
era addresses create \
  --id "CUSTOMER_ID"
tools/call
{
  "name": "era__customers__add_address",
  "arguments": {
    "id": "CUSTOMER_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, already_merged, empty_address · 429 slow down

customers.add_identity

Add a way to recognise this person — Records another email, phone number, domain or external key for one customer.

Needs customers:write · changes data

POSThttps://app.getera.io/api/v1/customer_identities/actions/add_identity
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.add_identity

Input

FieldTypeRequiredNotes
iduuidyesCustomer · id of customer
kindstring (enum)yesKind · Allowed values: email, phone, domain, external, app_user, channel
valuestringyesValue · max 320

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe record must exist.
already_mergedA merged record is edited through its survivor.
empty_identityThe value must not be empty.
identity_already_presentThe record must not already carry that key.
identity_takenA key that belongs to someone else is a merge, not an edit.

Events it publishes: customers.identity_changed

What it writes: customer_identity (insert) — Adds the key

curl -X POST https://app.getera.io/api/v1/customer_identities/actions/add_identity \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"id":"CUSTOMER_ID","kind":"email","value":"Value"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.identity_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.customerIdentities.addIdentity({
  id: "CUSTOMER_ID",
  kind: "email",
  value: "Value"
})
era customer_identities add_identity \
  --id "CUSTOMER_ID" \
  --kind "email" \
  --value "Value"
tools/call
{
  "name": "era__customers__add_identity",
  "arguments": {
    "id": "CUSTOMER_ID",
    "kind": "email",
    "value": "Value"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, already_merged, empty_identity, identity_already_present, identity_taken · 429 slow down

customers.add_my_address

Add an address to my account — A signed-in customer saves a delivery or billing address to their own address book.

Needs customers:write · changes data · Also callable from a published site (customer)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/addresses/actions/add_my_address
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.add_my_address

Input

FieldTypeRequiredNotes
labelstringLabel · max 60
firstNamestringFirst name · max 256
lastNamestringLast name · max 256
streetAddress1stringStreet · max 256
streetAddress2stringStreet (line 2) · max 256
cityAreastringDistrict · max 128
citystringCity · max 256
countryAreastringProvince · max 128
postalCodestringPostal code · max 20
countrystringCountry · max 2
phonephonePhone
isDefaultShippingbooleanDefault for delivery
isDefaultBillingbooleanDefault for billing

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
empty_addressAn address needs at least a street, a city or a postal code.

Events it publishes: customers.address_changed

What it writes: address (insert) — The address; customer_address (insert) — In their own book

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 empty_address · 429 slow down

customers.archive

Archive a customer — Takes the record out of lists and pickers without deleting anything.

Needs customers:delete · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/customers/{id}/actions/archive
API key or OAuth token · customers:delete
  • REST alias: DELETE /api/v1/customers/{id}
  • Canonical address: POST /api/v1/actions/customers.archive

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Customer · id of customer

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe record must exist.
already_mergedA merged record is already out of circulation.
already_archivedThe record must still be active.

Events it publishes: customers.archived

What it writes: customer (update) — Stamps archived_at

curl -X POST https://app.getera.io/api/v1/customers/CUSTOMER_ID/actions/archive \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.archived"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.customers.archive("CUSTOMER_ID")
era customers archive CUSTOMER_ID
tools/call
{
  "name": "era__customers__archive",
  "arguments": {
    "id": "CUSTOMER_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, already_merged, already_archived · 429 slow down

customers.capture

Add or recognise a customer — Resolves an email or phone number to one customer record, creating it only if new.

Needs customers:write · changes data

POSThttps://app.getera.io/api/v1/customers/actions/capture
API key or OAuth token · customers:write
  • REST alias: POST /api/v1/customers
  • Canonical address: POST /api/v1/actions/customers.capture

Input

FieldTypeRequiredNotes
namestringyesName · max 200
firstNamestringFirst name · max 100
lastNamestringLast name · max 100
phoneCountryCodenumberDialling code · min 1 · max 9999
countrystringCountry · ISO code, e.g. TR. · max 2
localestringLanguage · max 35
emailemailEmail
phonephonePhone
notesstringNotes · max 2000
kindstring (enum)Type · Allowed values: person, company
identitiesobject / arrayOther keys · Keys beyond email and phone: a channel id, an external reference, a company domain.

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
no_identityAt least one way of recognising the person is required.
unknown_identity_kindEvery key must be of a known kind.

Events it publishes: customers.created, customers.recognised

What it writes: customer (upsert) — Creates the customer when the identity is new; customer_identity (upsert) — Records every way this person reaches you

curl -X POST https://app.getera.io/api/v1/customers/actions/capture \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name":"Name"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.created",
    "customers.recognised"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.customers.create({
  name: "Name"
})
era customers create \
  --name "Name"
tools/call
{
  "name": "era__customers__capture",
  "arguments": {
    "name": "Name"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 no_identity, unknown_identity_kind · 429 slow down

customers.capture_site_guest

Recognise a website buyer — The checkout’s contact step: the signed-in customer, or a guest recognised by email, and the delivery address as this order’s own copy.

Needs customers:write · changes data · Also callable from a published site (visitor)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/customers/actions/capture_site_guest
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.capture_site_guest

Input

FieldTypeRequiredNotes
emailstringyesEmail · max 320
firstNamestringFirst name · max 100
lastNamestringLast name · max 100
phonestringPhone · max 40
streetAddress1stringStreet · max 256
streetAddress2stringStreet (line 2) · max 256
cityAreastringDistrict · max 128
citystringCity · max 256
countryAreastringProvince · max 128
postalCodestringPostal code · max 20
countrystringCountry · max 2

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
invalid_emailThe email must be usable.
name_missingA buyer gives a first and a last name.

Events it publishes: customers.recognised, customers.created

What it writes: customer (insert) — A new guest, when the email is not known; address (insert) — The delivery address, as the order’s own copy

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_email, name_missing · 429 slow down

customers.confirm_email_change

Confirm my new email — The code from the new inbox moves the site account and the customer record to that address.

Needs customers:write · reaches outside (sends, publishes or charges) · Also callable from a published site (customer)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/customer_login_codes/actions/confirm_email_change
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.confirm_email_change

Input

FieldTypeRequiredNotes
emailemailyesNew email
codestringyesCode · max 12

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_signed_inOnly a signed-in customer changes their email.
login_disabledThe business has switched this account off.
same_emailThe new address differs from the current one.
email_takenNo other account took the address in the meantime.
code_expiredA change code lives ten minutes.

Events it publishes: customers.login_changed, customers.updated

What it writes: customer_login_code (update) — Counts the attempt or uses the code up; customer_login (update) — Moves the sign-in to the new address; customer (update) — Their record gets the new address

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_signed_in, login_disabled, same_email, email_taken, code_expired · 429 slow down

customers.disable_login

Switch off site account — Stops the customer signing in to your website and signs them out everywhere. Their record and history stay.

Needs customers:manage · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/customer_logins/actions/disable_login
API key or OAuth token · customers:manage
  • Canonical address: POST /api/v1/actions/customers.disable_login

Input

FieldTypeRequiredNotes
iduuidyesCustomer · id of customer
reasonstringReason · max 500

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe customer must exist.
no_loginOnly a customer with a site account.
already_disabledNot one that is already switched off.

Events it publishes: customers.login_changed

What it writes: customer_login (update) — Switched off, with the reason; customer_session (update) — Every open session revoked

curl -X POST https://app.getera.io/api/v1/customer_logins/actions/disable_login \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"id":"CUSTOMER_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.login_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.customerLogins.disableLogin({
  id: "CUSTOMER_ID"
})
era customer_logins disable_login \
  --id "CUSTOMER_ID"
tools/call
{
  "name": "era__customers__disable_login",
  "arguments": {
    "id": "CUSTOMER_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, no_login, already_disabled · 429 slow down

customers.enable_login

Switch site account back on — Lets the customer sign in to your website again.

Needs customers:manage · changes data

POSThttps://app.getera.io/api/v1/customer_logins/actions/enable_login
API key or OAuth token · customers:manage
  • Canonical address: POST /api/v1/actions/customers.enable_login

Input

FieldTypeRequiredNotes
iduuidyesCustomer · id of customer

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe customer must exist.
no_loginOnly a customer with a site account.
not_disabledOnly one that is switched off.

Events it publishes: customers.login_changed

What it writes: customer_login (update) — Switched back on

curl -X POST https://app.getera.io/api/v1/customer_logins/actions/enable_login \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"id":"CUSTOMER_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.login_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.customerLogins.enableLogin({
  id: "CUSTOMER_ID"
})
era customer_logins enable_login \
  --id "CUSTOMER_ID"
tools/call
{
  "name": "era__customers__enable_login",
  "arguments": {
    "id": "CUSTOMER_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, no_login, not_disabled · 429 slow down

customers.export

Export customers — The customer list as filtered, as rows — the customers screen turns them into a CSV or Excel file.

Needs customers:export · reaches outside (sends, publishes or charges)

POSThttps://app.getera.io/api/v1/actions/customers.export
API key or OAuth token · customers:export

Input

FieldTypeRequiredNotes
qstringSearch · max 200
kindstring (enum)Kind · Allowed values: person, company
idsobject / arrayOnly these customers
excludeIdsobject / arrayLeave these customers out

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
invalid_filterA list of customers to keep or leave out must be customer ids.

Events it publishes: customers.exported

curl -X POST https://app.getera.io/api/v1/actions/customers.export \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"q":"Search"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.exported"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("customers.export", {
  q: "Search"
})
era run customers.export \
  --q "Search"
tools/call
{
  "name": "era__customers__export",
  "arguments": {
    "q": "Search"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:export · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_filter · 429 slow down

customers.merge

Merge two customers — Folds one record into another, moving its identities and history across.

Needs customers:manage · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/customers/{id}/actions/merge
API key or OAuth token · customers:manage
  • Canonical address: POST /api/v1/actions/customers.merge

Input

FieldTypeRequiredNotes
keepIduuidyesFilled from the path {id} · Keep · id of customer
mergeIduuidyesMerge away · id of customer
resolvedobject / arrayChosen values · Where the two records disagree, the value the survivor keeps: name, firstName, lastName, email, phone, phoneCountryCode, country, locale, notes.

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundBoth records must exist.
same_recordThe two records must be different.
already_mergedThe record being merged must still be live.
keep_is_mergedThe surviving record must still be live.
company_has_membersPeople attached to a company can only follow it into another company.

Events it publishes: customers.merged

What it writes: customer (update) — Tombstones the merged record and applies the chosen values; customer_identity (update) — Moves identities to the survivor; customer_address (update) — Moves the address book to the survivor

curl -X POST https://app.getera.io/api/v1/customers/CUSTOMER_ID/actions/merge \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"mergeId":"CUSTOMER_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.merged"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.customers.merge("CUSTOMER_ID", {
  mergeId: "CUSTOMER_ID"
})
era customers merge CUSTOMER_ID \
  --merge-id "CUSTOMER_ID"
tools/call
{
  "name": "era__customers__merge",
  "arguments": {
    "keepId": "CUSTOMER_ID",
    "mergeId": "CUSTOMER_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, same_record, already_merged, keep_is_merged, company_has_members · 429 slow down

customers.remove_address

Archive an address — Takes it out of the book without touching the documents that used it.

Needs customers:delete · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/addresses/{id}/actions/remove_address
API key or OAuth token · customers:delete
  • Canonical address: POST /api/v1/actions/customers.remove_address

Input

FieldTypeRequiredNotes
addressIduuidyesFilled from the path {id} · Address · id of address

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
address_not_foundThe address must exist.
address_is_snapshotA document’s copy belongs to the document.
already_archivedThe address must still be in the book.

Events it publishes: customers.address_changed

What it writes: address (update) — Stamps archived_at; customer_address (update) — Clears the defaults

curl -X POST https://app.getera.io/api/v1/addresses/ADDRESSE_ID/actions/remove_address \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.address_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.addresses.removeAddress("ADDRESSE_ID")
era addresses remove_address ADDRESSE_ID
tools/call
{
  "name": "era__customers__remove_address",
  "arguments": {
    "addressId": "ADDRESSE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 address_not_found, address_is_snapshot, already_archived · 429 slow down

customers.remove_identity

Remove a way to recognise this person — Drops one key, unless it is the only one left.

Needs customers:write · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/customer_identities/actions/remove_identity
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.remove_identity

Input

FieldTypeRequiredNotes
iduuidyesCustomer · id of customer
kindstring (enum)yesKind · Allowed values: email, phone, domain, external, app_user, channel
valuestringyesValue · max 320

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe record must exist.
already_mergedA merged record is edited through its survivor.
identity_not_foundThe key must be on this record.
last_identityA record must keep at least one way of being recognised.

Events it publishes: customers.identity_changed

What it writes: customer_identity (delete) — Removes the key

curl -X POST https://app.getera.io/api/v1/customer_identities/actions/remove_identity \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"id":"CUSTOMER_ID","kind":"email","value":"Value"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.identity_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.customerIdentities.removeIdentity({
  id: "CUSTOMER_ID",
  kind: "email",
  value: "Value"
})
era customer_identities remove_identity \
  --id "CUSTOMER_ID" \
  --kind "email" \
  --value "Value"
tools/call
{
  "name": "era__customers__remove_identity",
  "arguments": {
    "id": "CUSTOMER_ID",
    "kind": "email",
    "value": "Value"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, already_merged, identity_not_found, last_identity · 429 slow down

customers.remove_my_address

Remove an address from my account — A signed-in customer takes an address out of their own address book.

Needs customers:write · removes or cannot be undone · Also callable from a published site (customer)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/addresses/{id}/actions/remove_my_address
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.remove_my_address

Input

FieldTypeRequiredNotes
addressIduuidyesFilled from the path {id} · Address · id of address

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
address_not_on_customerOnly an address in their own book.

Events it publishes: customers.address_changed

What it writes: address (update) — Archived — orders that used it keep their copy; customer_address (update) — No longer a default

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 address_not_on_customer · 429 slow down

customers.request_email_change

Change my sign-in email — A signed-in customer asks to sign in with another address; a code goes to that address to prove it is theirs.

Needs customers:write · reaches outside (sends, publishes or charges) · Also callable from a published site (customer)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/customer_login_codes/actions/request_email_change
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.request_email_change

Input

FieldTypeRequiredNotes
emailemailyesNew email
sourceIpstringFrom address · max 64

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_signed_inOnly a signed-in customer changes their email.
login_disabledThe business has switched this account off.
same_emailThe new address differs from the current one.
email_takenNo other account signs in with the new address.
too_many_codesAt most five codes an hour go to one address.

Events it publishes: customers.sign_in_code_requested

What it writes: customer_login_code (insert) — Stores the hash of a ten-minute change code

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_signed_in, login_disabled, same_email, email_taken, too_many_codes · 429 slow down

customers.request_sign_in_code

Send a sign-in code — Emails a six-digit code a visitor types to sign in to the site — no password needed.

Needs customers:write · changes data · Also callable from a published site (visitor)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/customer_login_codes/actions/request_sign_in_code
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.request_sign_in_code

Input

FieldTypeRequiredNotes
emailemailyesEmail
sourceIpstringFrom address · max 64

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
login_disabledThe business has switched this account off.
too_many_codesAt most five codes an hour go to one address.

Events it publishes: customers.sign_in_code_requested

What it writes: customer_login_code (insert) — Stores the hash of a ten-minute code

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 login_disabled, too_many_codes · 429 slow down

customers.restore

Restore a customer — Brings an archived record back.

Needs customers:delete · changes data

POSThttps://app.getera.io/api/v1/customers/{id}/actions/restore
API key or OAuth token · customers:delete
  • Canonical address: POST /api/v1/actions/customers.restore

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Customer · id of customer

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe record must exist.
already_mergedA merged record is restored through its survivor.
not_archivedThe record must be archived.

Events it publishes: customers.restored

What it writes: customer (update) — Clears archived_at

curl -X POST https://app.getera.io/api/v1/customers/CUSTOMER_ID/actions/restore \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.restored"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.customers.restore("CUSTOMER_ID")
era customers restore CUSTOMER_ID
tools/call
{
  "name": "era__customers__restore",
  "arguments": {
    "id": "CUSTOMER_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, already_merged, not_archived · 429 slow down

Send a site sign-in link — Emails the customer a link to sign in to your website — the first time, or when they cannot get in. They get their own code there.

Needs customers:write · reaches outside (sends, publishes or charges)

POSThttps://app.getera.io/api/v1/actions/customers.send_sign_in_link
API key or OAuth token · customers:write
FieldTypeRequiredNotes
iduuidyesCustomer · id of customer

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe customer must exist.
already_mergedA merged record is handled through its survivor.
no_emailThe customer needs an email address.
login_disabledA switched-off account gets no link until it is switched back on.

Events it publishes: customers.sign_in_link_sent

curl -X POST https://app.getera.io/api/v1/actions/customers.send_sign_in_link \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"id":"CUSTOMER_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.sign_in_link_sent"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.actions.run("customers.send_sign_in_link", {
  id: "CUSTOMER_ID"
})
era run customers.send_sign_in_link \
  --id "CUSTOMER_ID"
tools/call
{
  "name": "era__customers__send_sign_in_link",
  "arguments": {
    "id": "CUSTOMER_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, already_merged, no_email, login_disabled · 429 slow down

customers.set_default_address

Set a default address — Which address is used for delivery or for billing unless told otherwise.

Needs customers:write · changes data

POSThttps://app.getera.io/api/v1/customer_addresses/actions/set_default_address
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.set_default_address

Input

FieldTypeRequiredNotes
iduuidyesCustomer · id of customer
addressIduuidyesAddress · id of address
shippingbooleanDefault for delivery
billingbooleanDefault for billing

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe record must exist.
already_mergedA merged record is edited through its survivor.
address_not_on_customerThe address must be in this person’s book.
address_archivedAn archived address cannot be the default.
nothing_to_setSay which default is meant.

Events it publishes: customers.address_default_changed

What it writes: customer_address (update) — Moves the default

curl -X POST https://app.getera.io/api/v1/customer_addresses/actions/set_default_address \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"id":"CUSTOMER_ID","addressId":"ADDRESS_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.address_default_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.customerAddresses.setDefaultAddress({
  id: "CUSTOMER_ID",
  addressId: "ADDRESS_ID"
})
era customer_addresses set_default_address \
  --id "CUSTOMER_ID" \
  --address-id "ADDRESS_ID"
tools/call
{
  "name": "era__customers__set_default_address",
  "arguments": {
    "id": "CUSTOMER_ID",
    "addressId": "ADDRESS_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, already_merged, address_not_on_customer, address_archived, nothing_to_set · 429 slow down

customers.set_my_password

Set my password — A signed-in customer sets or changes the password for their site account.

Needs customers:write · changes data · Also callable from a published site (customer)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/customer_logins/actions/set_my_password
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.set_my_password

Input

FieldTypeRequiredNotes
passwordstringyesNew password · max 200
currentPasswordstringCurrent password · max 200

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_signed_inOnly a signed-in customer changes their own password.
password_too_shortAt least eight characters.
wrong_passwordChanging a password needs the current one.

Events it publishes: customers.login_changed

What it writes: customer_login (update) — Stores the scrypt hash of the password

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_signed_in, password_too_short, wrong_password · 429 slow down

customers.set_parent

Attach to a company — Says which company this person belongs to, or detaches them.

Needs customers:write · changes data

POSThttps://app.getera.io/api/v1/customers/{id}/actions/set_parent
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.set_parent

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Customer · id of customer
parentIduuidCompany · id of customer

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundBoth records must exist.
already_mergedA merged record is edited through its survivor.
self_parentA record cannot belong to itself.
parent_not_a_companyOnly a company can have people attached to it.
parent_cycleTwo companies cannot contain each other.

Events it publishes: customers.parent_changed

What it writes: customer (update) — Sets the company link

curl -X POST https://app.getera.io/api/v1/customers/CUSTOMER_ID/actions/set_parent \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"parentId":"CUSTOMER_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.parent_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.customers.setParent("CUSTOMER_ID", {
  parentId: "CUSTOMER_ID"
})
era customers set_parent CUSTOMER_ID \
  --parent-id "CUSTOMER_ID"
tools/call
{
  "name": "era__customers__set_parent",
  "arguments": {
    "id": "CUSTOMER_ID",
    "parentId": "CUSTOMER_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, already_merged, self_parent, parent_not_a_company, parent_cycle · 429 slow down

customers.sign_in_with_password

Sign in with a password — Signs a customer in to the site with the password they set on their account.

Needs customers:write · changes data · Also callable from a published site (visitor)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/customer_logins/actions/sign_in_with_password
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.sign_in_with_password

Input

FieldTypeRequiredNotes
emailemailyesEmail
passwordstringyesPassword · max 200
sourceIpstringFrom address · max 64
userAgentstringBrowser · max 400

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
wrong_credentialsThe email and password must match an account with a password.
login_disabledThe business has switched this account off.

Events it publishes: customers.signed_in

What it writes: customer_session (insert) — A thirty-day site session; customer_login (update) — Last sign-in

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 wrong_credentials, login_disabled · 429 slow down

customers.sign_out

Sign out — Ends the customer’s site session on this device.

Needs customers:write · changes data · Also callable from a published site (customer)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/actions/customers.sign_out
API key or OAuth token · customers:write

Input

FieldTypeRequiredNotes
sessionIduuidyesSession

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
session_not_foundThe session must be theirs and still open.

Events it publishes: customers.signed_out

What it writes: customer_session (update) — Revoked

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 session_not_found · 429 slow down

customers.update

Edit a customer — Changes the person’s details and tells the rest of the system.

Needs customers:write · changes data

POSThttps://app.getera.io/api/v1/customers/{id}/actions/update
API key or OAuth token · customers:write
  • REST alias: PATCH /api/v1/customers/{id}
  • Canonical address: POST /api/v1/actions/customers.update

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Customer · id of customer
namestringName · max 200
firstNamestringFirst name · max 100
lastNamestringLast name · max 100
phoneCountryCodenumberDialling code · min 1 · max 9999
countrystringCountry · ISO code, e.g. TR. · max 2
localestringLanguage · max 35
emailemailEmail
phonephonePhone
notesstringNotes · max 2000
kindstring (enum)Type · Allowed values: person, company

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe record must exist.
already_mergedA merged record is edited through its survivor.
company_has_membersA company with people attached cannot become a person.

Events it publishes: customers.updated

What it writes: customer (update) — Updates the record

curl -X POST https://app.getera.io/api/v1/customers/CUSTOMER_ID/actions/update \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name":"Name"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.updated"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.customers.update("CUSTOMER_ID", {
  name: "Name"
})
era customers update CUSTOMER_ID \
  --name "Name"
tools/call
{
  "name": "era__customers__update",
  "arguments": {
    "id": "CUSTOMER_ID",
    "name": "Name"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, already_merged, company_has_members · 429 slow down

customers.update_address

Edit an address — Changes a book address. The copy frozen on a document does not move.

Needs customers:write · changes data

POSThttps://app.getera.io/api/v1/addresses/{id}/actions/update_address
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.update_address

Input

FieldTypeRequiredNotes
addressIduuidyesFilled from the path {id} · Address · id of address
labelstringLabel · max 60
firstNamestringFirst name · max 256
lastNamestringLast name · max 256
companyNamestringCompany · max 256
streetAddress1stringStreet · max 256
streetAddress2stringStreet (line 2) · max 256
cityAreastringDistrict · max 128
citystringCity · max 256
countryAreastringProvince / state · max 128
postalCodestringPostal code · max 20
countrystringCountry · ISO code, e.g. TR. · max 2
phonephonePhone
rawstringAddress as written · The bridge for an address nobody has split yet, and for countries with no schema. · max 2000
validationSkippedbooleanAccepted without validation

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
address_not_foundThe address must exist.
address_is_snapshotA document’s copy of an address never changes.
empty_addressAn address with nothing in it is not a record.

Events it publishes: customers.address_changed

What it writes: address (update) — Updates the address; customer_address (update) — Updates its label

curl -X POST https://app.getera.io/api/v1/addresses/ADDRESSE_ID/actions/update_address \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"label":"Label"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "customers.address_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.addresses.updateAddress("ADDRESSE_ID", {
  label: "Label"
})
era addresses update_address ADDRESSE_ID \
  --label "Label"
tools/call
{
  "name": "era__customers__update_address",
  "arguments": {
    "addressId": "ADDRESSE_ID",
    "label": "Label"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 address_not_found, address_is_snapshot, empty_address · 429 slow down

customers.update_my_profile

Update my details — A signed-in customer corrects their own name, phone and language.

Needs customers:write · changes data · Also callable from a published site (customer)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/customers/actions/update_my_profile
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.update_my_profile

Input

FieldTypeRequiredNotes
firstNamestringFirst name · max 100
lastNamestringLast name · max 100
phonephonePhone
localestringLanguage · max 35

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
name_requiredA first name cannot be cleared.

Events it publishes: customers.updated

What it writes: customer (update) — Their own record

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 name_required · 429 slow down

customers.verify_sign_in_code

Sign in with a code — Checks the emailed code and signs the visitor in — making their account the first time, attached to the customer the business already knows.

Needs customers:write · changes data · Also callable from a published site (visitor)

A website action: your site calls it through the site API (/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).
POSThttps://app.getera.io/api/v1/customer_logins/actions/verify_sign_in_code
API key or OAuth token · customers:write
  • Canonical address: POST /api/v1/actions/customers.verify_sign_in_code

Input

FieldTypeRequiredNotes
emailemailyesEmail
codestringyesCode · max 12
namestringName · max 200
acceptTermsbooleanAccepted the privacy notice
sourceIpstringFrom address · max 64
userAgentstringBrowser · max 400

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
code_expiredA code works for ten minutes and once.
login_disabledThe business has switched this account off.

Events it publishes: customers.signed_in, customers.sign_in_failed, customers.login_created, customers.created, customers.recognised

What it writes: customer_login_code (update) — Counts the attempt or uses the code up; customer_login (upsert) — Makes the login the first time; marks the email verified; customer (insert) — A new customer when nobody with this email is known; customer_identity (insert) — The email, as a way of recognising them; customer_session (insert) — A thirty-day site session

Errors: 401 no or expired credential · 403 the credential does not hold customers:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 code_expired, login_disabled · 429 slow down

Data models

Address

Table address · Resource addresses

ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
first_namestring · textyes
last_namestring · textyes
company_namestring · textyes
street_address_1string · textyes
street_address_2string · textyes
city_areastring · textyes
citystring · textyes
country_areastring · textyes
postal_codestring · textyes
countrystring · bpcharyesmax 2
phonestring · textyes
rawstring · textyes
validation_skippedboolean · bool
is_snapshotboolean · bool
archived_atstring (date-time) · timestamptzyes
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Checks:

  • CHECK ((COALESCE(street_address_1, city, postal_code, raw, company_name) IS NOT NULL))
  • CHECK ((NOT (is_snapshot AND (archived_at IS NOT NULL))))

CustomerAddress

Table customer_address · Resource customer_addresses

  • GET /api/v1/customer_addresses
  • GET /api/v1/customer_addresses/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
customer_idstring (uuid) · uuidcustomer
address_idstring (uuid) · uuidaddress
is_default_shippingboolean · bool
is_default_billingboolean · bool
labelstring · textyes
created_atstring (date-time) · timestamptz

Primary key: id

Unique: customer_id, address_id

CustomerIdentity

Table customer_identity · Resource customer_identities

  • GET /api/v1/customer_identities
  • GET /api/v1/customer_identities/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
customer_idstring (uuid) · uuidcustomer
kindstring · textemail, phone, domain, external, app_user, channel
valuestring · text
sourcestring · text
created_atstring (date-time) · timestamptz

Primary key: id

Checks:

  • CHECK (( CASE kind WHEN 'email'::text THEN (value = lower(value)) WHEN 'domain'::text THEN (value = lower(value)) ELSE true END AND (value = btrim(value)) AND (length(value) > 0)))

CustomerLoginCode

Table customer_login_code · Resource customer_login_codes

  • GET /api/v1/customer_login_codes
  • GET /api/v1/customer_login_codes/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
site_idstring (uuid) · uuidyessite
login_idstring (uuid) · uuidyescustomer_login
channelstring · textemail, sms
destinationstring · text
attemptsinteger · int4
expires_atstring (date-time) · timestamptz
consumed_atstring (date-time) · timestamptzyes
source_ipstring · textyes
created_atstring (date-time) · timestamptz
purposestring · textsign_in, email_change

Primary key: id

CustomerLogin

Table customer_login · Resource customer_logins

  • GET /api/v1/customer_logins
  • GET /api/v1/customer_logins/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
customer_idstring (uuid) · uuidcustomer
emailstring · textyes
phonestring · textyes
email_verified_atstring (date-time) · timestamptzyes
phone_verified_atstring (date-time) · timestamptzyes
terms_accepted_atstring (date-time) · timestamptzyes
disabled_atstring (date-time) · timestamptzyes
disabled_reasonstring · textyes
last_sign_in_atstring (date-time) · timestamptzyes
sign_in_countinteger · int4
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Checks:

  • CHECK (((email IS NOT NULL) OR (phone IS NOT NULL)))
  • CHECK (((email IS NULL) OR ((email = lower(btrim(email))) AND (POSITION(('@'::text) IN (email)) > 1))))
  • CHECK (((disabled_reason IS NULL) OR (length(disabled_reason) <= 500)))

Customer

Table customer · Resource customers

ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
kindstring · textperson, company
namestring · text
emailstring · textyes
phonestring · textyes
notesstring · textyes
owner_user_idstring (uuid) · uuidyesapp_user
merged_into_idstring (uuid) · uuidyescustomer
archived_atstring (date-time) · timestamptzyes
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz
first_namestring · textyes
last_namestring · textyes
phone_country_codeinteger · int2yes
countrystring · bpcharyesmax 2
parent_customer_idstring (uuid) · uuidyescustomer
localestring · textyes
avatar_file_idstring (uuid) · uuidyesfile
anonymized_atstring (date-time) · timestamptzyes

Primary key: id

Events

Workflows and webhooks react to these events.

EventWhat happensPayload
customers.address_changedAn address was added, edited or archived.id, addressId, added
customers.address_default_changedThe default delivery or billing address moved.id, addressId, shipping, billing
customers.archivedA customer was taken out of circulation.id
customers.createdA new customer was added.id, name
customers.exportedThe customer list was taken out as a file.count, truncated
customers.identity_changedA way of recognising someone was added or removed.id, kind, added, removed
customers.login_changedA site account was switched off or on, or its password changed.id, customerId, change
customers.login_createdA customer made a site account.id, customerId, recognised
customers.mergedTwo records were folded into one.id, mergedId
customers.parent_changedSomeone was attached to or detached from a company.id, parentId
customers.recognisedAn arrival matched an existing customer.id
customers.restoredAn archived customer was brought back.id
customers.sign_in_code_requestedA site sign-in code was sent.loginId, codeId
customers.sign_in_failedA wrong sign-in code was typed.codeId, burnt
customers.sign_in_link_sentA member emailed a customer the link to sign in to the site.id, loginId
customers.signed_inA customer signed in to the site.loginId, customerId, method
customers.signed_outA customer signed out of the site.customerId, sessionId
customers.updatedA customer’s details changed.id, name