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.
Começo rápido
- O dono do negócio abre API no painel da Basely e cria uma chave com os scopes que o seu sistema precisa.
- Guarde a chave no ambiente do seu servidor. Ela aparece uma vez só.
- 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.
| Scope | Serviço | Acesso |
|---|---|---|
| financial:read | financial | Lê |
| accounts:read | financial | Lê |
| transactions:read | financial | Lê |
| transactions:write | financial | Escreve nos livros |
| invoices:read | financial | Lê |
| invoices:write | financial | Escreve nos livros |
| bills:read | financial | Lê |
| bills:write | financial | Escreve nos livros |
| contacts:read | crm | Lê |
| contacts:write | crm | Escreve |
| conversations:read | phone | Lê |
| texts:send | phone | Escreve |
| inbox:write | phone | Escreve |
| calls:create | phone | Escreve |
| sessions:manage | phone | Escreve |
| otp:send | phone | Escreve |
| phone_contacts:write | phone | Escreve |
| bank:refresh | banking | Em breve |
| email:send | Em breve | |
| sms:send | sms | Em breve |
| calendar:read | calendar | Em breve |
| calendar:write | calendar | Em breve |
| files:read | files | Em breve |
Endpoints
Base abaixo. Toda resposta tem o cabeçalho x-request-id.
Plataforma
| GET | /v1/me | any key | The business and the key behind this request. |
Financeiro
| GET | /v1/financial/summary | financial:read | Cash, inflows, outflows, expenses, recurring costs, burn and runway for a period. |
| GET | /v1/financial/cash-flow | financial:read | Cash flow month by month: operating, investing and financing. |
| GET | /v1/financial/reports/{report} | financial:read | A financial report: pnl, balance-sheet, expenses-by-category or trial-balance. |
| GET | /v1/accounts | accounts:read | Bank accounts connected to the books, with their ledger balance. |
| GET | /v1/transactions | transactions:read | Bank transactions, newest first. |
| GET | /v1/transactions/{id} | transactions:read | One bank transaction. |
| POST | /v1/transactions/{id}/categorize | transactions:write | Post a bank transaction to a category in the books. |
| GET | /v1/invoices | invoices:read | Invoices, newest first. |
| POST | /v1/invoices | invoices:write | Create an invoice. |
| GET | /v1/invoices/{id} | invoices:read | One invoice, with what is still open. |
| POST | /v1/invoices/{id}/payments | invoices:write | Record a payment received for an invoice. |
| GET | /v1/bills | bills:read | Bills to pay, newest first. |
| POST | /v1/bills | bills:write | Create a bill to pay. |
CRM
| GET | /v1/contacts | contacts:read | Contacts of the business. |
| POST | /v1/contacts | contacts:write | Create a contact, or update the one with the same email or phone. |
Phone e textos
| GET | /v1/businesses/{negocioId}/conversations | conversations:read | Messages and calls, masked. |
| POST | /v1/businesses/{negocioId}/texts | texts:send | Send a text from the business number. |
| POST | /v1/businesses/{negocioId}/emails | inbox:write | Hand a received email to the inbox. |
| POST | /v1/businesses/{negocioId}/calls | calls:create | Ring a team member. |
| POST | /v1/businesses/{negocioId}/contacts | phone_contacts:write | Declare a customer or a team member. |
| POST | /v1/businesses/{negocioId}/sessions | sessions:manage | Open a masked line. |
| DELETE | /v1/businesses/{negocioId}/sessions/{sessionId} | sessions:manage | Close a masked line. |
| POST | /v1/businesses/{negocioId}/otp | otp:send | Send a one-time code by text. |
| POST | /v1/businesses/{negocioId}/otp/verify | otp:send | Verify 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_KEY | Send your API key as Authorization: Bearer <key>. |
| INVALID_KEY | This API key is not valid. |
| KEY_REVOKED | This API key was revoked or replaced. Use the current key of this business. |
| KEY_EXPIRED | This API key expired. Ask the owner of the business for a new one. |
| NOT_FOUND | Not found. |
| BUSINESS_FROZEN | This business is frozen. Calls resume when it is active again. |
| BUSINESS_CLOSED | This business is closed. |
| SCOPE_DENIED | This key does not have the scope this endpoint needs. |
| MODULE_NOT_ENABLED | Finance is not enabled for this business. |
| BOOKS_NOT_SET_UP | The books of this business are not set up yet. |
| RATE_LIMITED | Too many requests for this key. Wait and try again. |
| INVALID_REQUEST | The request is not valid. |
| INVALID_JSON | The body must be a JSON object. |
| TEST_MODE | Test keys validate writes but never save them. |
| CONFLICT | The books do not allow this change right now. See reason. |
| FORBIDDEN | The owner of this key can no longer do this in the business. |
| INTERNAL | Something 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.
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 ?? ""));
}