Customers
Customer identity, contact channels, service history, and organization-scoped records.
/api/public/v1/customersGET/api/public/v1/customersList customers in the active organization with filtering and pagination.
/api/public/v1/customersList customers in the active organization with filtering and pagination.Required permissions
read:customers
200Request parameters
limitQuery · integer · Optional- Maximum records per page, from 1 to 100 (default 25).
offsetQuery · integer · Optional- Number of matching records to skip.
searchQuery · string · Optional- Filter by customer name or email.
Usage
curl -X GET \
'http://localhost:3000/api/public/v1/customers' \
-H "Authorization: Bearer ast_live_your_key"import { AsteriPublicApiClient } from '@asteri/public-api-sdk';
const client = new AsteriPublicApiClient({
baseUrl: 'http://localhost:3000',
apiKey: process.env.ASTERI_API_KEY!,
});
const response = await client.customers.list();Response body
{
"data": [
{
"id": "8af2ca8e-8f31-4e6e-a46c-0703b61f7ca3",
"organization_id": "dce3853d-4f94-4bc8-a816-4d0d74c11a9d",
"account_type": "business",
"display_name": "North Harbor Facilities",
"email": "ops@northerborfacilities.com",
"phone": "+1-404-555-0128",
"created_at": "2026-03-18T15:40:12.000Z"
}
],
"pagination": {
"count": 2,
"limit": 25,
"next_cursor": null
}
}Common errors: 400 Bad request · 401 Unauthorized · 403 Forbidden · 429 Rate limited · 503 Temporarily unavailable · 500 Internal error
400 Bad request
Malformed query or request payload.
- Usually happens when:
- The request sends malformed query/body data.
- What to do:
- Validate request fields against the contract examples before retrying.
{
"error": "Invalid request payload",
"issues": [
{
"path": "email",
"message": "Invalid email address"
}
]
}401 Unauthorized
Missing, invalid, or expired API credentials.
- Usually happens when:
- The API key is invalid, revoked, archived, expired, or missing a required expiry.
- What to do:
- Verify the key value, status, expiration, and Authorization: Bearer ast_live_... header.
{
"error": "invalid_api_key",
"message": "This API key is not valid. Check the key and try again.",
"request_id": "req_example_123"
}403 Forbidden
Missing plan access, capability, IP access, or scopes for this request.
- Usually happens when:
- The key is valid but the company’s plan does not include this (reads need a paid plan, writes need Premium or above), API access is suspended, the key IP allowlist does not match, or the key lacks the required scope.
- What to do:
- Upgrade the plan for writes, adjust the key IP allowlist, or create a key with the required scope.
{
"error": "developer_api_missing_scope",
"message": "This API key does not have access to this request. Check its scopes and try again.",
"request_id": "req_example_123"
}429 Rate limited
Source IP, API key, or organization request limit exceeded.
- Usually happens when:
- The source IP, current key, or organization has exceeded a request limit.
- What to do:
- Throttle request volume and avoid immediate fan-out retries from parallel workers.
- Retry guidance:
- Honor the `Retry-After` header and use exponential backoff for repeated limits.
{
"error": "developer_api_rate_limited",
"message": "Too many API requests. Please try again shortly.",
"request_id": "req_example_123"
}503 Temporarily unavailable
Developer API access or a required admission service is unavailable.
- Usually happens when:
- The API is disabled or its rate-limit service cannot safely admit this request.
- What to do:
- Check API availability, then retry with backoff.
{
"error": "developer_api_rate_limit_unavailable",
"message": "We could not check the request limit. Please try again shortly.",
"request_id": "req_example_123"
}500 Internal error
Unexpected API failure.
- Usually happens when:
- The API hit an unexpected internal failure while reading or serializing the resource.
- What to do:
- Log the contract version and response body, then retry once before escalating to Asteri support.
{
"error": "We couldn't load customers. Try again."
}POST/api/public/v1/customersCreate a customer with organization-scoped identity and contact data.
/api/public/v1/customersCreate a customer with organization-scoped identity and contact data.Required permissions
write:customers
201Usage
curl -X POST \
'http://localhost:3000/api/public/v1/customers' \
-H "Authorization: Bearer ast_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"account_type": "business",
"display_name": "North Harbor Facilities",
"company_name": "North Harbor Facilities",
"first_name": "Nora",
"last_name": "Harbor",
"email": "ops@northerborfacilities.com",
"phone": "+1-404-555-0128"
}'import { AsteriPublicApiClient } from '@asteri/public-api-sdk';
const client = new AsteriPublicApiClient({
baseUrl: 'http://localhost:3000',
apiKey: process.env.ASTERI_API_KEY!,
});
const response = await client.customers.create({
"account_type": "business",
"display_name": "North Harbor Facilities",
"company_name": "North Harbor Facilities",
"first_name": "Nora",
"last_name": "Harbor",
"email": "ops@northerborfacilities.com",
"phone": "+1-404-555-0128"
});Request body
Payload for creating a customer in the public API.
{
"account_type": "business",
"display_name": "North Harbor Facilities",
"company_name": "North Harbor Facilities",
"first_name": "Nora",
"last_name": "Harbor",
"email": "ops@northerborfacilities.com",
"phone": "+1-404-555-0128"
}View request fields
| Field | Type | Required | Description |
|---|---|---|---|
| account_type | "individual" | "business" | Yes | Account classification used when creating the customer. |
| display_name | string | No | Customer display name or company name. Useful when you do not want to provide explicit name fields. |
| company_name | string | No | Explicit company name for business accounts. |
| first_name | string | No | Primary contact first name. |
| last_name | string | No | Primary contact last name. |
| string | Yes | Primary contact email. | |
| phone | string | Yes | Primary contact phone number. |
Response body
{
"data": {
"id": "8af2ca8e-8f31-4e6e-a46c-0703b61f7ca3",
"organization_id": "dce3853d-4f94-4bc8-a816-4d0d74c11a9d",
"account_type": "business",
"display_name": "North Harbor Facilities",
"email": "ops@northerborfacilities.com",
"phone": "+1-404-555-0128",
"created_at": "2026-03-18T15:40:12.000Z"
}
}Common errors: 400 Bad request · 401 Unauthorized · 403 Forbidden · 409 Conflict · 429 Rate limited · 413 Request too large · 503 Temporarily unavailable · 500 Internal error
400 Bad request
Invalid request payload.
- Usually happens when:
- The request body is missing required fields or includes invalid field values.
- What to do:
- Validate the payload against the generated request schema and fix any `issues` entries before retrying.
{
"error": "Invalid request payload",
"issues": [
{
"path": "email",
"message": "Invalid email address"
}
]
}401 Unauthorized
Missing, invalid, or expired API credentials.
- Usually happens when:
- The API key is invalid, revoked, archived, expired, or missing a required expiry.
- What to do:
- Verify the key value, status, expiration, and Authorization: Bearer ast_live_... header.
{
"error": "invalid_api_key",
"message": "This API key is not valid. Check the key and try again.",
"request_id": "req_example_123"
}403 Forbidden
Creating and changing records needs the Premium plan or above.
- Usually happens when:
- The company plan includes reading through the API but not writing, or the key lacks the write scope.
- What to do:
- Upgrade to Premium in Subscription settings, or create a key with the needed write scope.
{
"error": "developer_api_missing_scope",
"message": "This API key does not have access to this request. Check its scopes and try again.",
"request_id": "req_example_123"
}409 Conflict
Duplicate customer email or conflicting customer state.
- Usually happens when:
- A create request reuses a primary email that already belongs to another active customer in the same organization.
- What to do:
- Fetch the existing customer first or update your upstream dedupe logic before retrying the create.
{
"error": "A customer already uses this email. Use a different email or open the existing customer.",
"field": "email"
}429 Rate limited
Source IP, API key, or organization request limit exceeded.
- Usually happens when:
- The source IP, current key, or organization has exceeded a request limit.
- What to do:
- Throttle mutation throughput and avoid blind retries from queues or worker pools.
- Retry guidance:
- Honor the `Retry-After` header and replay writes only after the relevant limit window resets.
{
"error": "developer_api_rate_limited",
"message": "Too many API requests. Please try again shortly.",
"request_id": "req_example_123"
}413 Request too large
The JSON request body exceeds this route’s byte limit.
- Usually happens when:
- The request body is larger than this write route accepts.
- What to do:
- Reduce the request body and try again.
{
"error": "The request is too large. Send a smaller JSON body and try again."
}503 Temporarily unavailable
Developer API access or a required admission service is unavailable.
- Usually happens when:
- The API is disabled or its rate-limit service cannot safely admit this write.
- What to do:
- Check API availability, then retry with the same idempotency key when applicable.
{
"error": "developer_api_rate_limit_unavailable",
"message": "We could not check the request limit. Please try again shortly.",
"request_id": "req_example_123"
}500 Internal error
Unexpected API failure.
- Usually happens when:
- The API hit an unexpected internal failure while validating or persisting the mutation.
- What to do:
- Capture the response payload and contract version, then retry once before escalating.
{
"error": "We couldn't load customers. Try again."
}PATCH/api/public/v1/customers/{customerId}Change a customer’s name, company, email or phone. Send only the fields that change.
/api/public/v1/customers/{customerId}Change a customer’s name, company, email or phone. Send only the fields that change.Required permissions
write:customers
200Usage
curl -X PATCH \
'http://localhost:3000/api/public/v1/customers/8af2ca8e-8f31-4e6e-a46c-0703b61f7ca3' \
-H "Authorization: Bearer ast_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Nora",
"last_name": "Harper",
"company_name": "North Harbor Facilities",
"email": "nora@example.com",
"phone": "+15555550100"
}'import { AsteriPublicApiClient } from '@asteri/public-api-sdk';
const client = new AsteriPublicApiClient({
baseUrl: 'http://localhost:3000',
apiKey: process.env.ASTERI_API_KEY!,
});
const response = await client.customers.update({ customerId: '8af2ca8e-8f31-4e6e-a46c-0703b61f7ca3' }, {
"first_name": "Nora",
"last_name": "Harper",
"company_name": "North Harbor Facilities",
"email": "nora@example.com",
"phone": "+15555550100"
});Request body
Payload for changing a customer. Send only the fields that change.
{
"first_name": "Nora",
"last_name": "Harper",
"company_name": "North Harbor Facilities",
"email": "nora@example.com",
"phone": "+15555550100"
}View request fields
| Field | Type | Required | Description |
|---|---|---|---|
| first_name | string | No | Primary contact first name. |
| last_name | string | No | Primary contact last name. |
| company_name | string or null | No | Company name for business accounts. Send null to clear it. |
| string or null | No | Primary email address. Send null to remove it. | |
| phone | string or null | No | Primary phone number. Send null to remove it. |
Response body
{
"data": {
"id": "8af2ca8e-8f31-4e6e-a46c-0703b61f7ca3",
"organization_id": "dce3853d-4f94-4bc8-a816-4d0d74c11a9d",
"account_type": "business",
"display_name": "North Harbor Facilities",
"email": "ops@northerborfacilities.com",
"phone": "+1-404-555-0128",
"created_at": "2026-03-18T15:40:12.000Z"
}
}Common errors: 400 Bad request · 401 Unauthorized · 403 Forbidden · 409 Conflict · 429 Rate limited · 413 Request too large · 503 Temporarily unavailable · 500 Internal error
400 Bad request
Invalid request payload.
- Usually happens when:
- The request body is missing required fields or includes invalid field values.
- What to do:
- Validate the payload against the generated request schema and fix any `issues` entries before retrying.
{
"error": "Invalid request payload",
"issues": [
{
"path": "email",
"message": "Invalid email address"
}
]
}401 Unauthorized
Missing, invalid, or expired API credentials.
- Usually happens when:
- The API key is invalid, revoked, archived, expired, or missing a required expiry.
- What to do:
- Verify the key value, status, expiration, and Authorization: Bearer ast_live_... header.
{
"error": "invalid_api_key",
"message": "This API key is not valid. Check the key and try again.",
"request_id": "req_example_123"
}403 Forbidden
Creating and changing records needs the Premium plan or above.
- Usually happens when:
- The company plan includes reading through the API but not writing, or the key lacks the write scope.
- What to do:
- Upgrade to Premium in Subscription settings, or create a key with the needed write scope.
{
"error": "developer_api_missing_scope",
"message": "This API key does not have access to this request. Check its scopes and try again.",
"request_id": "req_example_123"
}409 Conflict
Another customer in this company already uses the new email address.
- Usually happens when:
- The email sent belongs to another active customer in the same organization.
- What to do:
- Find that customer and update it instead, or send a different email address.
{
"error": "A customer already uses this email. Use a different email or open the existing customer.",
"field": "email"
}429 Rate limited
Source IP, API key, or organization request limit exceeded.
- Usually happens when:
- The source IP, current key, or organization has exceeded a request limit.
- What to do:
- Throttle mutation throughput and avoid blind retries from queues or worker pools.
- Retry guidance:
- Honor the `Retry-After` header and replay writes only after the relevant limit window resets.
{
"error": "developer_api_rate_limited",
"message": "Too many API requests. Please try again shortly.",
"request_id": "req_example_123"
}413 Request too large
The JSON request body exceeds this route’s byte limit.
- Usually happens when:
- The request body is larger than this write route accepts.
- What to do:
- Reduce the request body and try again.
{
"error": "The request is too large. Send a smaller JSON body and try again."
}503 Temporarily unavailable
Developer API access or a required admission service is unavailable.
- Usually happens when:
- The API is disabled or its rate-limit service cannot safely admit this write.
- What to do:
- Check API availability, then retry with the same idempotency key when applicable.
{
"error": "developer_api_rate_limit_unavailable",
"message": "We could not check the request limit. Please try again shortly.",
"request_id": "req_example_123"
}500 Internal error
Unexpected API failure.
- Usually happens when:
- The API hit an unexpected internal failure while validating or persisting the mutation.
- What to do:
- Capture the response payload and contract version, then retry once before escalating.
{
"error": "We couldn't load customers. Try again."
}GET/api/public/v1/customers/{customerId}Retrieve one customer and related profile metadata.
/api/public/v1/customers/{customerId}Retrieve one customer and related profile metadata.Required permissions
read:customers
200Usage
curl -X GET \
'http://localhost:3000/api/public/v1/customers/8af2ca8e-8f31-4e6e-a46c-0703b61f7ca3' \
-H "Authorization: Bearer ast_live_your_key"import { AsteriPublicApiClient } from '@asteri/public-api-sdk';
const client = new AsteriPublicApiClient({
baseUrl: 'http://localhost:3000',
apiKey: process.env.ASTERI_API_KEY!,
});
const response = await client.customers.get({ customerId: '8af2ca8e-8f31-4e6e-a46c-0703b61f7ca3' });Response body
{
"data": {
"id": "8af2ca8e-8f31-4e6e-a46c-0703b61f7ca3",
"organization_id": "dce3853d-4f94-4bc8-a816-4d0d74c11a9d",
"account_type": "business",
"display_name": "North Harbor Facilities",
"email": "ops@northerborfacilities.com",
"phone": "+1-404-555-0128",
"created_at": "2026-03-18T15:40:12.000Z"
}
}Common errors: 400 Bad request · 401 Unauthorized · 403 Forbidden · 404 Not found · 429 Rate limited · 503 Temporarily unavailable · 500 Internal error
400 Bad request
Malformed query or request payload.
- Usually happens when:
- The request sends malformed query/body data.
- What to do:
- Validate request fields against the contract examples before retrying.
{
"error": "Invalid request payload",
"issues": [
{
"path": "email",
"message": "Invalid email address"
}
]
}401 Unauthorized
Missing, invalid, or expired API credentials.
- Usually happens when:
- The API key is invalid, revoked, archived, expired, or missing a required expiry.
- What to do:
- Verify the key value, status, expiration, and Authorization: Bearer ast_live_... header.
{
"error": "invalid_api_key",
"message": "This API key is not valid. Check the key and try again.",
"request_id": "req_example_123"
}403 Forbidden
Missing plan access, capability, IP access, or scopes for this request.
- Usually happens when:
- The key is valid but the company’s plan does not include this (reads need a paid plan, writes need Premium or above), API access is suspended, the key IP allowlist does not match, or the key lacks the required scope.
- What to do:
- Upgrade the plan for writes, adjust the key IP allowlist, or create a key with the required scope.
{
"error": "developer_api_missing_scope",
"message": "This API key does not have access to this request. Check its scopes and try again.",
"request_id": "req_example_123"
}404 Not found
Requested customer record was not found.
- Usually happens when:
- The requested `customerId` does not exist in the active organization or has been removed from the public view.
- What to do:
- Refresh your local record cache and confirm the customer belongs to the organization id you sent.
{
"error": "We couldn't find that customer. Check its ID and try again."
}429 Rate limited
Source IP, API key, or organization request limit exceeded.
- Usually happens when:
- The source IP, current key, or organization has exceeded a request limit.
- What to do:
- Throttle request volume and avoid immediate fan-out retries from parallel workers.
- Retry guidance:
- Honor the `Retry-After` header and use exponential backoff for repeated limits.
{
"error": "developer_api_rate_limited",
"message": "Too many API requests. Please try again shortly.",
"request_id": "req_example_123"
}503 Temporarily unavailable
Developer API access or a required admission service is unavailable.
- Usually happens when:
- The API is disabled or its rate-limit service cannot safely admit this request.
- What to do:
- Check API availability, then retry with backoff.
{
"error": "developer_api_rate_limit_unavailable",
"message": "We could not check the request limit. Please try again shortly.",
"request_id": "req_example_123"
}500 Internal error
Unexpected API failure.
- Usually happens when:
- The API hit an unexpected internal failure while reading or serializing the resource.
- What to do:
- Log the contract version and response body, then retry once before escalating to Asteri support.
{
"error": "We couldn't load customers. Try again."
}View customers schemas and field examples
Customer
Canonical public representation of a customer record in Asteri.
| Field | Type | Required | Description |
|---|---|---|---|
| id | uuid | Yes | Stable customer identifier. |
| organization_id | uuid | Yes | Organization that owns the record. |
| account_type | "individual" | "business" | Yes | High-level account classification. |
| display_name | string | Yes | Primary display name shown across the platform. |
| string | null | Yes | Primary contact email. | |
| phone | string | null | Yes | Primary contact phone number. |
| created_at | datetime | Yes | ISO-8601 timestamp for record creation. |
{
"id": "8af2ca8e-8f31-4e6e-a46c-0703b61f7ca3",
"organization_id": "dce3853d-4f94-4bc8-a816-4d0d74c11a9d",
"account_type": "business",
"display_name": "North Harbor Facilities",
"email": "ops@northerborfacilities.com",
"phone": "+1-404-555-0128",
"created_at": "2026-03-18T15:40:12.000Z"
}CreateCustomerRequest
Payload for creating a customer in the public API.
| Field | Type | Required | Description |
|---|---|---|---|
| account_type | "individual" | "business" | Yes | Account classification used when creating the customer. |
| display_name | string | No | Customer display name or company name. Useful when you do not want to provide explicit name fields. |
| company_name | string | No | Explicit company name for business accounts. |
| first_name | string | No | Primary contact first name. |
| last_name | string | No | Primary contact last name. |
| string | Yes | Primary contact email. | |
| phone | string | Yes | Primary contact phone number. |
{
"account_type": "business",
"display_name": "North Harbor Facilities",
"company_name": "North Harbor Facilities",
"first_name": "Nora",
"last_name": "Harbor",
"email": "ops@northerborfacilities.com",
"phone": "+1-404-555-0128"
}UpdateCustomerRequest
Payload for changing a customer. Send only the fields that change.
| Field | Type | Required | Description |
|---|---|---|---|
| first_name | string | No | Primary contact first name. |
| last_name | string | No | Primary contact last name. |
| company_name | string or null | No | Company name for business accounts. Send null to clear it. |
| string or null | No | Primary email address. Send null to remove it. | |
| phone | string or null | No | Primary phone number. Send null to remove it. |
{
"first_name": "Nora",
"last_name": "Harper",
"company_name": "North Harbor Facilities",
"email": "nora@example.com",
"phone": "+15555550100"
}CustomerListResponse
Response body for customer list endpoints.
| Field | Type | Required | Description |
|---|---|---|---|
| data | Customer[] | Yes | Collection of customers matching the current filter set. |
| pagination | Pagination | Yes | List metadata for page traversal. |
{
"data": [
{
"id": "8af2ca8e-8f31-4e6e-a46c-0703b61f7ca3",
"organization_id": "dce3853d-4f94-4bc8-a816-4d0d74c11a9d",
"account_type": "business",
"display_name": "North Harbor Facilities",
"email": "ops@northerborfacilities.com",
"phone": "+1-404-555-0128",
"created_at": "2026-03-18T15:40:12.000Z"
}
],
"pagination": {
"count": 2,
"limit": 25,
"next_cursor": null
}
}