Saltar al contenido
Clientes y ventas

Clientes y grupos

Crea y actualiza clientes, direcciones y grupos desde tu CRM o ERP.

Sincroniza a Andrea Ramos desde tu CRM: búscala por email, créala si no existe, mantén sus datos al día y agrégala al grupo «Mayoristas».

Requisitos

  • Un token de Admin en ECOMIQ_TOKEN. Ver Inicio rápido.
  • Los permisos customer:read, customer:create y customer:update para clientes y direcciones; customer_group:read, customer_group:create y customer_group:update para grupos.
  • Una URL HTTPS pública, para recibir customer.created.

Los ejemplos de JavaScript y Python usan esta función, que envía el token y falla si la respuesta no es 2xx:

ecomiq.js
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();
}

Busca por email

El email es único en la tienda, sin distinguir mayúsculas. search busca en email, nombre y apellido, con coincidencias parciales.

curl -G https://api.ecomiq.pe/api/v1/customers \
  -H "Authorization: Bearer $ECOMIQ_TOKEN" \
  --data-urlencode "search=andrea.ramos@example.com"
Respuesta
{
  "data": [],
  "hasNextPage": false,
  "nextCursor": null,
  "totalCount": 0
}

Compara el email completo en los resultados. Si encuentras al cliente, guarda su id y actualízalo; si no, créalo.

Crea un cliente

Envía email, firstName, lastName, customerType y countryIsoCode. Usa metadata para guardar el ID de tu CRM.

cliente.json
{
  "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 @cliente.json
Respuesta
{
  "id": "7204558912004325301",
  "status": "pendingVerification",
  "isVerified": false
}

Guarda el id como string en tu CRM. Un cliente individual empieza en pendingVerification. consent: true indica que aceptó recibir correos de marketing. El mismo documento de identidad no puede repetirse en la tienda.

Crea una empresa

Envía customerType: "business" y la razón social en entityName. firstName y lastName son de la persona de contacto.

curl -X POST https://api.ecomiq.pe/api/v1/customers \
  -H "Authorization: Bearer $ECOMIQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "compras@example.com",
    "firstName": "Lucía",
    "lastName": "Torres",
    "customerType": "business",
    "countryIsoCode": "PE",
    "phone": "+5115550142",
    "identificationType": "RUC",
    "identificationNumber": "20123456789",
    "entityName": "Comercial Lima SAC"
  }'

La empresa empieza en pendingApproval. Apruébala para pasarla a active; la respuesta es 204:

curl -X POST https://api.ecomiq.pe/api/v1/customers/7204558912004325302/approve \
  -H "Authorization: Bearer $ECOMIQ_TOKEN"

Actualiza un cliente

PATCH cambia solo los campos que envías y responde 204. null borra un campo opcional. metadata reemplaza el objeto guardado.

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"
  }'

El consentimiento se reinicia

Cambiar email pone consent en false, y cambiar phone pone smsConsent en false.

Si el cliente tiene userId, su cuenta está vinculada y el email no se cambia por esta vía.

Reemplaza las etiquetas

tags reemplaza todas las etiquetas: para agregar una, envía la lista actual más la nueva. [] o null las borra. Acepta hasta 100 etiquetas de hasta 100 caracteres, y quita espacios sobrantes y duplicados.

curl -X PATCH https://api.ecomiq.pe/api/v1/customers/7204558912004325301 \
  -H "Authorization: Bearer $ECOMIQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "tags": ["mayorista", "lima"] }'

Guarda una dirección

La primera dirección queda como predeterminada; con isDefault: true, la nueva pasa a serlo. La respuesta 201 trae su 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": "casa",
    "countryIsoCode": "PE",
    "addressLine": "Av. Larco 1234",
    "department": "Lima",
    "province": "Lima",
    "district": "Miraflores",
    "phone": "+51987654321",
    "isDefault": true
  }'

Para editarla, envía la dirección completa a PUT /v1/customers/{customerId}/addresses/{addressId}: los campos que omitas quedan vacíos.

Crea un grupo manual

Un grupo manual tiene los clientes que eliges. El nombre es único en la tienda.

curl -X POST https://api.ecomiq.pe/api/v1/customers/groups \
  -H "Authorization: Bearer $ECOMIQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Mayoristas", "type": "manual" }'

Agrega miembros con sus IDs:

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"] }'
Respuesta
{
  "customerGroupId": "7204558912004325310",
  "addedCount": 2,
  "existingCount": 0,
  "requestedCount": 2
}

Los clientes que ya estaban suman en existingCount, así que puedes repetir la llamada. Para quitar miembros, envía sus IDs a POST /v1/customers/groups/{id}/members/remove; el cliente no se borra.

