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
| Resource | Table | List and read | POST | PATCH | DELETE |
|---|---|---|---|---|---|
addresses | address | /api/v1/addresses | customers.add_address | ||
customer_addresses | customer_address | /api/v1/customer_addresses | |||
customer_identities | customer_identity | /api/v1/customer_identities | |||
customer_login_codes | customer_login_code | /api/v1/customer_login_codes | |||
customer_logins | customer_login | /api/v1/customer_logins | |||
customers | customer | /api/v1/customers | customers.capture | customers.update | customers.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
| Action | Needs | What it does |
|---|---|---|
customers.add_address | customers:write | Add an address |
customers.add_identity | customers:write | Add a way to recognise this person |
customers.add_my_address | customers:write | Add an address to my account |
customers.archive | customers:delete | Archive a customer |
customers.capture | customers:write | Add or recognise a customer |
customers.capture_site_guest | customers:write | Recognise a website buyer |
customers.confirm_email_change | customers:write | Confirm my new email |
customers.disable_login | customers:manage | Switch off site account |
customers.enable_login | customers:manage | Switch site account back on |
customers.export | customers:export | Export customers |
customers.merge | customers:manage | Merge two customers |
customers.remove_address | customers:delete | Archive an address |
customers.remove_identity | customers:write | Remove a way to recognise this person |
customers.remove_my_address | customers:write | Remove an address from my account |
customers.request_email_change | customers:write | Change my sign-in email |
customers.request_sign_in_code | customers:write | Send a sign-in code |
customers.restore | customers:delete | Restore a customer |
customers.send_sign_in_link | customers:write | Send a site sign-in link |
customers.set_default_address | customers:write | Set a default address |
customers.set_my_password | customers:write | Set my password |
customers.set_parent | customers:write | Attach to a company |
customers.sign_in_with_password | customers:write | Sign in with a password |
customers.sign_out | customers:write | Sign out |
customers.update | customers:write | Edit a customer |
customers.update_address | customers:write | Edit an address |
customers.update_my_profile | customers:write | Update my details |
customers.verify_sign_in_code | customers:write | Sign 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
https://app.getera.io/api/v1/addresses/actions/add_address- REST alias:
POST /api/v1/addresses - Canonical address:
POST /api/v1/actions/customers.add_address
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Customer · id of customer |
label | string | Label · Home, Office — what the picker shows. · max 60 | |
firstName | string | First name · max 256 | |
lastName | string | Last name · max 256 | |
companyName | string | Company · max 256 | |
streetAddress1 | string | Street · max 256 | |
streetAddress2 | string | Street (line 2) · max 256 | |
cityArea | string | District · max 128 | |
city | string | City · max 256 | |
countryArea | string | Province / state · max 128 | |
postalCode | string | Postal code · max 20 | |
country | string | Country · ISO code, e.g. TR. · max 2 | |
phone | phone | Phone | |
raw | string | Address as written · The bridge for an address nobody has split yet, and for countries with no schema. · max 2000 | |
validationSkipped | boolean | Accepted without validation | |
isDefaultShipping | boolean | Default for delivery | |
isDefaultBilling | boolean | Default for billing |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The record must exist. |
already_merged | A merged record is edited through its survivor. |
empty_address | An 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"}'{
"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"{
"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
https://app.getera.io/api/v1/customer_identities/actions/add_identity- Canonical address:
POST /api/v1/actions/customers.add_identity
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Customer · id of customer |
kind | string (enum) | yes | Kind · Allowed values: email, phone, domain, external, app_user, channel |
value | string | yes | Value · max 320 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The record must exist. |
already_merged | A merged record is edited through its survivor. |
empty_identity | The value must not be empty. |
identity_already_present | The record must not already carry that key. |
identity_taken | A 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"}'{
"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"{
"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)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/addresses/actions/add_my_address- Canonical address:
POST /api/v1/actions/customers.add_my_address
Input
| Field | Type | Required | Notes |
|---|---|---|---|
label | string | Label · max 60 | |
firstName | string | First name · max 256 | |
lastName | string | Last name · max 256 | |
streetAddress1 | string | Street · max 256 | |
streetAddress2 | string | Street (line 2) · max 256 | |
cityArea | string | District · max 128 | |
city | string | City · max 256 | |
countryArea | string | Province · max 128 | |
postalCode | string | Postal code · max 20 | |
country | string | Country · max 2 | |
phone | phone | Phone | |
isDefaultShipping | boolean | Default for delivery | |
isDefaultBilling | boolean | Default for billing |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
empty_address | An 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
https://app.getera.io/api/v1/customers/{id}/actions/archive- REST alias:
DELETE /api/v1/customers/{id} - Canonical address:
POST /api/v1/actions/customers.archive
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Customer · id of customer |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The record must exist. |
already_merged | A merged record is already out of circulation. |
already_archived | The 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 '{}'{
"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{
"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
https://app.getera.io/api/v1/customers/actions/capture- REST alias:
POST /api/v1/customers - Canonical address:
POST /api/v1/actions/customers.capture
Input
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | Name · max 200 |
firstName | string | First name · max 100 | |
lastName | string | Last name · max 100 | |
phoneCountryCode | number | Dialling code · min 1 · max 9999 | |
country | string | Country · ISO code, e.g. TR. · max 2 | |
locale | string | Language · max 35 | |
email | |||
phone | phone | Phone | |
notes | string | Notes · max 2000 | |
kind | string (enum) | Type · Allowed values: person, company | |
identities | object / array | Other 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:
| Code | Refused when |
|---|---|
no_identity | At least one way of recognising the person is required. |
unknown_identity_kind | Every 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"}'{
"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"{
"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)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/customers/actions/capture_site_guest- Canonical address:
POST /api/v1/actions/customers.capture_site_guest
Input
| Field | Type | Required | Notes |
|---|---|---|---|
email | string | yes | Email · max 320 |
firstName | string | First name · max 100 | |
lastName | string | Last name · max 100 | |
phone | string | Phone · max 40 | |
streetAddress1 | string | Street · max 256 | |
streetAddress2 | string | Street (line 2) · max 256 | |
cityArea | string | District · max 128 | |
city | string | City · max 256 | |
countryArea | string | Province · max 128 | |
postalCode | string | Postal code · max 20 | |
country | string | Country · max 2 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_email | The email must be usable. |
name_missing | A 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)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/customer_login_codes/actions/confirm_email_change- Canonical address:
POST /api/v1/actions/customers.confirm_email_change
Input
| Field | Type | Required | Notes |
|---|---|---|---|
email | yes | New email | |
code | string | yes | Code · max 12 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_signed_in | Only a signed-in customer changes their email. |
login_disabled | The business has switched this account off. |
same_email | The new address differs from the current one. |
email_taken | No other account took the address in the meantime. |
code_expired | A 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
https://app.getera.io/api/v1/customer_logins/actions/disable_login- Canonical address:
POST /api/v1/actions/customers.disable_login
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Customer · id of customer |
reason | string | Reason · max 500 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The customer must exist. |
no_login | Only a customer with a site account. |
already_disabled | Not 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"}'{
"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"{
"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
https://app.getera.io/api/v1/customer_logins/actions/enable_login- Canonical address:
POST /api/v1/actions/customers.enable_login
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Customer · id of customer |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The customer must exist. |
no_login | Only a customer with a site account. |
not_disabled | Only 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"}'{
"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"{
"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)
https://app.getera.io/api/v1/actions/customers.exportInput
| Field | Type | Required | Notes |
|---|---|---|---|
q | string | Search · max 200 | |
kind | string (enum) | Kind · Allowed values: person, company | |
ids | object / array | Only these customers | |
excludeIds | object / array | Leave these customers out |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_filter | A 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"}'{
"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"{
"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
https://app.getera.io/api/v1/customers/{id}/actions/merge- Canonical address:
POST /api/v1/actions/customers.merge
Input
| Field | Type | Required | Notes |
|---|---|---|---|
keepId | uuid | yes | Filled from the path {id} · Keep · id of customer |
mergeId | uuid | yes | Merge away · id of customer |
resolved | object / array | Chosen 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:
| Code | Refused when |
|---|---|
not_found | Both records must exist. |
same_record | The two records must be different. |
already_merged | The record being merged must still be live. |
keep_is_merged | The surviving record must still be live. |
company_has_members | People 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"}'{
"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"{
"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
https://app.getera.io/api/v1/addresses/{id}/actions/remove_address- Canonical address:
POST /api/v1/actions/customers.remove_address
Input
| Field | Type | Required | Notes |
|---|---|---|---|
addressId | uuid | yes | Filled from the path {id} · Address · id of address |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
address_not_found | The address must exist. |
address_is_snapshot | A document’s copy belongs to the document. |
already_archived | The 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 '{}'{
"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{
"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
https://app.getera.io/api/v1/customer_identities/actions/remove_identity- Canonical address:
POST /api/v1/actions/customers.remove_identity
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Customer · id of customer |
kind | string (enum) | yes | Kind · Allowed values: email, phone, domain, external, app_user, channel |
value | string | yes | Value · max 320 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The record must exist. |
already_merged | A merged record is edited through its survivor. |
identity_not_found | The key must be on this record. |
last_identity | A 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"}'{
"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"{
"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)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/addresses/{id}/actions/remove_my_address- Canonical address:
POST /api/v1/actions/customers.remove_my_address
Input
| Field | Type | Required | Notes |
|---|---|---|---|
addressId | uuid | yes | Filled from the path {id} · Address · id of address |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
address_not_on_customer | Only 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)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/customer_login_codes/actions/request_email_change- Canonical address:
POST /api/v1/actions/customers.request_email_change
Input
| Field | Type | Required | Notes |
|---|---|---|---|
email | yes | New email | |
sourceIp | string | From address · max 64 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_signed_in | Only a signed-in customer changes their email. |
login_disabled | The business has switched this account off. |
same_email | The new address differs from the current one. |
email_taken | No other account signs in with the new address. |
too_many_codes | At 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)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/customer_login_codes/actions/request_sign_in_code- Canonical address:
POST /api/v1/actions/customers.request_sign_in_code
Input
| Field | Type | Required | Notes |
|---|---|---|---|
email | yes | ||
sourceIp | string | From address · max 64 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
login_disabled | The business has switched this account off. |
too_many_codes | At 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
https://app.getera.io/api/v1/customers/{id}/actions/restore- Canonical address:
POST /api/v1/actions/customers.restore
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Customer · id of customer |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The record must exist. |
already_merged | A merged record is restored through its survivor. |
not_archived | The 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 '{}'{
"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{
"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
customers.send_sign_in_link
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)
https://app.getera.io/api/v1/actions/customers.send_sign_in_linkInput
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Customer · id of customer |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The customer must exist. |
already_merged | A merged record is handled through its survivor. |
no_email | The customer needs an email address. |
login_disabled | A 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"}'{
"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"{
"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
https://app.getera.io/api/v1/customer_addresses/actions/set_default_address- Canonical address:
POST /api/v1/actions/customers.set_default_address
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Customer · id of customer |
addressId | uuid | yes | Address · id of address |
shipping | boolean | Default for delivery | |
billing | boolean | Default for billing |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The record must exist. |
already_merged | A merged record is edited through its survivor. |
address_not_on_customer | The address must be in this person’s book. |
address_archived | An archived address cannot be the default. |
nothing_to_set | Say 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"}'{
"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"{
"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)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/customer_logins/actions/set_my_password- Canonical address:
POST /api/v1/actions/customers.set_my_password
Input
| Field | Type | Required | Notes |
|---|---|---|---|
password | string | yes | New password · max 200 |
currentPassword | string | Current password · max 200 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_signed_in | Only a signed-in customer changes their own password. |
password_too_short | At least eight characters. |
wrong_password | Changing 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
https://app.getera.io/api/v1/customers/{id}/actions/set_parent- Canonical address:
POST /api/v1/actions/customers.set_parent
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Customer · id of customer |
parentId | uuid | Company · id of customer |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | Both records must exist. |
already_merged | A merged record is edited through its survivor. |
self_parent | A record cannot belong to itself. |
parent_not_a_company | Only a company can have people attached to it. |
parent_cycle | Two 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"}'{
"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"{
"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)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/customer_logins/actions/sign_in_with_password- Canonical address:
POST /api/v1/actions/customers.sign_in_with_password
Input
| Field | Type | Required | Notes |
|---|---|---|---|
email | yes | ||
password | string | yes | Password · max 200 |
sourceIp | string | From address · max 64 | |
userAgent | string | Browser · max 400 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
wrong_credentials | The email and password must match an account with a password. |
login_disabled | The 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)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/actions/customers.sign_outInput
| Field | Type | Required | Notes |
|---|---|---|---|
sessionId | uuid | yes | Session |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
session_not_found | The 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
https://app.getera.io/api/v1/customers/{id}/actions/update- REST alias:
PATCH /api/v1/customers/{id} - Canonical address:
POST /api/v1/actions/customers.update
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Customer · id of customer |
name | string | Name · max 200 | |
firstName | string | First name · max 100 | |
lastName | string | Last name · max 100 | |
phoneCountryCode | number | Dialling code · min 1 · max 9999 | |
country | string | Country · ISO code, e.g. TR. · max 2 | |
locale | string | Language · max 35 | |
email | |||
phone | phone | Phone | |
notes | string | Notes · max 2000 | |
kind | string (enum) | Type · Allowed values: person, company |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The record must exist. |
already_merged | A merged record is edited through its survivor. |
company_has_members | A 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"}'{
"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"{
"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
https://app.getera.io/api/v1/addresses/{id}/actions/update_address- Canonical address:
POST /api/v1/actions/customers.update_address
Input
| Field | Type | Required | Notes |
|---|---|---|---|
addressId | uuid | yes | Filled from the path {id} · Address · id of address |
label | string | Label · max 60 | |
firstName | string | First name · max 256 | |
lastName | string | Last name · max 256 | |
companyName | string | Company · max 256 | |
streetAddress1 | string | Street · max 256 | |
streetAddress2 | string | Street (line 2) · max 256 | |
cityArea | string | District · max 128 | |
city | string | City · max 256 | |
countryArea | string | Province / state · max 128 | |
postalCode | string | Postal code · max 20 | |
country | string | Country · ISO code, e.g. TR. · max 2 | |
phone | phone | Phone | |
raw | string | Address as written · The bridge for an address nobody has split yet, and for countries with no schema. · max 2000 | |
validationSkipped | boolean | Accepted without validation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
address_not_found | The address must exist. |
address_is_snapshot | A document’s copy of an address never changes. |
empty_address | An 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"}'{
"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"{
"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)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/customers/actions/update_my_profile- Canonical address:
POST /api/v1/actions/customers.update_my_profile
Input
| Field | Type | Required | Notes |
|---|---|---|---|
firstName | string | First name · max 100 | |
lastName | string | Last name · max 100 | |
phone | phone | Phone | |
locale | string | Language · max 35 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
name_required | A 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)
/api/site/v1/{siteId}, e.g. with @getera/storefront). With an API key or a token it is refused (403 site_only).https://app.getera.io/api/v1/customer_logins/actions/verify_sign_in_code- Canonical address:
POST /api/v1/actions/customers.verify_sign_in_code
Input
| Field | Type | Required | Notes |
|---|---|---|---|
email | yes | ||
code | string | yes | Code · max 12 |
name | string | Name · max 200 | |
acceptTerms | boolean | Accepted the privacy notice | |
sourceIp | string | From address · max 64 | |
userAgent | string | Browser · max 400 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
code_expired | A code works for ten minutes and once. |
login_disabled | The 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
GET /api/v1/addressesGET /api/v1/addresses/{id}POST /api/v1/addresses→customers.add_address
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
first_name | string · text | yes | ||
last_name | string · text | yes | ||
company_name | string · text | yes | ||
street_address_1 | string · text | yes | ||
street_address_2 | string · text | yes | ||
city_area | string · text | yes | ||
city | string · text | yes | ||
country_area | string · text | yes | ||
postal_code | string · text | yes | ||
country | string · bpchar | yes | max 2 | |
phone | string · text | yes | ||
raw | string · text | yes | ||
validation_skipped | boolean · bool | |||
is_snapshot | boolean · bool | |||
archived_at | string (date-time) · timestamptz | yes | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (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_addressesGET /api/v1/customer_addresses/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
customer_id | string (uuid) · uuid | customer | ||
address_id | string (uuid) · uuid | address | ||
is_default_shipping | boolean · bool | |||
is_default_billing | boolean · bool | |||
label | string · text | yes | ||
created_at | string (date-time) · timestamptz |
Primary key: id
Unique: customer_id, address_id
CustomerIdentity
Table customer_identity · Resource customer_identities
GET /api/v1/customer_identitiesGET /api/v1/customer_identities/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
customer_id | string (uuid) · uuid | customer | ||
kind | string · text | email, phone, domain, external, app_user, channel | ||
value | string · text | |||
source | string · text | |||
created_at | string (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_codesGET /api/v1/customer_login_codes/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
site_id | string (uuid) · uuid | yes | site | |
login_id | string (uuid) · uuid | yes | customer_login | |
channel | string · text | email, sms | ||
destination | string · text | |||
attempts | integer · int4 | |||
expires_at | string (date-time) · timestamptz | |||
consumed_at | string (date-time) · timestamptz | yes | ||
source_ip | string · text | yes | ||
created_at | string (date-time) · timestamptz | |||
purpose | string · text | sign_in, email_change |
Primary key: id
CustomerLogin
Table customer_login · Resource customer_logins
GET /api/v1/customer_loginsGET /api/v1/customer_logins/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
customer_id | string (uuid) · uuid | customer | ||
email | string · text | yes | ||
phone | string · text | yes | ||
email_verified_at | string (date-time) · timestamptz | yes | ||
phone_verified_at | string (date-time) · timestamptz | yes | ||
terms_accepted_at | string (date-time) · timestamptz | yes | ||
disabled_at | string (date-time) · timestamptz | yes | ||
disabled_reason | string · text | yes | ||
last_sign_in_at | string (date-time) · timestamptz | yes | ||
sign_in_count | integer · int4 | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (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
GET /api/v1/customersGET /api/v1/customers/{id}POST /api/v1/customers→customers.capturePATCH /api/v1/customers/{id}→customers.updateDELETE /api/v1/customers/{id}→customers.archive
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
kind | string · text | person, company | ||
name | string · text | |||
email | string · text | yes | ||
phone | string · text | yes | ||
notes | string · text | yes | ||
owner_user_id | string (uuid) · uuid | yes | app_user | |
merged_into_id | string (uuid) · uuid | yes | customer | |
archived_at | string (date-time) · timestamptz | yes | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
first_name | string · text | yes | ||
last_name | string · text | yes | ||
phone_country_code | integer · int2 | yes | ||
country | string · bpchar | yes | max 2 | |
parent_customer_id | string (uuid) · uuid | yes | customer | |
locale | string · text | yes | ||
avatar_file_id | string (uuid) · uuid | yes | file | |
anonymized_at | string (date-time) · timestamptz | yes |
Primary key: id
Events
Workflows and webhooks react to these events.
| Event | What happens | Payload |
|---|---|---|
customers.address_changed | An address was added, edited or archived. | id, addressId, added |
customers.address_default_changed | The default delivery or billing address moved. | id, addressId, shipping, billing |
customers.archived | A customer was taken out of circulation. | id |
customers.created | A new customer was added. | id, name |
customers.exported | The customer list was taken out as a file. | count, truncated |
customers.identity_changed | A way of recognising someone was added or removed. | id, kind, added, removed |
customers.login_changed | A site account was switched off or on, or its password changed. | id, customerId, change |
customers.login_created | A customer made a site account. | id, customerId, recognised |
customers.merged | Two records were folded into one. | id, mergedId |
customers.parent_changed | Someone was attached to or detached from a company. | id, parentId |
customers.recognised | An arrival matched an existing customer. | id |
customers.restored | An archived customer was brought back. | id |
customers.sign_in_code_requested | A site sign-in code was sent. | loginId, codeId |
customers.sign_in_failed | A wrong sign-in code was typed. | codeId, burnt |
customers.sign_in_link_sent | A member emailed a customer the link to sign in to the site. | id, loginId |
customers.signed_in | A customer signed in to the site. | loginId, customerId, method |
customers.signed_out | A customer signed out of the site. | customerId, sessionId |
customers.updated | A customer’s details changed. | id, name |