Eventos de Uso
Registro e consulta de eventos de uso para cobrança baseada em consumo.
Endpoints
| Método | Endpoint | Descrição | Permissão |
|---|---|---|---|
| POST | /billing/api/v1/usage | Registrar evento | BILLING_USAGE_CREATE |
| POST | /billing/api/v1/usage/batch | Registrar em lote | BILLING_USAGE_CREATE |
| GET | /billing/api/v1/usage/summary | Resumo de uso | BILLING_USAGE_READ |
Atributos do Evento
| Campo | Tipo | Descrição |
|---|---|---|
billingAccountId | string (UUID) | ID da conta de faturamento |
meterId | string (UUID) | ID do medidor (opcional se usar eventName) |
eventName | string | Nome do evento (opcional se usar meterId) |
idempotencyKey | string | Chave única para deduplicação |
eventTimestamp | datetime | Timestamp do evento |
quantity | number | Quantidade/valor do evento |
properties | object | Propriedades adicionais |
Registrar Evento de Uso
POST /billing/api/v1/usage
Registra um único evento de uso. Usa idempotencyKey para evitar duplicatas.
Atributos
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
billingAccountId | string | Sim | ID da conta de faturamento |
meterId | string | Não* | ID do medidor |
eventName | string | Não* | Nome do evento |
idempotencyKey | string | Sim | Chave única para deduplicação |
eventTimestamp | datetime | Não | Timestamp (default: agora) |
quantity | number | Sim | Quantidade do evento |
properties | object | Não | Metadados adicionais |
*Forneça meterId ou eventName (o sistema associa via eventName do medidor).
- cURL
- JavaScript
curl -X POST 'https://billing.stg.catalisa.app/billing/api/v1/usage' \
-H 'Authorization: Bearer SEU_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"data": {
"type": "usage-events",
"attributes": {
"billingAccountId": "550e8400-e29b-41d4-a716-446655440050",
"meterId": "550e8400-e29b-41d4-a716-446655440010",
"idempotencyKey": "req-2024-01-15-001",
"quantity": 100,
"properties": {
"endpoint": "/api/v1/users",
"method": "GET"
}
}
}
}'
const response = await fetch('https://billing.stg.catalisa.app/billing/api/v1/usage', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
data: {
type: 'usage-events',
attributes: {
billingAccountId: '550e8400-e29b-41d4-a716-446655440050',
meterId: '550e8400-e29b-41d4-a716-446655440010',
idempotencyKey: `req-${Date.now()}`,
quantity: 100,
properties: {
endpoint: '/api/v1/users',
method: 'GET',
},
},
},
}),
});
const { data, meta } = await response.json();
console.log(`Evento registrado: ${data.id}`);
console.log(`É duplicata: ${meta.isDuplicate}`);
Response (201 Created)
{
"data": {
"type": "usage-events",
"id": "550e8400-e29b-41d4-a716-446655440100",
"links": {
"self": "/billing/api/v1/usage/550e8400-e29b-41d4-a716-446655440100"
},
"attributes": {
"billingAccountId": "550e8400-e29b-41d4-a716-446655440050",
"meterId": "550e8400-e29b-41d4-a716-446655440010",
"idempotencyKey": "req-2024-01-15-001",
"eventTimestamp": "2024-01-15T10:30:00Z",
"quantity": 100,
"properties": {
"endpoint": "/api/v1/users",
"method": "GET"
}
}
},
"meta": {
"isDuplicate": false
}
}
Registrar Eventos em Lote
POST /billing/api/v1/usage/batch
Registra múltiplos eventos de uso em uma única requisição (até 1000 eventos).
- cURL
- JavaScript
curl -X POST 'https://billing.stg.catalisa.app/billing/api/v1/usage/batch' \
-H 'Authorization: Bearer SEU_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"data": [
{
"type": "usage-events",
"attributes": {
"billingAccountId": "550e8400-e29b-41d4-a716-446655440050",
"eventName": "api.request",
"idempotencyKey": "batch-001-event-1",
"quantity": 50
}
},
{
"type": "usage-events",
"attributes": {
"billingAccountId": "550e8400-e29b-41d4-a716-446655440050",
"eventName": "api.request",
"idempotencyKey": "batch-001-event-2",
"quantity": 75
}
}
]
}'
// Preparar eventos
const events = [
{ billingAccountId: 'uuid-1', eventName: 'api.request', quantity: 50 },
{ billingAccountId: 'uuid-1', eventName: 'api.request', quantity: 75 },
{ billingAccountId: 'uuid-2', eventName: 'api.request', quantity: 100 },
];
const data = events.map((e, i) => ({
type: 'usage-events',
attributes: {
...e,
idempotencyKey: `batch-${Date.now()}-${i}`,
},
}));
const response = await fetch('https://billing.stg.catalisa.app/billing/api/v1/usage/batch', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ data }),
});
const result = await response.json();
console.log(`Registrados: ${result.meta.recorded}`);
console.log(`Duplicatas: ${result.meta.duplicates}`);
console.log(`Erros: ${result.meta.errors}`);
Response (201 Created)
{
"data": [
"550e8400-e29b-41d4-a716-446655440101",
"550e8400-e29b-41d4-a716-446655440102"
],
"meta": {
"recorded": 2,
"duplicates": 0,
"errors": 0
}
}
Response com Erros Parciais
{
"data": [
"550e8400-e29b-41d4-a716-446655440101"
],
"meta": {
"recorded": 1,
"duplicates": 1,
"errors": 1
},
"errors": [
{
"index": 2,
"idempotencyKey": "batch-001-event-3",
"error": "Meter not found"
}
]
}
Obter Resumo de Uso
GET /billing/api/v1/usage/summary
Retorna o resumo de uso agregado por medidor em um período.
Query Parameters
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
billingAccountId | UUID | Sim | ID da conta de faturamento |
meterId | UUID | Não | Filtrar por medidor |
startDate | datetime | Sim | Data inicial do período |
endDate | datetime | Sim | Data final do período |
- cURL
- JavaScript
curl 'https://billing.stg.catalisa.app/billing/api/v1/usage/summary?billingAccountId=550e8400-e29b-41d4-a716-446655440050&startDate=2024-01-01T00:00:00Z&endDate=2024-01-31T23:59:59Z' \
-H 'Authorization: Bearer SEU_TOKEN'
const params = new URLSearchParams({
billingAccountId: '550e8400-e29b-41d4-a716-446655440050',
startDate: '2024-01-01T00:00:00Z',
endDate: '2024-01-31T23:59:59Z',
});
const response = await fetch(`https://billing.stg.catalisa.app/billing/api/v1/usage/summary?${params}`, {
headers: {
'Authorization': `Bearer ${token}`,
},
});
const { data } = await response.json();
console.log(`Período: ${data.attributes.periodStart} - ${data.attributes.periodEnd}`);
data.attributes.items.forEach(item => {
console.log(`${item.meterName}: ${item.aggregatedValue} (${item.eventCount} eventos)`);
});
Response (200 OK)
{
"data": {
"type": "usage-summary",
"attributes": {
"billingAccountId": "550e8400-e29b-41d4-a716-446655440050",
"periodStart": "2024-01-01T00:00:00Z",
"periodEnd": "2024-01-31T23:59:59Z",
"items": [
{
"meterId": "550e8400-e29b-41d4-a716-446655440010",
"meterName": "API Calls",
"aggregationType": "SUM",
"aggregatedValue": 15000,
"eventCount": 150
},
{
"meterId": "550e8400-e29b-41d4-a716-446655440011",
"meterName": "Storage Used",
"aggregationType": "LAST",
"aggregatedValue": 5368709120,
"eventCount": 30
}
]
}
}
}
Boas Práticas
Idempotência
Sempre use uma idempotencyKey única e determinística:
// Bom: baseado em dados do evento
const idempotencyKey = `${userId}-${eventType}-${timestamp}`;
// Ruim: aleatório (não permite retry seguro)
const idempotencyKey = Math.random().toString();
Batch vs Individual
| Cenário | Recomendação |
|---|---|
| Tempo real, poucos eventos | Individual |
| Processamento batch, muitos eventos | Batch |
| Importação histórica | Batch (max 1000/request) |
Timestamp
Sempre inclua eventTimestamp quando o evento ocorreu no passado:
// Evento em tempo real (pode omitir timestamp)
{ quantity: 1 }
// Evento histórico (sempre incluir timestamp)
{
quantity: 1,
eventTimestamp: '2024-01-14T10:30:00Z'
}
Erros Comuns
| Código | Erro | Descrição |
|---|---|---|
| 400 | VALIDATION | Dados inválidos ou período inválido |
| 404 | NOT_FOUND | Conta ou medidor não encontrado |
| 409 | DUPLICATE | Evento com mesma idempotencyKey já existe |