API: Subscription¶
Visão Geral¶
Consulta de informações da assinatura do tenant autenticado. Créditos de IA são consumidos a cada resposta de IA e atualizados em tempo real via SignalR.
Endpoints¶
GET /api/subscription/credits¶
Retorna o saldo de créditos e status da assinatura do tenant.
Autorização: Qualquer usuário autenticado
Response (200 OK) — com assinatura:
Response (200 OK) — sem assinatura:
Campos:
| Campo | Tipo | Descrição |
|---|---|---|
| balance | int | Saldo de créditos (-1 se tenant não possui assinatura) |
| status | string? | Status da assinatura (null se sem assinatura) |
Status possíveis:
| Status | Descrição |
|---|---|
| Active | Assinatura ativa, todos os recursos disponíveis |
| AiQuotaExhausted | Créditos esgotados, sistema funciona sem IA |
| PastDue | Pagamento vencido, grace period de 24h |
| Suspended | Suspenso após grace period |
| Cancelled | Cancelado após 30 dias de suspensão |
GET /api/subscription/details¶
Retorna detalhes completos da assinatura: status, período, créditos, preço, configuração contratada e uso atual de recursos.
Autorização: Admin
Response (200 OK):
{
"status": "Active",
"currentPeriodStart": "2024-01-01T00:00:00Z",
"currentPeriodEnd": "2024-02-01T00:00:00Z",
"creditBalance": 450,
"monthlyCreditPackage": 500,
"monthlyPriceBrl": 199.90,
"contractedInstances": 2,
"totalAgents": 6,
"extraAgents": 2,
"extraUsers": 0,
"extraKbBlocks": 1,
"extraStepBlocks": 0,
"extraAttachmentBlocks": 0,
"extraFaqBlocks": 0,
"agents": { "current": 3, "max": 6 },
"users": { "current": 2, "max": 4 },
"instances": { "current": 2, "max": 2 },
"knowledgeDocs": { "current": 7, "max": 15 },
"steps": { "current": 4, "max": 10 },
"faqs": { "current": 12, "max": 100 },
"attachmentStorage": { "currentBytes": 52428800, "maxBytes": 1073741824 }
}
Response (404 Not Found): Tenant sem assinatura.
Campos:
| Campo | Tipo | Descrição |
|---|---|---|
| status | string | Status da assinatura |
| currentPeriodStart | DateTime | Início do período atual |
| currentPeriodEnd | DateTime | Fim do período atual |
| creditBalance | int | Saldo de créditos |
| monthlyCreditPackage | int | Franquia mensal de créditos |
| monthlyPriceBrl | decimal | Valor mensal total em BRL |
| contractedInstances | int | Instâncias contratadas |
| totalAgents | int | Total de agentes permitidos (free + extras) |
| extraAgents | int | Blocos extras de agentes |
| extraUsers | int | Blocos extras de usuários |
| extraKbBlocks | int | Blocos extras de documentos de conhecimento |
| extraStepBlocks | int | Blocos extras de steps |
| extraAttachmentBlocks | int | Blocos extras de armazenamento de anexos |
| extraFaqBlocks | int | Blocos extras de FAQs |
| agents | ResourceUsage | Agentes criados vs limite |
| users | ResourceUsage | Usuários criados vs limite |
| instances | ResourceUsage | Instâncias criadas vs limite contratado |
| knowledgeDocs | ResourceUsage | Documentos de conhecimento vs limite |
| steps | ResourceUsage | Steps criados (total do tenant) vs limite |
| faqs | ResourceUsage | FAQs criadas (total do tenant) vs limite |
| attachmentStorage | StorageUsage | Bytes usados vs limite em bytes |
ResourceUsage: { current: int, max: int }
StorageUsage: { currentBytes: long, maxBytes: long }
GET /api/subscription/pricing¶
Retorna o catálogo de produtos e pacotes de créditos disponíveis para composição da assinatura.
Autorização: Qualquer usuário autenticado (rate limited)
Response (200 OK):
{
"products": [
{
"code": "instance",
"name": "Instância WhatsApp",
"description": "Conexão com número WhatsApp",
"priceBrl": 79.90,
"priceUsd": 14.99
}
],
"monthlyPackages": [
{
"credits": 500,
"priceBrl": 49.90,
"priceUsd": 9.99,
"discountPercent": 0,
"isAddon": false
}
],
"addonPackages": [
{
"credits": 100,
"priceBrl": 12.90,
"priceUsd": 2.49,
"discountPercent": 0,
"isAddon": true
}
]
}
Campos:
| Campo | Tipo | Descrição |
|---|---|---|
| products | SubscriptionProductResponse[] | Produtos modulares (instância, agentes extras, etc.) |
| monthlyPackages | CreditPackageResponse[] | Pacotes de créditos mensais (não-addon) |
| addonPackages | CreditPackageResponse[] | Pacotes avulsos de créditos (addon) |
SubscriptionProductResponse:
| Campo | Tipo | Descrição |
|---|---|---|
| code | string | Código único do produto |
| name | string | Nome de exibição |
| description | string? | Descrição do produto |
| priceBrl | decimal | Preço unitário em BRL |
| priceUsd | decimal | Preço unitário em USD |
CreditPackageResponse:
| Campo | Tipo | Descrição |
|---|---|---|
| credits | int | Quantidade de créditos |
| priceBrl | decimal | Preço em BRL |
| priceUsd | decimal | Preço em USD |
| discountPercent | int | Desconto percentual (0-100) |
| isAddon | bool | Se é pacote avulso (addon) |
POST /api/subscription/calculate¶
Calcula o preço mensal estimado com base na configuração desejada, sem alterar a assinatura.
Autorização: Qualquer usuário autenticado (rate limited)
Request:
{
"instances": 2,
"extraAgents": 1,
"extraUsers": 0,
"extraKbBlocks": 1,
"extraStepBlocks": 0,
"extraAttachmentBlocks": 0,
"extraFaqBlocks": 0,
"monthlyCreditPackage": 500
}
Validação:
| Campo | Regra |
|---|---|
| instances | >= 1, <= 100 |
| extraAgents | >= 0, <= 100 |
| extraUsers | >= 0, <= 100 |
| extraKbBlocks | >= 0, <= 100 |
| extraStepBlocks | >= 0, <= 100 |
| extraAttachmentBlocks | >= 0, <= 100 |
| extraFaqBlocks | >= 0, <= 100 |
| monthlyCreditPackage | >= 0, <= 100000 |
Response (200 OK):
{
"monthlyPriceBrl": 259.70,
"monthlyPriceUsd": 49.97,
"limits": {
"maxAgents": 5,
"maxUsers": 4,
"maxKnowledgeDocs": 15,
"maxSteps": 10,
"maxAttachmentStorageMb": 1024,
"maxFaqs": 100
},
"lineItems": [
{
"code": "instance",
"name": "Instância WhatsApp",
"quantity": 2,
"unitPriceBrl": 79.90,
"totalBrl": 159.80,
"unitPriceUsd": 14.99,
"totalUsd": 29.98
},
{
"code": "extra_agent",
"name": "Agente Extra",
"quantity": 1,
"unitPriceBrl": 49.90,
"totalBrl": 49.90,
"unitPriceUsd": 9.99,
"totalUsd": 9.99
},
{
"code": "credit_package",
"name": "Créditos Mensais (500)",
"quantity": 1,
"unitPriceBrl": 49.90,
"totalBrl": 49.90,
"unitPriceUsd": 9.99,
"totalUsd": 9.99
}
]
}
Campos:
| Campo | Tipo | Descrição |
|---|---|---|
| monthlyPriceBrl | decimal | Total mensal em BRL |
| monthlyPriceUsd | decimal | Total mensal em USD |
| limits | CalculatedLimitsResponse | Limites resultantes da configuração |
| lineItems | PriceLineItem[] | Detalhamento por item (somente items com quantity > 0) |
PriceLineItem:
| Campo | Tipo | Descrição |
|---|---|---|
| code | string | Código do produto (constantes em ProductCodes) |
| name | string | Nome de exibição |
| quantity | int | Quantidade |
| unitPriceBrl | decimal | Preço unitário BRL |
| totalBrl | decimal | Total do item BRL |
| unitPriceUsd | decimal | Preço unitário USD |
| totalUsd | decimal | Total do item USD |
Response (400 Bad Request): Validação falhou — retorna erros do FluentValidation.
Atualização em Tempo Real¶
O saldo de créditos é atualizado via SignalR sempre que um crédito é consumido ou adicionado:
O frontend usa SubscriptionStateService para cache client-side (TTL 5 min). O SignalR atualiza o cache diretamente — componentes reagem via evento OnChanged sem re-fetch da API.
Erros Comuns¶
| Status | Código | Descrição |
|---|---|---|
| 401 | UNAUTHORIZED | Token JWT ausente ou inválido |
| 400 | VALIDATION_ERROR | Campos inválidos no request (CalculatePrice) |
| 404 | NOT_FOUND | Tenant sem assinatura (GetDetails) |