Pular para o conteúdo

Datas e dinheiro

Dois formatos que aparecem em quase todo recurso, e que erram fácil quando o integrador supõe em vez de conferir.

Data com hora sai em ISO 8601, sempre em UTC, com o Z no fim:

2026-09-16T14:30:00Z

Campo que guarda só data, sem hora, sai assim:

2026-09-16

O mesmo formato vale nos dois sentidos: o que você manda numa data segue a mesma regra do que você recebe, inclusive nos filtros de paginação.

Campo de data e hora sai sempre assim: created_at e updated_at de qualquer recurso, key.expires_at em GET /public/v1/me, next_follow_up e last_interaction_at no cliente, due_date, started_at e completed_at na tarefa, starts_at e ends_at no compromisso da agenda, scheduled_start, scheduled_end, started_at e completed_at na ordem de serviço, valid_until, estimated_start_date, estimated_completion_date, approved_at e rejected_at no orçamento, e estimated_delivery_date e canceled_at na venda (e canceled_at e created_at em cada recebimento dela). O movimento do extrato de estoque só tem created_at, porque nunca muda depois de gravado. Campo de só data usa YYYY-MM-DD, como o birth_date do cliente e o paid_at e o due_date do plano de pagamento da conversão de orçamento em venda, e na venda o sale_date, o due_date e o paid_date de cada parcela, o paid_at de cada recebimento e as datas dos filtros sale_date_after e sale_date_before. No financeiro, transaction_date, due_date e payment_date do lançamento são só data, nos dois sentidos, como os filtros due_after e due_before.

A API não devolve horário local. Ela devolve UTC e diz qual é o fuso da empresa, em organization.timezone na resposta de GET /public/v1/me:

{
"organization": {
"timezone": "America/Sao_Paulo",
"currency": "BRL"
}
}

O painel mostra as mesmas datas convertidas para esse fuso. Então o mesmo registro aparece como 2026-09-16T14:30:00Z na API e como 16/09/2026 11:30 na tela, e os dois estão certos.

Se o seu sistema mostra data para uma pessoa, converta de UTC para organization.timezone na hora de exibir, e nunca guarde a data já convertida: guardar em UTC é o que faz a conta de horário de verão e de mudança de fuso continuar certa depois.

Dinheiro é inteiro, em centavos, e o nome do campo diz isso com todas as letras:

{ "total_cents": 150000 }

150000 são R$ 1.500,00. O nome sempre termina em _cents, então não existe campo de dinheiro em que você precise adivinhar a unidade.

A moeda vem em organization.currency, na resposta de GET /public/v1/me. A API não converte moeda: o inteiro está na moeda que esse campo diz.

O primeiro campo de dinheiro no ar é o price_cents do compromisso da agenda: price_cents: 15000 são R$ 150,00, e null quer dizer compromisso sem valor. A ordem de serviço também traz dinheiro em centavos: subtotal_cents, discount_cents, additional_costs_cents e total_cents, mais unit_price_cents e total_cents em cada item. O orçamento também: subtotal_cents, discount_cents, additional_costs_cents, total_cents e recurring_total_cents (a mensalidade, que nunca soma com o total), mais unit_price_cents e total_cents em cada item, e entry_amount_cents e amount_cents no plano de pagamento da conversão em venda. A venda traz subtotal_cents, discount_cents, shipping_cents, additional_costs_cents, total_cents e paid_cents, mais unit_price_cents, original_price_cents e total_price_cents em cada item, amount_cents, paid_cents e remaining_cents em cada parcela e amount_cents em cada recebimento; o recebimento e o reagendamento também recebem amount_cents. O produto traz price_cents e promotional_price_cents (e o mesmo par em cada variação), e o serviço traz base_price_cents. A alíquota de ISS do serviço (iss_rate) é fração e sai como número: 0.05 quer dizer 5%. O percentual de desconto (discount_percentage) não é dinheiro e sai como número: 10 quer dizer 10%. O lançamento do financeiro traz amount_cents e, no parcelamento, original_amount_cents; o parcelamento recebe total_amount_cents ou installment_amount_cents. A conta traz opening_balance_cents, current_balance_cents e, no cartão, credit_limit_cents, credit_used_cents e credit_available_cents; o alert_percentage da conta é percentual e sai como número. Clientes, endereços, leads, tarefas e equipe não têm campo de dinheiro. A mesma regra vale para os recursos que chegam nas próximas fases.

Número decimal com ponto flutuante perde centavo. Não é teoria: em qualquer linguagem que use ponto flutuante binário, 0.1 + 0.2 não dá 0.3, e uma soma de mil itens acumula a diferença até o total fechar errado.

Com inteiro isso não acontece. Somar, subtrair e multiplicar por quantidade são contas exatas. A divisão continua pedindo cuidado, porque é onde o arredondamento entra, e aí você decide a regra em vez de descobrir o resultado depois.

Os exemplos imprimem o valor formatado e voltam de texto para centavos com aritmética inteira.

Terminal window
CENTS=150000
# cURL só transporta a resposta. A conta é do shell, com inteiro.
printf 'R$ %d,%02d\n' "$((CENTS / 100))" "$((CENTS % 100))"
# De volta, de "1500,00" para centavos, sem passar por decimal.
VALOR="1500,00"
INTEIRA=${VALOR%%,*}
FRACAO=${VALOR##*,}
printf '%d\n' "$((INTEIRA * 100 + 10#$FRACAO))"

Rode com bash. O 10# obriga a leitura em base 10, senão 08 e 09 viram erro de octal. O separador de milhar fica de fora: agrupamento por idioma é trabalho da linguagem que monta a tela, não do shell.

  • Paginação: os filtros de data das listas usam este mesmo formato.
  • Versões e changelog: por que campo novo numa resposta não quebra a sua integração.