Cayena integration API
Cayena consumes this API on Demander Web. The company configuration is in Integration setup.
Address
Section titled “Address”https://sistema.demander.com.br/cayena/v1POST bodies are JSON. Error responses follow this format:
{ "error": { "code": "INVALID_REQUEST", "message": "Texto do erro.", "details": {} }}details is present only when the API needs to point at a product, order or city.
| Limit | Where |
|---|---|
| 60 calls per minute | POST /cayena/v1/auth, by company code |
| 300 calls per minute | Other routes, by access token |
Above the limit the response is HTTP 429, code RATE_LIMIT_EXCEEDED.
Authentication
Section titled “Authentication”POST /cayena/v1/auth
{ "clientId": "codigo-da-empresa", "clientSecret": "token-gerado-na-configuracao"}clientId is the company code in Demander. clientSecret is the token from the Integração Cayena screen.
HTTP 200 response:
{ "accessToken": "token-de-acesso", "tokenType": "Bearer"}The access token lasts 1 hour. On the other routes send:
Authorization: Bearer accessTokenaccessToken is the value returned by authentication.
| HTTP | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST |
clientId or clientSecret missing, or clientId that is not the company code |
| 401 | INVALID_CREDENTIALS |
Different token or inactive integration |
| 403 | COMPANY_INACTIVE |
Company inactive or outside the trial period |
| 401 | INVALID_TOKEN |
On the other routes: Bearer missing, expired, tampered, or from a configuration that was turned off or had its token replaced |
Product catalog
Section titled “Product catalog”GET /cayena/v1/products?page=1&limit=100
| Parameter | Default | Rule |
|---|---|---|
page |
1 | Integer from 1 |
limit |
100 | Integer from 1 to 1000 |
Active products with a price greater than zero on the configured price list are included. A price exclusive to one customer is not included. If the product has more than one price on that list, the one with the lowest code is used.
{ "currentPage": 1, "totalPages": 3, "totalProducts": 250, "products": [ { "productId": "10", "description": "Nome do produto", "content": "1", "size": "0.5", "availableStock": 20, "price": 10.25, "boxWeight": 12, "ean": "7890000000000", "brand": "Marca", "units": [ { "unitCode": "UN", "unitDescription": "Unidade" } ] } ]}| Field | Origin in Demander |
|---|---|
productId |
Product code |
description |
Description |
content |
Package quantity on the price list |
size |
Net weight on the price list |
availableStock |
Stock balance. A negative value comes back as 0 |
price |
Price on the configured list |
boxWeight |
First package multiple, when it exists |
ean |
Barcode, when filled |
brand |
Brand, when filled |
units |
Unit acronym and the mapping description |
The unit comes from the price. If it is empty, the product sales unit is used.
| HTTP | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST |
Invalid page or limit |
| 422 | MISSING_UNIT |
A product on the page has no unit |
| 422 | UNIT_NOT_MAPPED |
Acronym without a description in the mapping |
These 422 errors stop the page. details brings productId and, for the unit, unitCode.
Customers
Section titled “Customers”GET /cayena/v1/customers/documentNumber
Send CPF or CNPJ with digits only. Demander also accepts the document with a mask.
Create
Section titled “Create”POST /cayena/v1/customers
{ "documentNumber": "11444777000161", "documentType": "CNPJ", "legalName": "Cliente Exemplo Ltda", "tradeName": "Cliente Exemplo", "email": "compras@exemplo.com", "phone": "11999998888", "stateRegistration": "123456789", "address": { "street": "Rua Exemplo", "number": "100", "complement": "Sala 2", "neighborhood": "Centro", "city": "São Paulo", "state": "SP", "zipCode": "01001000" }, "owner": { "name": "Maria Souza", "email": "maria@exemplo.com", "phone": "11988887777" }}| Field | Rule |
|---|---|
documentNumber |
Valid CPF or CNPJ |
documentType |
CPF or CNPJ, matching the document |
legalName, email, phone |
Required. Valid email. Phone with area code, 10 or 11 digits |
address |
Street, number, neighborhood, city, state and ZIP. Numeric number. State with 2 letters. ZIP with 8 digits |
stateRegistration |
Optional. Becomes the state registration |
owner |
Optional. When it has name, it stores a customer contact |
tradeName |
Optional. Empty: the legal name is used |
New customer: HTTP 201. Customer that already exists for the same document: HTTP 200 with the record already in Demander. POST does not update name, address or contact.
The city must exist in Demander for the city and state pair. The name may come with or without accents.
Response:
{ "customerId": "12345", "documentNumber": "11444777000161", "documentType": "CNPJ", "legalName": "Cliente Exemplo Ltda", "tradeName": "Cliente Exemplo", "status": "ACTIVE", "approvalStatus": "APPROVED", "isExternalAsset": false, "externalAssetReason": null, "email": "compras@exemplo.com", "phone": "11999998888", "stateRegistration": "123456789", "address": { "street": "Rua Exemplo", "number": "100", "neighborhood": "Centro", "city": "São Paulo", "state": "SP", "zipCode": "01001000", "complement": "Sala 2" }}| Customer situation | status |
approvalStatus |
|---|---|---|
| Active and not blocked | ACTIVE |
APPROVED |
| Inactive | INACTIVE |
REJECTED |
| Blocked | BLOCKED |
REJECTED |
isExternalAsset always comes back false.
| HTTP | Code | When |
|---|---|---|
| 400 | INVALID_DOCUMENT |
Document that has neither 11 nor 14 digits |
| 422 | INVALID_DOCUMENT |
CPF or CNPJ with invalid check digits |
| 422 | INVALID_DOCUMENT |
documentType does not match the document |
| 400 | INVALID_REQUEST |
Name, email, phone or address incomplete |
| 404 | CUSTOMER_NOT_FOUND |
Lookup with no customer for the document |
| 422 | CITY_NOT_FOUND |
City and state not found. details brings city and state |
| 409 | CUSTOMER_PROCESSING |
Another write of the same document is still in progress |
The new customer is active, on the vendor and price list from the configuration.
Orders
Section titled “Orders”Create
Section titled “Create”POST /cayena/v1/orders
{ "orderNumber": "20261007001", "customerId": "12345", "deliveryDate": "2026-10-08", "totalAmount": 20.5, "note": "Entregar pela manhã", "items": [ { "productId": "10", "quantity": 2, "unitPrice": 10.25 } ]}| Field | Rule |
|---|---|
orderNumber |
Required text, up to 50 characters. It is the order number in Cayena |
customerId |
customerId returned by the customers API |
deliveryDate |
Date YYYY-MM-DD. It cannot be before today |
totalAmount |
Greater than zero. It must be the sum of quantity × price of the items, in cents |
note |
Optional. Goes to the order remark |
items |
At least one item. productId, quantity and unitPrice required and greater than zero. The same product cannot repeat |
The product must be active, priced on the configured list, and with enough stock. Negative stock counts as zero.
New order: HTTP 201.
{ "orderId": "1500", "orderNumber": "20261007001", "status": "CONFIRMED", "description": "RECEBIDO"}orderId is the order code in Demander. status is the mapping orderStatus. description is the status description in Demander.
The order is stored with origin Cayena, on the vendor, list, branch, type, payment term and initial status from the configuration.
Repeated order
Section titled “Repeated order”Sending the same orderNumber again does not create another order. The response is HTTP 200, with the same fields and "duplicate": true.
This also applies when the order was already delivered or cancelled.
GET /cayena/v1/orders/orderId
orderId is numeric. The response brings orderId, status, description and, when present, orderNumber.
| HTTP | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST |
Required field missing, date in the wrong format, or invalid item |
| 422 | INVALID_DELIVERY_DATE |
Delivery date before today |
| 422 | INVALID_REQUEST |
Total, or quantity/price, less than or equal to zero |
| 422 | DUPLICATE_PRODUCT |
Product repeated on the same order |
| 422 | ORDER_TOTAL_MISMATCH |
totalAmount different from the sum of the items |
| 404 | CUSTOMER_NOT_FOUND |
customerId not found in this company |
| 403 | CUSTOMER_INACTIVE |
Customer inactive or blocked |
| 422 | PRODUCT_NOT_FOUND |
Product inactive or without a price on the list |
| 422 | INSUFFICIENT_STOCK |
Quantity greater than the balance |
| 409 | ORDER_PROCESSING |
The same number is still being stored |
| 404 | ORDER_NOT_FOUND |
Lookup of an order that is not from this company or did not come from Cayena |
| 422 | INVALID_ORDER_STATUS |
Current order status outside the mapping |
Order status
Section titled “Order status”GET /cayena/v1/orders/orderId/status
{ "orderId": "1500", "orderNumber": "20261007001", "orderStatus": "DELIVERED", "orderStatusDescription": "FATURADO", "updatedAt": "2026-10-08T14:30:00-03:00", "deliveredAt": "2026-10-08T14:30:00-03:00"}orderStatus comes from the mapping of the current status. orderStatusDescription is the description registered in Demander. updatedAt is the moment the order entered that status. deliveredAt appears only when that status mapping has entregue true.
Dates use the São Paulo time zone, in ISO format with the offset.
A missing order or a status without a mapping uses the same codes as the order lookup.
Demander or the ERP changes the status, not this API. Cayena only reads it.
Invoice
Section titled “Invoice”GET /cayena/v1/orders/orderId/invoice
The invoice comes from table 170 — Order invoice. The key must be in chaveNfe, with 44 digits, without a mask. The series is in serieNfe.
If there is more than one invoice with a key, the one with the latest date is used.
{ "orderId": "1500", "nfeKey": "35261000000000000000550010000000011000000010", "invoiceNumber": "1", "series": "1", "issuedDate": "2026-10-08T10:00:00-03:00", "totalAmount": 20.5}nfeKey is required in the response. Number, series, date and amount are included only when the invoice has them.
| HTTP | Code | When |
|---|---|---|
| 404 | NFE_NOT_ISSUED |
Order still without an NF-e key |
| 422 | INVALID_NFE_KEY |
Key that does not have 44 digits |
Unexpected error
Section titled “Unexpected error”An unforeseen failure responds HTTP 500, code SYSTEM_ERROR, message Erro inesperado. The internal detail is not in the body. The call stays in the company log.