Crea un grupo dinámico

Un grupo dynamic incluye a los clientes que cumplen sus conditions: con match: "all", todas; con "any", alguna. Necesita al menos una condición y no acepta miembros a mano.

Campo (field)OperadoresValor
amountSpent, orderCountatLeast, atMost, betweennumbers
lastOrderDate, firstOrderDate, createdDate, abandonedCheckoutDatewithinLastDays, moreThanDaysAgo, betweenDatesnumbers (días) o dates
paymentMethodin (usó), notIn (no usó), lastOrderInvalues: códigos de método
department, province, districtin, notInvalues: IDs de /v1/geo/areas
countryin, notInvalues: códigos ISO
productPurchased, categoryPurchased, salesChannel, group, tag, status, customerType, emailSubscription, smsSubscriptionin, notInvalues

amountSpent está en la moneda de la tienda y descuenta reembolsos. Varios valores en una condición cuentan como «cualquiera de ellos».

curl -X POST https://api.ecomiq.pe/api/v1/customers/groups \
  -H "Authorization: Bearer $ECOMIQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Clientes frecuentes",
    "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"] }
      ]
    }
  }'

Para probar las reglas sin guardar el grupo, envía match y conditions en la raíz del body a POST /v1/customers/groups/preview:

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
  }'

La respuesta trae count (clientes que cumplen), total (clientes de la tienda) y sample. limit va de 1 a 20 (10 por defecto); con includeSample: false, sample llega vacío.

Lista los miembros

groupId en GET /v1/customers y GET /v1/customers/count evalúa las reglas del grupo en vivo; en un grupo manual, trae sus miembros. Los demás filtros se suman con AND:

GET /v1/customers?groupId=7204558912004325311&minOrderCount=3
GET /v1/customers/count?groupId=7204558912004325311&minOrderCount=3

Descuentos y campañas usan las membresías guardadas, igual que la condición group y los filtros memberOfGroupIds y excludeMemberOfGroupIds. Se calculan al crear el grupo y se recalculan con cada pedido. customerCount muestra el último cálculo; lastMaterializedOn es null hasta el primero.

Para exportar o editar en hoja la misma selección, envía esos filtros en el body de POST /v1/customers/exports o en filters de POST /v1/customers/spreadsheet. En JSON, status, excludeStatus, customerType y excludeCustomerType van en plural: statuses, excludeStatuses, customerTypes y excludeCustomerTypes.

PUT reemplaza el grupo

PUT /v1/customers/groups/{id} espera todos los campos. Si omites type, el grupo pasa a manual. Con isActive: false, deja de recalcularse.

Recibe customer.created

customer.created llega por cada cliente nuevo, también por los que crea tu CRM. Registra tu URL:

curl -X POST https://api.ecomiq.pe/api/v1/webhooks/endpoints \
  -H "Authorization: Bearer $ECOMIQ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Clientes al CRM",
    "url": "https://crm.example.com/webhooks/ecomiq",
    "eventTypes": ["customer.created"],
    "secret": "whsec_replace_with_a_random_secret"
  }'

Verifica la firma y responde 2xx como en Pedidos entrantes. El cuerpo es un CustomerWebhookModel con el ID del cliente.

La entrega es al menos una vez. Si el ID ya está en tu CRM, ignora el evento; si no, lee el cliente y regístralo:

customer-created.js
export async function handleCustomerCreated(customerId) {
  if (await crmHasCustomer(customerId)) return;

  const customer = await ecomiq("GET", `/v1/customers/${customerId}`);
  await createCrmContact(customer);
}

crmHasCustomer y createCrmContact son funciones de tu CRM.

Errores

codeEstadoQué hacer
Common.Validation400Revisa errors: falta un campo o tiene otro formato.
CustomerGroup.DynamicMembership400El grupo es dinámico: cambia sus condiciones, no sus miembros.
Common.Forbidden403Agrega a tu app el permiso que falta.
Customer.NotFound404El cliente no existe en la tienda.
CustomerGroup.CustomersNotFound404Ninguno de los customerIds es de la tienda.
Customer.EmailAlreadyExists409Otro cliente usa ese email: búscalo y actualízalo.
Customer.IdentificationAlreadyExists409Otro cliente tiene ese documento.
Customer.LinkedAccountEmail409El cliente tiene cuenta: el email lo cambia él desde su cuenta.
Customers.ApprovalRequiresPending409Solo se aprueba un cliente en pendingApproval.
CustomerGroup.AlreadyExists409Ya hay un grupo con ese nombre.

Siguientes pasos

Última actualización:

En esta página