Trowel Academy
Acciones

#Productos, proveedores y precios

La API usa los identificadores id que devuelve Trowel. No admite identificadores externos ni rutas con externalId. Conserva los id en tu sistema si necesitas relacionar ambos catálogos.

Las listas usan paginación por cursor. Envía limit entre 1 y 200 y, cuando hasMore sea true, solicita la página siguiente con nextCursor. No modifiques el cursor.

#Productos

Scopes: products:read para consultar y products:write para crear, actualizar o eliminar.

GET /api/public/v1/products
GET /api/public/v1/products/{id}
POST /api/public/v1/products
PUT /api/public/v1/products/{id}
DELETE /api/public/v1/products/{id}?concurrencyToken={concurrencyToken}

La lista permite filtrar por code, active, unitCode, search y modifiedSince. También admite sort; antepón - para ordenar de forma descendente, por ejemplo sort=-modifiedAt.

Para crear un producto:

{
  "code": "MAT-001",
  "shortDescription": "Cemento gris",
  "description": "Saco de 25 kg",
  "unitCode": "kg",
  "purchasePrice": 4.75,
  "purchasePriceDate": "2026-10-09T00:00:00Z",
  "salePrice": 6.2,
  "active": true
}

La creación devuelve 201 con el producto, su id y su concurrencyToken. Para actualizarlo, envía todos los campos editables y el último token recibido:

{
  "code": "MAT-001",
  "shortDescription": "Cemento gris",
  "description": "Saco de 25 kg",
  "unitCode": "kg",
  "purchasePrice": 4.9,
  "purchasePriceDate": "2026-10-15T00:00:00Z",
  "salePrice": 6.2,
  "active": true,
  "concurrencyToken": "<token de la última lectura o escritura>"
}

Si el producto ha cambiado desde que obtuviste el token, la actualización o el borrado devuelve 409 ProductConcurrencyConflict. Un producto utilizado en Trowel no se puede eliminar y devuelve 409 ProductIsUsed.

unitCode coincide exactamente con el símbolo publicado en el catálogo de unidades de Trowel. La API no acepta el GUID de la unidad ni traduce su nombre. Un código desconocido devuelve 422 UnknownUnitCode.

purchasePrice es el precio de compra de referencia del producto. No es el precio de un proveedor concreto.

#Proveedores

Scopes: suppliers:read para consultar y suppliers:write para crear, actualizar o desactivar.

GET /api/public/v1/suppliers
GET /api/public/v1/suppliers/{id}
POST /api/public/v1/suppliers
PUT /api/public/v1/suppliers/{id}
DELETE /api/public/v1/suppliers/{id}?concurrencyToken={concurrencyToken}

La lista permite filtrar por code, taxIdentifier, active, search y modifiedSince.

Para crear un proveedor:

{
  "code": "PROV-001",
  "businessName": "Materiales Norte SL",
  "commercialName": "Materiales Norte",
  "taxIdentifier": "B12345678",
  "active": true
}

La creación devuelve 201. Para actualizar, usa el id del proveedor y añade el último concurrencyToken al cuerpo. DELETE no borra la empresa: desactiva el proveedor y devuelve 204.

Los códigos y NIF duplicados devuelven 409 SupplierCodeAlreadyExists o 409 SupplierTaxIdentifierAlreadyExists. La API no enlaza automáticamente un registro existente. Si el token de concurrencia ya no es válido, devuelve 409 SupplierConcurrencyConflict.

#Precios de proveedor

Scopes: prices:read para consultar y prices:write para crear o actualizar.

GET /api/public/v1/products/{productId}/supplier-prices
POST /api/public/v1/products/{productId}/supplier-prices
PUT /api/public/v1/products/{productId}/supplier-prices/{id}

La lista se puede filtrar por supplierId y modifiedSince.

Para crear un precio, usa el id del producto en la ruta y el id del proveedor en el cuerpo:

{
  "supplierId": "<id del proveedor>",
  "supplierProductCode": "CEM-25",
  "effectiveDate": "2026-10-09T00:00:00Z",
  "price": 90,
  "priceBeforeDiscount": 100,
  "discountPercentage": 10,
  "setAsReferencePurchasePrice": true
}

La creación devuelve 201. Para actualizar un precio existente, usa su id, conserva los mismos campos salvo supplierId y añade el último concurrencyToken.

Los precios no pueden ser negativos, el descuento debe estar entre 0 y 100 y price debe coincidir con el precio base menos el descuento, redondeado a cuatro decimales. Si setAsReferencePurchasePrice es true, Trowel actualiza el precio y la fecha de compra del producto en la misma transacción.

Un precio modificado desde la última lectura devuelve 409 SupplierPriceConcurrencyConflict.

#Sincronización inicial y cambios

Scope: changes:read.

  1. Obtén un punto de control con GET /api/public/v1/product-changes/cursor.
  2. Lee todas las páginas de productos, proveedores y precios.
  3. Solicita GET /api/public/v1/product-changes?cursor=<punto-de-control>.
  4. Aplica los eventos en orden de sequence y continúa con nextCursor.

El feed incluye cambios realizados mediante la API pública y cambios hechos directamente en Trowel. origin vale publicApi o trowel; los eventos de la API incluyen originApiClientId. Cada evento identifica el recurso mediante resourceId e incluye su concurrencyToken. operation distingue altas, actualizaciones, activaciones, desactivaciones y borrados. En los borrados, tombstone vale true.

El cursor está ligado al tenant, al cliente de API y al feed. Un cursor anterior al historial conservado devuelve 410 ChangeCursorExpired; en ese caso repite la sincronización inicial.

#Reintentos

En 429, espera los segundos indicados por Retry-After. Para un 5xx, usa reintentos con espera creciente y conserva el mismo X-Correlation-ID en los intentos de la misma operación.

No des por hecho que un POST fallido no llegó a crear el recurso: consulta el catálogo antes de repetirlo para evitar duplicados. Ante un 409 de concurrencia, lee el recurso actual, resuelve el conflicto y envía el nuevo concurrencyToken.

⌕Esc

Escribe para buscar en toda la Academy.