1. Introdução
Esta página é uma documentação complementar da API do ERP Spalla. Ela se dirige ao integrador (ou ao agente de IA) que lê dados do ERP para outro sistema — um painel de BI, um data warehouse, um e-commerce, um CRM — e precisa mantê-lo atualizado sem sobrecarregar a API.
Muitas tabelas do ERP guardam, em cada registro, a data e a hora da última atualização. O servidor preenche esse valor sozinho a cada inclusão e a cada alteração. Com ele, a integração resolve dois problemas de uma vez:
| Objetivo | Como a data de atualização ajuda |
|---|---|
| Dividir a carga de tabelas muito grandes | Em vez de uma consulta aberta, ou de um período longo, que monta tudo numa resposta só, a leitura é feita em pacotes pequenos, cada um começando onde o anterior terminou. |
| Manter a sua base atualizada | Depois da primeira carga, cada ciclo lê só o que foi incluído ou alterado desde o ciclo anterior. Um registro antigo que foi alterado ganha data nova e volta sozinho para a fila de leitura. |
Em uma frase: não releia a tabela inteira a cada sincronização. Leia uma vez, em pacotes, e depois leia só o que mudou, também em pacotes. Cada ciclo passa a trazer uma fração pequena da tabela, e o seu sistema fica poucos minutos atrás do ERP.
Esta página descreve a estratégia e a ordem das chamadas. Ela não substitui o dicionário oficial da sua instância, que é a fonte autoritativa dos nomes de tabela, de coluna e da chave primária.
Usa um agente de IA para escrever a integração? O Spalla API Toolkit ensina esta estratégia a ele: o agente propõe a carga incremental sozinho quando a tarefa pede volume e escreve o código com as três rotinas desta página. Veja a seção Spalla API Toolkit.
2. Onde encontrar o dicionário oficial da sua API
Cada empresa que opera o ERP Spalla expõe o seu próprio dicionário automático em um endereço próprio. Para acessá-lo, abra no navegador a URL no formato abaixo, substituindo os placeholders pelos valores da sua instância:
https://<host>:<porta>/root/dicionario?userToken=<token>
<host>— endereço do servidor que hospeda a API.<porta>— porta de comunicação configurada no servidor.<token>— token de identificação (userToken) fornecido pela equipe responsável pela API.
Para esta página, o dicionário responde três perguntas sobre cada tabela: qual é a coluna de data de atualização, quais colunas formam a chave primária e quais colunas o seu sistema realmente precisa.
3. Como a API funciona
Todas as leituras desta página passam pelo mesmo endpoint da API, com a arquitetura unificada: o verbo HTTP não determina a operação; o campo "method" do corpo JSON determina.
| Item | Valor |
|---|---|
| Endpoint | /root/integrador |
| Verbo HTTP | PATCH (para leitura, ferramentas que não enviam PATCH, como o Power BI, podem usar POST ou GET) |
| Autenticação | Header userToken |
| Operação | "method": "GET" no corpo, sobre "objectType": "TABLE" |
Os parâmetros da consulta que fazem a carga incremental:
| Parâmetro | Papel na carga incremental |
|---|---|
where | Filtro pela data de atualização, com um item "type": "expression": "ME_DTATU >= '2026-09-30 08:14:57'". A data vai com espaço entre a data e a hora, e a hora é obrigatória. |
orderBy | A ordem dos registros. Na sincronização, pela data de atualização; na carga inicial e na reconciliação, pela chave primária. |
limit | Tamanho do pacote: quantos registros cada resposta traz. |
offset | Número da página, a partir de 1 — não é a quantidade de registros a pular. Com "limit": 500, "offset": 1 traz os registros 1 a 500 e "offset": 2 traz os registros 501 a 1000. Use sempre junto com limit e orderBy. |
fields | Só as colunas que o seu sistema usa. Em tabelas com centenas de colunas, é o que mais reduz o tamanho da resposta. |
Três termos aparecem em toda a página:
- Pacote — a resposta de uma consulta com
limit. Pacote cheio é o que voltou com exatamentelimitregistros; pacote incompleto, o que voltou com menos. - Marca — a data e hora a partir da qual o próximo pacote é pedido. O seu sistema guarda uma marca por tabela.
- Ciclo — uma execução da sincronização: pede pacotes a partir da marca até receber um pacote incompleto.
Atenção — o formato da resposta muda com a quantidade de registros. Com dois ou mais registros, a resposta é uma lista. Com um registro, é o próprio objeto, sem a lista em volta. Com nenhum registro, a resposta é o HTTP 404 com a mensagem Nao foram encontrados resultados para a requisicao do tipo GET com os parametros informados [ rowCount:0 ]. Normalize antes de contar ou percorrer: objeto vira lista de um item, e o 404 com rowCount:0 vira lista vazia — um pacote incompleto, e não um erro.
Atenção — consulta sem limit traz tudo de uma vez. A API não impõe um limite de registros por conta própria: sem limit, a resposta contém o resultado inteiro do filtro. Numa tabela de movimento, é esse o pedido que deixa a integração lenta e disputa recursos com os usuários do ERP.
A montagem da requisição — URL base, porta e o header userToken — e as regras de formatação de valores estão na documentação geral da API, em especial Datas, Horas e Data/Hora. Esta página mostra apenas o corpo JSON de cada consulta; os comentários // dos exemplos são explicativos e saem da requisição real.
4. Por que ler em pacotes
Uma consulta aberta sobre uma tabela de movimento obriga o servidor a montar toda a resposta de uma vez, antes de enviar o primeiro registro. A tabela de itens de notas fiscais e de movimentos de estoque, MOVEQ2, tem mais de 200 colunas: algumas dezenas de milhares de linhas completas numa resposta só podem passar de uma centena de megabytes. O tempo de resposta cresce com o volume, e uma falha no meio obriga a recomeçar do zero. O mesmo vale para um filtro de período longo: um ano de notas numa resposta só pesa como a tabela inteira.
| Aspecto | Releitura completa a cada ciclo | Carga incremental em pacotes |
|---|---|---|
| Volume por ciclo | A tabela inteira, sempre, e crescendo com o tempo | Só o que foi incluído ou alterado desde o ciclo anterior |
| Tamanho de cada resposta | Proporcional à tabela | Fixo, definido por você no limit |
| Falha no meio | Recomeça do zero | Recomeça do último pacote gravado |
| Frequência possível | Poucas vezes ao dia, fora do horário de pico | A cada poucos minutos |
| Registro antigo alterado | Só aparece na próxima releitura completa | Volta na leitura seguinte, com a data nova |
A diferença de volume é grande na prática. Numa base real de itens de nota, menos de 5% dos registros foram incluídos ou alterados em 30 dias, e menos de 0,5% nas últimas 24 horas. Nesse ritmo, numa tabela de 1 milhão de itens, reler tudo a cada 15 minutos traria 96 milhões de registros por dia; a carga incremental traria alguns milhares. Entre os itens alterados no mês havia itens de movimentos lançados meses antes: registros antigos que voltaram à fila e que uma leitura pela data de lançamento não traria de volta.
5. Como a data de atualização funciona
O comportamento é o mesmo em todas as tabelas que têm o controle:
- O servidor preenche o valor sozinho, na inclusão e em cada alteração do registro, com data, hora e fração de segundo. A integração não precisa gravar essa coluna: um valor enviado num
POSTouPUTé substituído pelo do servidor. - A alteração de uma única coluna já basta. As inclusões e alterações feitas pela tela do ERP ou pela API atualizam a data do registro.
- Em regra, a data é do registro daquela tabela. Alterar um item não muda a data do cabeçalho, e alterar o cabeçalho não muda a data dos itens. Nos movimentos, o controle fica nos itens (
MOVEQ2); o cabeçalho (MOVEQ1) não tem coluna equivalente e se acompanha pela data do movimento (Passo 4). No cadastro de pessoas é o contrário, e a favor de quem integra: alterar um contato ou um endereço da pessoa também atualiza aCF_DTATUdaCLIFOR1. - Exclusões não aparecem na fila. Um registro excluído deixa de existir, e não há data a consultar. O cancelamento de uma nota fiscal, por exemplo, remove os itens dela da
MOVEQ2. Quem reflete as exclusões no seu sistema é a releitura dos cabeçalhos recentes (Passo 4) e a reconciliação (Passo 6).
Nem toda coluna de data é de atualização. Leia a descrição da coluna no dicionário antes de adotá-la como filtro. Colunas terminadas em _DATPRO costumam guardar a data de criação do registro, que não muda depois. Colunas *_STATUSDATA mudam só quando o status muda. As colunas de atualização aparecem no dicionário com descrições como Data da última alteração ou Data de Atualização do Cadastro, e os nomes variam de tabela para tabela: _DTATU, _DTMOD, _DHATU, _DATATU, entre outros.
6. Tabelas com o controle
As tabelas abaixo são as mais procuradas em integrações de leitura e têm a coluna de atualização preparada para consulta por faixa de data, que é exatamente a consulta desta página:
| Tabela | Conteúdo | Coluna | Chave primária |
|---|---|---|---|
MOVEQ2 | Itens das notas fiscais e das movimentações de estoque | ME_DTATU | FL_COD, CF_TIPO, FT_COD, ME_COD, ME_ITM, PD_COD |
MOVEQC | Custo dos itens de compra | ME_DTATU | a mesma da MOVEQ2 |
CLIFOR1 | Cadastro de pessoas (clientes e fornecedores) | CF_DTATU | CF_COD |
PROD | Cadastro de produtos | PD_DATA | PD_COD |
PRODBARCOD | Códigos de barras dos produtos | PD_DTMOD | PD_COD, PD_BARCOD |
O mesmo controle existe em muitas outras tabelas do ERP. Para usar a estratégia numa tabela fora desta lista, confirme a coluna no dicionário e alinhe com o gestor do ERP Spalla o volume e o horário das cargas.
Os nomes da PROD fogem do padrão. A data de atualização do produto é PD_DATA, sem sufixo que a identifique. É o exemplo de por que a coluna se confirma pela descrição no dicionário e pelo comportamento (Passo 1), e não pelo nome.
7. Diagrama das três rotinas
A integração é organizada em três rotinas com frequências diferentes: a carga inicial (Passo 2) roda uma vez; a sincronização incremental (Passos 3 a 5) roda a cada poucos minutos; a reconciliação (Passo 6) roda com pouca frequência, fora do horário de pico.
Convenção visual: azul (Retaguarda do ERP) · verde (Via API) · cinza (no seu sistema).
8. Aviso sobre liberação na retaguarda
Atenção — a tabela precisa estar liberada para leitura no seu token.
Cada chave de API alcança uma lista própria de objetos, definida na retaguarda do ERP. Antes de começar:
- Confirme no dicionário da sua instância que a tabela aparece e aceita
GET. Se faltar, peça ao gestor do ERP Spalla a revisão do cadastro de API. - Alinhe a carga inicial com o gestor do ERP Spalla quando a tabela for grande: ela roda uma vez, mas é a rotina que mais consome, e o melhor horário é fora do pico de uso do ERP.
Para cada tabela que a integração vai ler, anote três coisas no dicionário da sua instância:
| O que anotar | Por quê |
|---|---|
| A coluna de data de atualização | É o filtro de todos os ciclos. Confirme pelo tipo (timestamp) e pela descrição, não só pelo nome (seção 5). |
| As colunas da chave primária | São a ordem da carga inicial e da reconciliação, e a chave da gravação no seu sistema. |
| As colunas que o seu sistema usa | Vão no fields. Numa tabela de centenas de colunas, pedir só as necessárias reduz a resposta várias vezes. |
Confirme a coluna também pelo comportamento. A consulta da marca inicial (Passo 2) devolve a maior data da coluna: numa tabela em uso, ela é recente. Se a maior data for antiga numa tabela que você sabe que está em uso, a coluna não é a de atualização.
Os exemplos desta página usam a MOVEQ2, com a coluna ME_DTATU e a chave FL_COD, CF_TIPO, FT_COD, ME_COD, ME_ITM, PD_COD. Para outra tabela, troque os nomes pelos do dicionário.
A carga inicial (rotina 1) traz a tabela inteira uma única vez. Ela tem duas etapas, nesta ordem.
Primeiro, guarde a marca inicial: a maior data de atualização que existe na tabela antes de a carga começar. É dela que a sincronização parte, e é ela que garante que nada alterado durante a carga fique para trás.
{
"method": "GET"
,"objectType": "TABLE"
,"objectName": "MOVEQ2"
// Só a coluna de atualização
,"fields": ["ME_DTATU"]
// A maior data primeiro
,"orderBy": ["ME_DTATU DESC"]
,"limit": 1
}
Como o pedido é de um registro só, a resposta é o próprio objeto (seção 3):
{
"ME_DTATU": "2026-09-30T08:14:57.321"
}
Guarde esse valor como a marca, no formato de filtro: troque o T por espaço e descarte a fração de segundo — 2026-09-30 08:14:57. Descartar a fração deixa a marca um instante mais cedo, e nunca mais tarde, que é o lado seguro. Se o valor vier só com a data, acrescente 00:00:00. Se a tabela estiver vazia (resposta 404 com rowCount:0), use uma marca antiga, como 1900-01-01 00:00:00.
Depois, leia a tabela em pacotes, na ordem da chave primária, avançando o offset de 1 em 1:
{
"method": "GET"
,"objectType": "TABLE"
,"objectName": "MOVEQ2"
// Só as colunas que o seu sistema usa, com a chave e a data de atualização
,"fields": ["FL_COD", "CF_TIPO", "FT_COD", "ME_COD", "ME_ITM", "PD_COD", "ME_QTD", "ME_PRC", "ME_VALOR", "ME_DTATU"]
// Ordem estável: a chave primária completa
,"orderBy": ["FL_COD ASC", "CF_TIPO ASC", "FT_COD ASC", "ME_COD ASC", "ME_ITM ASC", "PD_COD ASC"]
// Tamanho do pacote
,"limit": 500
// Número da página: 1, 2, 3...
,"offset": 1
}
Grave cada pacote no seu sistema (Passo 4) antes de pedir o próximo, e guarde junto a marca inicial e o número da última página gravada. Se o processo parar, retome da página seguinte com a mesma marca inicial, sem recalculá-la. A carga inicial termina quando vier um pacote incompleto.
- Chave de uma coluna só (
CLIFOR1,PROD): em vez dooffset, avance pela chave —"CF_COD > 1500", com o último código do pacote anterior, eorderBypela chave. Cada pacote começa exatamente onde o anterior terminou. - Tabelas de milhões de registros: quanto mais funda a página, mais trabalho cada pacote pede ao servidor. Alinhe o horário e o tamanho do pacote com o gestor do ERP Spalla.
Por que a carga inicial não usa a data de atualização. Em tabelas com histórico anterior ao controle, uma parte grande dos registros compartilha a mesma data e hora. Lida pela data, essa parte cairia inteira no caso do empate (Passo 3), página a página. A ordem da chave primária divide a tabela por igual.
Valores ilustrativos. As datas, as colunas escolhidas e o tamanho do pacote dos exemplos são ilustrativos. Os nomes reais das colunas estão no dicionário da sua instância.
Cada ciclo da sincronização (rotina 2) pede os registros com data de atualização igual ou posterior ao início do ciclo, em ordem de data. O início é a marca guardada menos uma margem de alguns minutos — por exemplo, 5 — explicada no Passo 5.
{
"method": "GET"
,"objectType": "TABLE"
,"objectName": "MOVEQ2"
,"fields": ["FL_COD", "CF_TIPO", "FT_COD", "ME_COD", "ME_ITM", "PD_COD", "ME_QTD", "ME_PRC", "ME_VALOR", "ME_DTATU"]
,"where": [
{
// Início do ciclo: a marca menos a margem, com espaço entre a data e a hora
"type": "expression"
,"value": "ME_DTATU >= '2026-09-30 08:09:57'"
}
]
// Só pela data de atualização
,"orderBy": ["ME_DTATU ASC"]
// Tamanho do pacote; sem offset
,"limit": 500
}
Grave o pacote (Passo 4) e decida pelo tamanho da resposta:
- Pacote incompleto: a leitura alcançou o presente. Guarde a marca e encerre o ciclo (Passo 5).
- Pacote cheio: há mais a ler. O próximo pacote começa na maior
ME_DTATUdo pacote, sem a fração de segundo.
Como o filtro é >= e o início perde a fração de segundo, os últimos registros de um pacote voltam no começo do seguinte. É de propósito: reler um registro não causa dano, porque a gravação é pela chave (Passo 4), e perder um registro causaria. Pela mesma razão, a ordem só precisa ser pela data: registros do mesmo segundo que ficaram na fronteira são relidos juntos no pacote seguinte.
Na sincronização, o pacote avança pela data, não pelo offset. Durante a leitura, os usuários continuam trabalhando no ERP. Um registro alterado entre um pacote e outro muda de posição na ordem de data, e a contagem por páginas passa a pular um registro que não mudou — em silêncio. Avançando pela data, cada pacote começa no valor em que o anterior terminou, e o que foi alterado no meio do caminho reaparece mais adiante.
Empate — pacote cheio em que todos os registros têm o mesmo segundo. Se a maior data do pacote, sem a fração de segundo, for igual ao início com que ele foi pedido, o próximo pacote pediria o mesmo segundo de novo, para sempre. Nesse caso, leia aquele segundo inteiro por páginas, na ordem da chave, e siga com o início no segundo seguinte:
{
"method": "GET"
,"objectType": "TABLE"
,"objectName": "MOVEQ2"
,"fields": ["FL_COD", "CF_TIPO", "FT_COD", "ME_COD", "ME_ITM", "PD_COD", "ME_QTD", "ME_PRC", "ME_VALOR", "ME_DTATU"]
,"where": [
{
// Só o segundo do empate
"type": "expression"
,"value": "ME_DTATU >= '2026-09-30 08:14:57' AND ME_DTATU < '2026-09-30 08:14:58'"
}
]
,"orderBy": ["FL_COD ASC", "CF_TIPO ASC", "FT_COD ASC", "ME_COD ASC", "ME_ITM ASC", "PD_COD ASC"]
,"limit": 500
// Aqui, sim, página a página: 1, 2, 3...
,"offset": 1
}
Calcule o segundo seguinte por aritmética de data (ele pode virar o dia). O empate acontece quando muitos registros são gravados de uma só vez; com o pacote de tamanho adequado, é a exceção do ciclo, não a regra.
O laço completo de um ciclo, em pseudo-código:
marca = ler_marca("MOVEQ2") // guardada no seu sistema, uma por tabela
inicio = marca - 5 minutos // margem (Passo 5)
repetir:
linhas = GET where ME_DTATU >= inicio, orderBy ME_DTATU, limit N
linhas = normalizar(linhas) // objeto: lista de um item; 404 rowCount:0: lista vazia
se linhas vazia: parar
gravar_pela_chave(linhas) // Passo 4
maior = truncar_segundo(max(ME_DTATU das linhas))
guardar_marca("MOVEQ2", max(marca, maior))
se quantidade(linhas) < N: parar // pacote incompleto: ciclo encerrado
se maior == inicio: // empate
ler o segundo 'inicio' por páginas, na ordem da chave
inicio = inicio + 1 segundo
senão:
inicio = maior
falha temporária (tempo esgotado, 5xx): repetir a mesma requisição, com espera crescente
400 ou 401: parar e registrar — não é temporário
Cada registro recebido, em qualquer das três rotinas, é incluído ou atualizado no seu sistema pela chave primária: se a chave já existe, atualize; se não existe, inclua. É essa gravação que torna inofensivas as releituras do Passo 3 e da retomada depois de uma falha.
- Guarde a chave completa. Na
MOVEQ2são seis colunas; guardar só parte delas faz itens diferentes se sobrescreverem. - Guarde também a data de atualização de cada registro. Ela permite conferir, do seu lado, se o dado que você tem é o mais recente.
Cabeçalhos dos movimentos. O cabeçalho (MOVEQ1, com a chave FL_COD, CF_TIPO, FT_COD, ME_COD) não tem coluna de atualização. Acompanhe-o pela data do movimento, ME_ENT, que é preparada para consulta por faixa: releia periodicamente — por exemplo, a cada hora — os cabeçalhos dos últimos dias, com as colunas que o seu sistema usa.
{
"method": "GET"
,"objectType": "TABLE"
,"objectName": "MOVEQ1"
// A chave, a indicação de cancelamento e as colunas que o seu sistema usa
,"fields": ["FL_COD", "CF_TIPO", "FT_COD", "ME_COD", "ME_ENT", "ME_CANCEL"]
,"where": [
{
// Movimentos dos últimos 7 dias
"type": "expression"
,"value": "ME_ENT >= '2026-09-23 00:00:00'"
}
]
,"orderBy": ["FL_COD ASC", "CF_TIPO ASC", "FT_COD ASC", "ME_COD ASC"]
,"limit": 500
,"offset": 1
}
Nota cancelada. O cancelamento de uma nota fiscal remove os itens dela da MOVEQ2 e marca o cabeçalho com ME_CANCEL = 1. Os itens não voltam pela fila, porque deixaram de existir. Na releitura dos cabeçalhos, remova do seu sistema os itens das notas com ME_CANCEL = 1. Alterações em cabeçalhos mais antigos que a janela da releitura ficam para a reconciliação completa (Passo 6).
A integração guarda uma marca por tabela (e, durante a carga inicial, também a última página gravada). Quatro regras a mantêm confiável:
| Regra | Por quê |
|---|---|
| Grave a marca só depois do pacote | A marca avança quando os registros do pacote já estão gravados no seu sistema. Se o processo cair no meio, o ciclo seguinte relê o último pacote em vez de pulá-lo. |
| A marca vem dos dados, não do relógio | Use a maior data de atualização recebida, nunca a hora do seu servidor. O relógio da sua máquina e o do servidor do ERP não são o mesmo, e a diferença vira registro perdido. |
| Grave a marca sem a margem | A margem entra só no início da leitura (Passo 3). Gravada na marca, ela recuaria a cada ciclo. |
| Comece cada ciclo com a margem | A data é marcada no início da gravação do registro, e ele só aparece na leitura quando a gravação termina. Numa gravação demorada, um registro pode surgir com data um pouco anterior à da última leitura. A margem de alguns minutos o alcança, e a gravação pela chave descarta a repetição. Ela precisa ser maior que a gravação mais longa da tabela; o que escapar dela, a reconciliação completa corrige. |
Com a marca e a margem, o intervalo entre os ciclos é decisão do seu negócio. A cada 5 a 15 minutos é o ponto de partida comum: os ciclos ficam curtos e a sua base fica poucos minutos atrás do ERP.
A reconciliação (rotina 3) faz parte da integração, e não é opcional. Ela cobre o que a data de atualização não alcança — os registros excluídos no ERP — e qualquer divergência que tenha escapado dos ciclos, como uma falha de gravação no seu próprio sistema.
Reconciliação de chaves. Leia só as colunas da chave, em pacotes, na ordem da chave. Sem as demais colunas, cada registro ocupa poucos bytes e o pacote pode ser bem maior:
{
"method": "GET"
,"objectType": "TABLE"
,"objectName": "MOVEQ2"
// Só a chave primária
,"fields": ["FL_COD", "CF_TIPO", "FT_COD", "ME_COD", "ME_ITM", "PD_COD"]
,"orderBy": ["FL_COD ASC", "CF_TIPO ASC", "FT_COD ASC", "ME_COD ASC", "ME_ITM ASC", "PD_COD ASC"]
,"limit": 5000
,"offset": 1
}
Compare as chaves recebidas com as do seu sistema nos dois sentidos:
| Divergência | O que fazer |
|---|---|
| A chave está no seu sistema e não veio do ERP | Confirme com uma consulta pela chave completa, com "type": "field" em cada coluna. Se a resposta for 404 com rowCount:0, o registro foi excluído: remova-o do seu lado. |
| A chave veio do ERP e não está no seu sistema | Busque o registro pela chave e inclua-o. |
A confirmação antes de remover importa: enquanto a reconciliação lê as páginas, o ERP continua em uso, e uma exclusão no meio do caminho desloca as páginas seguintes. A comparação nos dois sentidos também alcança o registro que uma carga inicial tenha deixado para trás pelo mesmo motivo — por isso vale rodá-la logo depois da carga inicial, e depois uma vez por dia, fora do horário de pico.
Reconciliação completa. De tempos em tempos — semanal ou mensal, conforme o volume — repita a leitura da carga inicial (Passo 2) comparando os valores com os do seu sistema. Ela garante que a sua base e o ERP continuam iguais.
Boas práticas
- Declare sempre o
limit. É ele que define o tamanho da resposta. Comece com 500 registros por pacote numa tabela larga como aMOVEQ2e ajuste pelo tempo de resposta: pacotes que voltam em poucos segundos são o ponto certo. Com poucas colunas emfields, o pacote pode ser maior. - Peça só as colunas que usa. Nas tabelas de movimento,
fieldsé o que mais reduz o volume trafegado. - Ordene sempre que paginar. Sem
orderBy, a ordem dos registros não é garantida, e páginas seguidas podem repetir ou deixar registros de fora.offsetvai sempre junto comlimit. - Normalize a resposta. Um registro chega como objeto, e nenhum registro chega como
404comrowCount:0(seção 3). - Uma requisição por vez para cada tabela. Disparar vários pacotes em paralelo contra a mesma tabela troca uma consulta grande por várias ao mesmo tempo — a carga no servidor é a mesma.
- Respeite o formato de data de cada lugar. No
where, a data vai com espaço ('2026-09-30 08:14:57'); a API devolve comT("2026-09-30T08:14:57.321"). Converta antes de reutilizar o valor como marca. - Compare com
>=, nunca com=. A data de atualização guarda fração de segundo, e uma comparação por igualdade sem a fração não encontra nada. Como limite superior, use<o segundo seguinte, e não<=. - Deixe as cargas pesadas para fora do pico. A carga inicial e a reconciliação são as rotinas que mais consomem; a sincronização incremental, com pacotes pequenos, pode rodar durante o expediente.
Resultados inesperados e erros
| Situação | Causa | O que fazer |
|---|---|---|
| Consulta demorada ou resposta muito grande | Consulta sem limit, com pacote grande demais ou com todas as colunas | Declarar o limit, reduzir o pacote e restringir o fields. |
404 — ... [ rowCount:0 ] | Nenhum registro atende ao filtro | Tratar como pacote vazio: fim da leitura, não erro. |
| Contagem de registros errada com um único registro | Um registro chega como objeto, não como lista | Normalizar a resposta para lista antes de contar. |
400 no filtro de data | Data com T ou sem a hora no where | Usar 'AAAA-MM-DD HH:MM:SS', com espaço. |
| Filtro de data sem nenhum registro, com dados existentes | Comparação por = contra uma data que tem fração de segundo | Comparar com >=. |
| O ciclo pede o mesmo pacote sem parar | Empate: pacote cheio com todos os registros no mesmo segundo | Ler aquele segundo por páginas, na ordem da chave (Passo 3). |
| Registros faltando na carga inicial | offset tratado como quantidade de registros a pular, ou páginas sem orderBy | Avançar o offset de 1 em 1, com orderBy pela chave, e reconciliar nos dois sentidos (Passo 6). |
| Itens de nota cancelada continuam no seu sistema | O cancelamento remove os itens, e a exclusão não gera data de atualização | Reler os cabeçalhos recentes e remover os itens das notas com ME_CANCEL = 1 (Passo 4). |
| Registro excluído no ERP continua no seu sistema | A exclusão não gera data de atualização | Reconciliar pelas chaves (Passo 6). |
401 com a mensagem contendo nao esta listado nas opcoes desta integracao | Tabela não liberada no token | Pedir a liberação ao gestor do ERP Spalla (seção 8). |
Spalla API Toolkit — deixe a sua IA montar a integração do jeito certo
Se você usa um agente de inteligência artificial para escrever ou operar a integração, o Spalla API Toolkit ensina a ele esta estratégia e as regras da API de que ela depende. Com o toolkit instalado, o agente:
- propõe a carga incremental por conta própria quando a tarefa é extrair volume, sincronizar ou alimentar um painel, em vez de escrever a consulta aberta;
- encontra no dicionário da sua instância a coluna de data de atualização e a chave primária de cada tabela, e confere a coluna antes de usá-la;
- escreve o código de sincronização com as três rotinas desta página: marca inicial, pacotes que avançam pela data, gravação pela chave, tratamento do empate, releitura dos cabeçalhos e reconciliação nos dois sentidos;
- aplica sozinho os formatos de data do filtro e do retorno, o significado correto do
offsete a normalização da resposta; - limita as próprias consultas de exploração, e avisa quando uma leitura ampla de tabela pede a estratégia de pacotes.
Como obter: o toolkit é distribuído de forma controlada. Fale com o gestor do ERP Spalla da sua empresa, que também fornece o endereço da API e o userToken da integração. Recursos e vantagens na documentação geral da API.
Dica — identificar a tabela e o campo a partir da interface do ERP
Atalho para inspeção visual. Ao operar uma tela diretamente pela interface do ERP, pressione Ctrl + Alt + Shift + H. O sistema altera o título da janela indicando que o modo de identificação está ativo. A partir daí, ao parar o mouse sobre qualquer campo da tela, aparece um hintbox mostrando o nome da tabela e do campo aos quais ele está vinculado. É a forma mais rápida de descobrir em que tabela está a informação que o seu sistema precisa — e, com ela, conferir no dicionário se a tabela tem a coluna de atualização.
Relação com os demais guias
- Cadastro de Pessoas — sincronização de clientes e fornecedores pela
CF_DTATU. Ver Cadastro de Pessoas. - Cadastro de Produto — sincronização de produtos pela
PD_DATAe dos códigos de barras pelaPD_DTMOD. Ver Cadastro de Produto. - Análise de Vendas — quando o objetivo é um painel de vendas e faturamento, as consultas da Análise de Vendas entregam o dado já consolidado, sem a necessidade de trazer os itens das notas para o seu lado. Ver Análise de Vendas.
Onde aprofundar
Os nomes de tabela, de coluna e da chave primária de cada objeto estão no dicionário oficial da sua instância (seção 2), que é a fonte autoritativa. A montagem da requisição — URL base, porta, autenticação, os parâmetros do GET e o formato dos valores — está na documentação geral da API.