Pular para o conteúdo

Começar

Company as code

Declare tenant, produtos, apps e webhooks em infi.company.ts — sync como Terraform.

Company as code configura seu catálogo por arquivo: um TypeScript versionado no git, aplicado com a CLI (plan/apply), sem clicar no dashboard pra cada mudança.

Isso é ferramenta de setup, não parte do seu app — quem constrói contra o Infi em runtime usa link de pagamento e SDK. Use a CLI se você prefere catálogo em git a catálogo no dashboard.

A CLI deduz o host da chave

A partir da 0.2.0 ela resolve o host pelo prefixo da chave (sk_test_ → sandbox, sk_live_ → produção), então INFI_API_URL só serve pra apontar pra outro lugar de propósito. Antes da 0.2.0 ele era obrigatório em sandbox.

Arquivo

// infi.company.ts
import { defineCompany } from "@beinfi/sdk";
 
export default defineCompany.fromIntent("prepaid-ai-chat");
 
// ou hand-authored:
export default defineCompany({
  products: [
    {
      key: "ai-chat",
      name: "AI Chat",
      pricingModel: "prepaid",
      billingCycle: "monthly",
      basePrice: "19.90",
      meters: [{ key: "tokens", unit: "token", aggregation: "sum" }],
      // Plan grants — creditam o saldo daquele meter na inscrição do cliente
      grants: [{ meter: "tokens", amount: "50000", on: "cycle" }],
    },
  ],
  webhooks: [{ url: "https://seu-app.com/api/webhooks/infi", events: ["payment.confirmed"] }],
});

defineBilling / infi.billing.ts ainda funcionam como aliases.

O arquivo é carregado como ESM

A CLI importa o .ts direto. Se o package.json do projeto não tem "type": "module", o load falha com "Cannot use import statement outside a module".

Intents

Atalhos que geram um company file sensato. Vivem na CLI e no arquivo — o endpoint público de provisionamento não aceita intent:

IntentUso típico
crmSaaS B2B / CRM
prepaid-ai-chatChat/LLM com créditos por meter
one-timePack / ebook / cobrança única
usage-saasPay-as-you-go metered

Comandos

ComandoPra quêEstado hoje
infi claim create --ref cli --jsonProvisiona tenant claimable + chaveok
infi sync infi.company.tsAplica o estado desejadook
infi sync infi.company.ts --planDry-run (diff)ok
infi pullBackend → infi.company.tsok
infi doctor --jsonSaúde do setup (checks + hints)ok
infi go-live --jsonGuidance claim → conta → KYC → sk_live_ok
infi bootstrap --intent …Claim + company file + sync + doctorok

Grants do plano

Cada produto pode declarar grants[]:

  • on: "cycle" — credita no abrir/renovar o período (assinatura/prepaid)
  • on: "payment" — credita em payment.confirmed (packs one-time)

O meter do grant é real: cada meter tem a sua carteira. Um grant em tokens credita a carteira de tokens, e GET /metering/customers/{id}/wallet devolve o saldo de cada uma:

{ "balances": [ { "meter": "tokens", "balance": "50000", "total": "50000" } ] }

`/credit` é legado e responde outra pergunta

GET /metering/customers/{id}/credit lê só o pool credits legado. Numa inscrição com 50.000 em tokens ele responde 0 — não é saldo zerado, é a carteira errada. Use /wallet?meter=…. A SDK já faz isso sozinha desde a 0.11.1: infi.meter({ meter: "tokens" }) gateia contra a carteira daquele meter.

`creditsPerCycle` ainda existe (mas é legado)

Ele continua nos tipos e continua sendo honrado: a regra é primeiro grants[{ on: "cycle" }], e creditsPerCycle como fallback. Ou seja, arquivo antigo não quebra — mas escreva grants[] em código novo, que é o único jeito de creditar um meter específico ou de creditar on: "payment".

Versão `prepaid` precisa de preço para publicar

Publicar uma versão prepaid exige basePrice positivo ou um preço de meter publicado nela. Sem nenhum dos dois o publish responde 422 e o produto fica em draft — cobrável por ninguém.

Ou seja: dá para ter tier grátis (sem mensalidade) desde que o meter tenha preço, que é o que rateia o consumo da carteira:

{ key: "studio", type: "agent", pricingModel: "prepaid", billingCycle: "monthly",
  meters: [{ key: "tokens", unit: "token", aggregation: "sum", valueProperty: "value" }],
  grants: [{ meter: "tokens", amount: "50000", on: "cycle" }],
  prices: [{ meter: "tokens", model: "per_unit", unitAmount: "0.00004", currency: "BRL" }] }

Toda price é taxa de meter: um valor fixo não é price, é o basePrice da versão.

Webhooks no arquivo

O webhooks[] do company file é aplicado pelo sync — e em sandbox isso bate no 503 secret_store_unavailable, porque tenant de teste não tem cofre de segredos. Não é erro de sintaxe do seu arquivo. Veja webhooks.

Agentes: rodem infi doctor --json, e em qualquer falha leiam InfiError.errors[] — é onde vem { field, description } dizendo o que a API recusou. fix.command / hint aparecem só em alguns códigos de erro.