Customers and groups
Create and update customers, addresses, and groups from your CRM or ERP.
Sync Andrea Ramos from your CRM: look her up by email, create her if she doesn't exist, keep her data current, and add her to the "Wholesale" group.
Requirements
- An Admin token in
ECOMIQ_TOKEN. See Quickstart. - The
customer:read,customer:create, andcustomer:updatepermissions for customers and addresses;customer_group:read,customer_group:create, andcustomer_group:updatefor groups. - A public HTTPS URL, to receive
customer.created.
The JavaScript and Python examples use this function, which sends the token and
throws when the response isn't 2xx:
export async function ecomiq(method, path, body) {
const init = {
method,
headers: {
Authorization: `Bearer ${process.env.ECOMIQ_TOKEN}`,
"Content-Type": "application/json",
},
};
if (body !== undefined) init.body = JSON.stringify(body);
const response = await fetch(`https://api.ecomiq.pe/api${path}`, init);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
if (response.status === 204) return null;
return response.json();
}Search by email
Email is unique within the store, case-insensitive. search matches email,
first name, and last name, including partial matches.
curl -G https://api.ecomiq.pe/api/v1/customers \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
--data-urlencode "search=andrea.ramos@example.com"{
"data": [],
"hasNextPage": false,
"nextCursor": null,
"totalCount": 0
}Compare the full email in the results. If you find the customer, store their
id and update them; otherwise, create them.
Create a customer
Send email, firstName, lastName, customerType, and countryIsoCode.
Use metadata to store your CRM ID.
{
"email": "andrea.ramos@example.com",
"firstName": "Andrea",
"lastName": "Ramos",
"customerType": "individual",
"countryIsoCode": "PE",
"phone": "+51987654321",
"identificationType": "DNI",
"identificationNumber": "72654321",
"consent": true,
"metadata": { "crmId": "CRM-1042" }
}curl -X POST https://api.ecomiq.pe/api/v1/customers \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
--data @customer.json{
"id": "7204558912004325301",
"status": "pendingVerification",
"isVerified": false
}Store the id as a string in your CRM. An individual customer starts in
pendingVerification. consent: true means they agreed to receive marketing
email. An identity document can belong to only one customer in the store.
Create a business
Send customerType: "business" and the legal name in entityName.
firstName and lastName belong to the contact person.
curl -X POST https://api.ecomiq.pe/api/v1/customers \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "purchasing@example.com",
"firstName": "Lucía",
"lastName": "Torres",
"customerType": "business",
"countryIsoCode": "PE",
"phone": "+5115550142",
"identificationType": "RUC",
"identificationNumber": "20123456789",
"entityName": "Comercial Lima SAC"
}'The business starts in pendingApproval. Approve it to move it to active;
the response is 204:
curl -X POST https://api.ecomiq.pe/api/v1/customers/7204558912004325302/approve \
-H "Authorization: Bearer $ECOMIQ_TOKEN"Update a customer
PATCH changes only the fields you send and returns 204. null clears an
optional field. metadata replaces the stored object.
curl -X PATCH https://api.ecomiq.pe/api/v1/customers/7204558912004325301 \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "andrea.ramos.p@example.com",
"phone": "+51912345678"
}'Consent resets
Changing email sets consent to false, and changing phone sets
smsConsent to false.
If the customer has a userId, their account is linked and you can't change
the email here.
Replace the tags
tags replaces every tag: to add one, send the current list plus the new tag.
[] or null clears them. You can send up to 100 tags of up to 100 characters;
extra whitespace and duplicates are removed.
curl -X PATCH https://api.ecomiq.pe/api/v1/customers/7204558912004325301 \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "tags": ["wholesale", "lima"] }'Save an address
The first address becomes the default; with isDefault: true, the new one
does. The 201 response includes its id.
curl -X POST https://api.ecomiq.pe/api/v1/customers/7204558912004325301/addresses \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"addressKey": "home",
"countryIsoCode": "PE",
"addressLine": "Av. Larco 1234",
"department": "Lima",
"province": "Lima",
"district": "Miraflores",
"phone": "+51987654321",
"isDefault": true
}'To edit it, send the full address to
PUT /: fields you omit are
cleared.
Create a manual group
A manual group holds the customers you pick. The name is unique within the
store.
curl -X POST https://api.ecomiq.pe/api/v1/customers/groups \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "Wholesale", "type": "manual" }'Add members by ID:
curl -X POST https://api.ecomiq.pe/api/v1/customers/groups/7204558912004325310/members \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "customerIds": ["7204558912004325301", "7204558912004325302"] }'{
"customerGroupId": "7204558912004325310",
"addedCount": 2,
"existingCount": 0,
"requestedCount": 2
}Customers already in the group count toward existingCount, so you can repeat
the call. To remove members, send their IDs to
POST /; the customer isn't deleted.
Create a dynamic group
A dynamic group includes the customers who meet its conditions: all of them
with match: "all", any of them with "any". It needs at least one condition
and doesn't accept manual members.
Field (field) | Operators | Value |
|---|---|---|
amountSpent, orderCount | atLeast, atMost, between | numbers |
lastOrderDate, firstOrderDate, createdDate, abandonedCheckoutDate | withinLastDays, moreThanDaysAgo, betweenDates | numbers (days) or dates |
paymentMethod | in (used), notIn (never used), lastOrderIn | values: method codes |
department, province, district | in, notIn | values: IDs from / |
country | in, notIn | values: ISO codes |
productPurchased, categoryPurchased, salesChannel, group, tag, status, customerType, emailSubscription, smsSubscription | in, notIn | values |
amountSpent is in the store currency, net of refunds. Several values in one
condition mean "any of them."
curl -X POST https://api.ecomiq.pe/api/v1/customers/groups \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Frequent customers",
"type": "dynamic",
"conditions": {
"match": "all",
"conditions": [
{ "field": "orderCount", "operator": "atLeast", "numbers": [3], "dates": null, "values": null },
{ "field": "paymentMethod", "operator": "in", "numbers": null, "dates": null, "values": ["yape", "plin"] }
]
}
}'To test the rules without saving the group, send match and conditions at
the root of the body to POST /:
curl -X POST https://api.ecomiq.pe/api/v1/customers/groups/preview \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"match": "all",
"conditions": [
{ "field": "orderCount", "operator": "atLeast", "numbers": [3], "dates": null, "values": null }
],
"includeSample": true,
"limit": 10
}'The response includes count (matching customers), total (customers in the
store), and sample. limit ranges from 1 to 20 (default 10); with
includeSample: false, sample comes back empty.
List the members
groupId on GET / and GET / evaluates the
group's rules live; for a manual group, it returns its members. Other filters
combine with AND:
GET /v1/customers?groupId=7204558912004325311&minOrderCount=3
GET /v1/customers/count?groupId=7204558912004325311&minOrderCount=3Discounts and campaigns use stored memberships, as do the group condition and
the memberOfGroupIds and excludeMemberOfGroupIds filters. They're computed
when the group is created and recomputed with each order. customerCount shows
the latest computation; lastMaterializedOn is null until the first one.
To export or edit the same selection in a spreadsheet, send those filters in
the body of POST / or in filters on
POST /. In JSON, status, excludeStatus,
customerType, and excludeCustomerType are plural: statuses,
excludeStatuses, customerTypes, and excludeCustomerTypes.
PUT replaces the group
PUT / expects every field. If you omit type, the
group becomes manual. With isActive: false, it stops being recomputed.
Receive customer.created
customer.created arrives for every new customer, including the ones your CRM
creates. Register your URL:
curl -X POST https://api.ecomiq.pe/api/v1/webhooks/endpoints \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Customers to CRM",
"url": "https://crm.example.com/webhooks/ecomiq",
"eventTypes": ["customer.created"],
"secret": "whsec_replace_with_a_random_secret"
}'Verify the signature and respond 2xx as in
Incoming orders. The body is a
CustomerWebhookModel
with the customer ID.
Delivery is at least once. If the ID is already in your CRM, ignore the event; otherwise, read the customer and record them:
export async function handleCustomerCreated(customerId) {
if (await crmHasCustomer(customerId)) return;
const customer = await ecomiq("GET", `/v1/customers/${customerId}`);
await createCrmContact(customer);
}crmHasCustomer and createCrmContact are functions in your CRM.
Errors
code | Status | What to do |
|---|---|---|
Common.Validation | 400 | Check errors: a field is missing or has the wrong format. |
CustomerGroup.DynamicMembership | 400 | The group is dynamic: change its conditions, not its members. |
Common.Forbidden | 403 | Add the missing permission to your app. |
Customer.NotFound | 404 | The customer doesn't exist in the store. |
CustomerGroup.CustomersNotFound | 404 | None of the customerIds belong to the store. |
Customer.EmailAlreadyExists | 409 | Another customer uses that email: look them up and update them. |
Customer.IdentificationAlreadyExists | 409 | Another customer has that identity document. |
Customer.LinkedAccountEmail | 409 | The customer has an account: they change the email from it. |
Customers.ApprovalRequiresPending | 409 | Only a customer in pendingApproval can be approved. |
CustomerGroup.AlreadyExists | 409 | A group with that name already exists. |
Next steps
Last updated: