Basely

Basely API

Referência da API

Uma chave por negócio. Leia os livros, as contas do banco, as transações, as faturas e as contas a pagar de um negócio, escreva neles com a permissão do dono e receba webhooks assinados quando algo muda.

https://api.baselyapp.com/v1openapi.jsonTodas as ferramentas para devs

Começo rápido

  1. O dono do negócio abre API no painel da Basely e cria uma chave com os scopes que o seu sistema precisa.
  2. Guarde a chave no ambiente do seu servidor. Ela aparece uma vez só.
  3. Chame /v1/me para testar a conexão e depois os endpoints que precisar.
curl https://api.baselyapp.com/v1/me \
  -H "Authorization: Bearer bsk_live_..."

curl "https://api.baselyapp.com/v1/financial/summary?period=2026-09" \
  -H "Authorization: Bearer bsk_live_..."

Autenticação

Mande a chave como Authorization: Bearer bsk_live_... em toda requisição.

  • O negócio sai da chave. Não existe id de negócio no pedido, e uma chave nunca alcança outro negócio.
  • Chaves de teste (bsk_test_) leem o negócio de verdade e validam escritas, mas nunca gravam.
  • Só o dono do negócio cria, gira e revoga chaves. A chave para de funcionar quando quem a criou deixa de ser dono.
  • Girar mantém a chave antiga funcionando pelo prazo que você escolher, para o servidor trocar sem cair.

Scopes

Cada chave leva só os scopes que recebeu. Endpoint sem o scope responde 403 SCOPE_DENIED.

ScopeServiçoAcesso
financial:readfinancialLê
accounts:readfinancialLê
transactions:readfinancialLê
transactions:writefinancialEscreve nos livros
invoices:readfinancialLê
invoices:writefinancialEscreve nos livros
bills:readfinancialLê
bills:writefinancialEscreve nos livros
contacts:readcrmLê
contacts:writecrmEscreve
conversations:readphoneLê
texts:sendphoneEscreve
inbox:writephoneEscreve
calls:createphoneEscreve
sessions:managephoneEscreve
otp:sendphoneEscreve
phone_contacts:writephoneEscreve
bank:refreshbankingEm breve
email:sendemailEm breve
sms:sendsmsEm breve
calendar:readcalendarEm breve
calendar:writecalendarEm breve
files:readfilesEm breve

Endpoints

Base abaixo. Toda resposta tem o cabeçalho x-request-id.

Plataforma

GET/v1/meany keyThe business and the key behind this request.

Financeiro

GET/v1/financial/summaryfinancial:readCash, inflows, outflows, expenses, recurring costs, burn and runway for a period.
GET/v1/financial/cash-flowfinancial:readCash flow month by month: operating, investing and financing.
GET/v1/financial/reports/{report}financial:readA financial report: pnl, balance-sheet, expenses-by-category or trial-balance.
GET/v1/accountsaccounts:readBank accounts connected to the books, with their ledger balance.
GET/v1/transactionstransactions:readBank transactions, newest first.
GET/v1/transactions/{id}transactions:readOne bank transaction.
POST/v1/transactions/{id}/categorizetransactions:writePost a bank transaction to a category in the books.
GET/v1/invoicesinvoices:readInvoices, newest first.
POST/v1/invoicesinvoices:writeCreate an invoice.
GET/v1/invoices/{id}invoices:readOne invoice, with what is still open.
POST/v1/invoices/{id}/paymentsinvoices:writeRecord a payment received for an invoice.
GET/v1/billsbills:readBills to pay, newest first.
POST/v1/billsbills:writeCreate a bill to pay.

CRM

GET/v1/contactscontacts:readContacts of the business.
POST/v1/contactscontacts:writeCreate a contact, or update the one with the same email or phone.

Phone e textos

GET/v1/businesses/{negocioId}/conversationsconversations:readMessages and calls, masked.
POST/v1/businesses/{negocioId}/textstexts:sendSend a text from the business number.
POST/v1/businesses/{negocioId}/emailsinbox:writeHand a received email to the inbox.
POST/v1/businesses/{negocioId}/callscalls:createRing a team member.
POST/v1/businesses/{negocioId}/contactsphone_contacts:writeDeclare a customer or a team member.
POST/v1/businesses/{negocioId}/sessionssessions:manageOpen a masked line.
DELETE/v1/businesses/{negocioId}/sessions/{sessionId}sessions:manageClose a masked line.
POST/v1/businesses/{negocioId}/otpotp:sendSend a one-time code by text.
POST/v1/businesses/{negocioId}/otp/verifyotp:sendVerify a one-time code.

Convenções

  • Dinheiro em centavos inteiros com a moeda. Datas em ISO 8601.
  • Listas devolvem object: list, data, has_more. Use limit (até 100) e from/to para filtrar.
  • Os saldos vêm dos livros, que o banco mantém em dia sozinho. Ler nunca pergunta ao banco direto.
  • Atualizar agora no banco (Fresh) é uma ação separada, com cota mensal própria. Ler a API nunca gasta Fresh.
  • Limite por chave por minuto: 120 leituras, 30 escritas, 20 relatórios. Passou: 429 com retry-after.

Erros

O erro tem um código estável, uma mensagem para gente e o id da requisição. Erro de escrita traz reason e field.

{ "error": { "code": "SCOPE_DENIED", "message": "...", "scope": "invoices:write", "request_id": "req_..." } }
MISSING_KEYSend your API key as Authorization: Bearer <key>.
INVALID_KEYThis API key is not valid.
KEY_REVOKEDThis API key was revoked or replaced. Use the current key of this business.
KEY_EXPIREDThis API key expired. Ask the owner of the business for a new one.
NOT_FOUNDNot found.
BUSINESS_FROZENThis business is frozen. Calls resume when it is active again.
BUSINESS_CLOSEDThis business is closed.
SCOPE_DENIEDThis key does not have the scope this endpoint needs.
MODULE_NOT_ENABLEDFinance is not enabled for this business.
BOOKS_NOT_SET_UPThe books of this business are not set up yet.
RATE_LIMITEDToo many requests for this key. Wait and try again.
INVALID_REQUESTThe request is not valid.
INVALID_JSONThe body must be a JSON object.
TEST_MODETest keys validate writes but never save them.
CONFLICTThe books do not allow this change right now. See reason.
FORBIDDENThe owner of this key can no longer do this in the business.
INTERNALSomething failed on our side. Try again; if it keeps failing, send us the request_id.

Webhooks

O dono cadastra o endereço do seu sistema e escolhe os eventos. A entrega tenta de novo por 24 horas.

transaction.createdtransaction.categorizedinvoice.createdinvoice.paidbill.createdbill.paidbank_connection.disconnected

Cada entrega vem assinada com o segredo do endereço em basely-signature e traz basely-event-id para você descartar repetição. Confira assim:

import { createHmac, timingSafeEqual } from "node:crypto";

// header: basely-signature: t=<unix seconds>,v1=<hex>
export function isFromBasely(rawBody: string, header: string, secret: string): boolean {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=") as [string, string]));
  const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return fresh && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ""));
}