Meters
9 operations under /v1/meters (GET POST): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Base URL: https://api.codespar.dev
Every operation below requires a Bearer token. See Authentication.
GET /v1/meters
https://api.codespar.dev/v1/metersList the project's meters with their cycle figures
Oldest first. period (YYYY-MM) reads a past cycle; the default is the current Sao Paulo month.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
period | string | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | Bad Request. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
data | array of object | yes | — |
object | "list" | yes | — |
period | string | yes | — |
curl -X GET https://api.codespar.dev/v1/meters \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/meters HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/meters",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/meters", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/meters");{
"object": "list",
"period": "string",
"data": [
{
"id": "obj_0000000000000000",
"object": "meter",
"environment": "live",
"event_name": "Example",
"aggregation": "count",
"price": "string",
"currency": "BRL",
"per_units": 1,
"unit_label": "Example",
"description": "string",
"channel": "invoice",
"status": "active",
"created_at": "string",
"updated_at": "string",
"cycle": {
"period": "string",
"usage": "string",
"customers": 0,
"amount_to_date": "2026-01-15",
"amount_to_date_minor": 1000,
"amount_projected": "1000",
"amount_projected_minor": 1000,
"projected_usage": "string",
"last_event_at": "string"
},
"spark": [
{
"day": "string",
"usage": "string"
}
]
}
]
}POST /v1/meters
https://api.codespar.dev/v1/metersDefine a meter on a usage event
Creates a meter in the caller's project. event_name is what the tenant's code sends as event on each usage event, unique in the project. aggregation says how a cycle is summed per customer: count (each event is 1), sum (of value), max (the largest value) or last (the latest value by event time). price is BRL per per_units (1 or 1000) units.
channel declares how the usage will be charged. It is stored and shown; nothing is issued from it, and no route here moves money.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
aggregation | "count" | "sum" | "max" | "last" | yes | — |
channel | "invoice" | "collect" | "gate" | yes | — |
description | string | no | — |
event_name | string | yes | — |
per_units | 1 | 1000 | no | — |
price | number | string | yes | — |
unit_label | string | no | — |
Responses
| Status | Body | Description |
|---|---|---|
201 | object | OK |
400 | object | Bad Request. |
409 | object | The project already has a meter for that event name. |
Response 201
| Field | Type | Required | Description |
|---|---|---|---|
aggregation | "count" | "sum" | "max" | "last" | yes | — |
channel | "invoice" | "collect" | "gate" | yes | The charge channel the tenant declared: monthly invoice, Collect link or 402 on the Gate. A declaration only: no invoice, link or paywall is created from it. |
created_at | string | yes | — |
currency | "BRL" | yes | — |
cycle | object | yes | — |
description | string,null | yes | — |
environment | "live" | "test" | yes | The project's environment. |
event_name | string | yes | The event each usage event names. Unique in the project. |
id | string | yes | mtr_ + 128 random bits, minted by the server. |
object | "meter" | yes | — |
per_units | 1 | 1000 | yes | — |
price | string | yes | BRL per per_units units, up to six decimals. |
status | "active" | yes | — |
unit_label | string,null | yes | — |
updated_at | string | yes | — |
curl -X POST https://api.codespar.dev/v1/meters \
-H "Authorization: Bearer $CODESPAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"event_name": "Example",
"aggregation": "count",
"price": 0,
"per_units": 1,
"unit_label": "Example",
"description": "string",
"channel": "invoice"
}'POST /v1/meters HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEY
Content-Type: application/json
{
"event_name": "Example",
"aggregation": "count",
"price": 0,
"per_units": 1,
"unit_label": "Example",
"description": "string",
"channel": "invoice"
}import os
import requests
res = requests.post(
"https://api.codespar.dev/v1/meters",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
json={
"event_name": "Example",
"aggregation": "count",
"price": 0,
"per_units": 1,
"unit_label": "Example",
"description": "string",
"channel": "invoice"
},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/meters", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"event_name": "Example",
"aggregation": "count",
"price": 0,
"per_units": 1,
"unit_label": "Example",
"description": "string",
"channel": "invoice"
}),
});
const data = await res.json();const result = await cs.api.post("/v1/meters", {
body: {
event_name: "Example",
aggregation: "count",
price: 0,
per_units: 1,
unit_label: "Example",
description: "string",
channel: "invoice"
}
});{
"id": "obj_0000000000000000",
"object": "meter",
"environment": "live",
"event_name": "Example",
"aggregation": "count",
"price": "string",
"currency": "BRL",
"per_units": 1,
"unit_label": "Example",
"description": "string",
"channel": "invoice",
"status": "active",
"created_at": "string",
"updated_at": "string",
"cycle": {
"period": "string",
"usage": "string",
"customers": 0,
"amount_to_date": "2026-01-15",
"amount_to_date_minor": 1000,
"amount_projected": "1000",
"amount_projected_minor": 1000,
"projected_usage": "string",
"last_event_at": "string"
}
}GET /v1/meters/adjustments
https://api.codespar.dev/v1/meters/adjustmentsLate max/last events pending adjustment
Events of a max or last meter that arrived after their cycle's closure. They are recorded, kept out of the closed cycle, never charged automatically and never discarded. period filters by the closed cycle of origin. Newest first by arrival, at most limit (1 to 50, default 20).
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | no | — |
period | string | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | Bad Request. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
data | array of object | yes | — |
object | "list" | yes | — |
curl -X GET https://api.codespar.dev/v1/meters/adjustments \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/meters/adjustments HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/meters/adjustments",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/meters/adjustments", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/meters/adjustments");{
"object": "list",
"data": [
{
"object": "meter_pending_adjustment",
"event_id": "event_0000000000000000",
"meter_id": "meter_0000000000000000",
"event_name": "Example",
"aggregation": "count",
"customer": "string",
"value": "string",
"late_of_period": "string",
"occurred_at": "string",
"received_at": "string"
}
]
}GET /v1/meters/cycle
https://api.codespar.dev/v1/meters/cycleThe meter cycle: progress, totals and totals by channel
The cycle is the Sao Paulo calendar month. day_of_cycle is today's day for the open cycle and days_remaining the days after today until it closes. Totals add each meter's exact amount and round once. Amounts are forecasts of usage times price; nothing has been charged.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
period | string | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | Bad Request. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
active_meters | integer | yes | — |
by_channel | object | yes | — |
currency | "BRL" | yes | — |
day_of_cycle | integer | yes | — |
days_in_cycle | integer | yes | — |
days_remaining | integer | yes | — |
end | string | yes | First instant after the cycle. |
object | "meter_cycle" | yes | — |
period | string | yes | — |
start | string | yes | — |
state | "open" | "closed" | yes | — |
totals | object | yes | — |
curl -X GET https://api.codespar.dev/v1/meters/cycle \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/meters/cycle HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/meters/cycle",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/meters/cycle", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/meters/cycle");{
"object": "meter_cycle",
"period": "string",
"start": "string",
"end": "string",
"state": "open",
"days_in_cycle": 0,
"day_of_cycle": 0,
"days_remaining": 0,
"currency": "BRL",
"active_meters": 0,
"totals": {
"meters": 0,
"amount_to_date": "2026-01-15",
"amount_to_date_minor": 1000,
"amount_projected": "1000",
"amount_projected_minor": 1000
},
"by_channel": {
"invoice": {
"meters": 0,
"amount_to_date": "2026-01-15",
"amount_to_date_minor": 1000,
"amount_projected": "1000",
"amount_projected_minor": 1000
},
"collect": {
"meters": 0,
"amount_to_date": "2026-01-15",
"amount_to_date_minor": 1000,
"amount_projected": "1000",
"amount_projected_minor": 1000
},
"gate": {
"meters": 0,
"amount_to_date": "2026-01-15",
"amount_to_date_minor": 1000,
"amount_projected": "1000",
"amount_projected_minor": 1000
}
}
}GET /v1/meters/statements
https://api.codespar.dev/v1/meters/statementsA closed cycle's statements
A cycle closes on D+2 at 06:00 Sao Paulo, D being its last day. The closure freezes one statement per customer, channel and currency; it never publishes or sends a charge. period defaults to the previous cycle. Before the closure, closure.state is open with closes_at, and data is empty.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
period | string | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | Bad Request. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
closure | object | object | yes | — |
data | array of object | yes | — |
object | "list" | yes | — |
period | string | yes | — |
curl -X GET https://api.codespar.dev/v1/meters/statements \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/meters/statements HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/meters/statements",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/meters/statements", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/meters/statements");{
"object": "list",
"period": "string",
"closure": {
"state": "closed",
"closed_at": "string",
"rule_version": "string"
},
"data": [
{
"id": "obj_0000000000000000",
"object": "bill_statement",
"environment": "live",
"period": "string",
"customer": "string",
"channel": "invoice",
"currency": "BRL",
"state": "eligible",
"total_minor": 1,
"exact_total": "string",
"rounding_micros": 0,
"minimum_minor": 1,
"carried_to_statement_id": "carriedtostatement_0000000000000000",
"test_document": true,
"invoice": {
"number": "string",
"due_on": "string"
},
"charge": {
"status": "not_requested",
"reference": "string",
"version": 0,
"external_id": "external_0000000000000000",
"refusal_code": "string",
"display": {
"payment": "paid",
"payment_evidence": "string",
"payment_confirmed_at": "string",
"url": "https://example.com/hook",
"channel_state": "string",
"amount_divergence": {
"bill_minor": 1,
"channel_minor": 1
}
}
},
"created_at": "string",
"updated_at": "string"
}
]
}GET /v1/meters/statements/{statementId}
https://api.codespar.dev/v1/meters/statements/{statementId}Read one statement with its lines
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
statementId | string | yes | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | Bad Request. |
404 | object | No statement with that id in this project. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
carried_to_statement_id | string,null | yes | For a below-minimum statement: the later statement that carries it. |
channel | "invoice" | "collect" | "gate" | yes | — |
charge | object | yes | — |
created_at | string | yes | — |
currency | "BRL" | yes | — |
customer | string | yes | — |
environment | "live" | "test" | yes | — |
exact_total | string | yes | The unrounded total, BRL, exact to the micro-real. |
id | string | yes | bst_ + 128 random bits. |
invoice | object,null | yes | The monthly invoice the PROJECT issues to its customer (the project is the issuer and creditor). Only on the invoice channel, once the charge is requested. |
lines | array of object | yes | — |
minimum_minor | integer,null | yes | The channel's minimum in centavos when its contract states one; null otherwise. |
object | "bill_statement" | yes | — |
period | string | yes | The closed cycle, YYYY-MM. |
rounding_micros | integer | yes | What the per-line rounding added (+) or removed (-), in micro-reais. |
state | "eligible" | "below_minimum" | "eligibility_undetermined" | "nothing_to_charge" | yes | The amount is frozen in every state; state is its eligibility for issuance. eligible: at or above the minimum the channel's contract states; whether and how it is charged belongs to the channel. below_minimum: under that minimum; carried to the next closure as a balance to charge. eligibility_undetermined: the channel states no minimum yet; an unknown minimum is not a zero minimum, so the statement is not treated as chargeable until the contract states one. nothing_to_charge: zero. |
test_document | boolean | yes | True for a Test project's statement: it, its invoice and its charge are test documents, with no real charge. |
total_minor | integer | yes | Sum of the lines' centavos; each line rounded half up once. |
updated_at | string | yes | — |
curl -X GET https://api.codespar.dev/v1/meters/statements/{statementId} \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/meters/statements/{statementId} HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/meters/statements/{statementId}",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/meters/statements/{statementId}", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/meters/statements/{statementId}", {
path: {
statementId: "statement_0000000000000000"
}
});{
"id": "obj_0000000000000000",
"object": "bill_statement",
"environment": "live",
"period": "string",
"customer": "string",
"channel": "invoice",
"currency": "BRL",
"state": "eligible",
"total_minor": 1,
"exact_total": "string",
"rounding_micros": 0,
"minimum_minor": 1,
"carried_to_statement_id": "carriedtostatement_0000000000000000",
"test_document": true,
"invoice": {
"number": "string",
"due_on": "string"
},
"charge": {
"status": "not_requested",
"reference": "string",
"version": 0,
"external_id": "external_0000000000000000",
"refusal_code": "string",
"display": {
"payment": "paid",
"payment_evidence": "string",
"payment_confirmed_at": "string",
"url": "https://example.com/hook",
"channel_state": "string",
"amount_divergence": {
"bill_minor": 1,
"channel_minor": 1
}
}
},
"created_at": "string",
"updated_at": "string",
"lines": [
{
"line_no": 0,
"kind": "usage",
"meter_id": "meter_0000000000000000",
"event_name": "Example",
"origin_period": "string",
"origin_statement_id": "originstatement_0000000000000000",
"usage": "string",
"amount": "1000",
"amount_minor": 1000
}
]
}GET /v1/meters/{meterId}
https://api.codespar.dev/v1/meters/{meterId}Read one meter with its cycle figures
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
meterId | string | yes | — |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
period | string | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | Bad Request. |
404 | object | No meter with that id in this project. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
aggregation | "count" | "sum" | "max" | "last" | yes | — |
channel | "invoice" | "collect" | "gate" | yes | The charge channel the tenant declared: monthly invoice, Collect link or 402 on the Gate. A declaration only: no invoice, link or paywall is created from it. |
created_at | string | yes | — |
currency | "BRL" | yes | — |
cycle | object | yes | — |
description | string,null | yes | — |
environment | "live" | "test" | yes | The project's environment. |
event_name | string | yes | The event each usage event names. Unique in the project. |
id | string | yes | mtr_ + 128 random bits, minted by the server. |
object | "meter" | yes | — |
per_units | 1 | 1000 | yes | — |
price | string | yes | BRL per per_units units, up to six decimals. |
status | "active" | yes | — |
unit_label | string,null | yes | — |
updated_at | string | yes | — |
curl -X GET https://api.codespar.dev/v1/meters/{meterId} \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/meters/{meterId} HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/meters/{meterId}",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/meters/{meterId}", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/meters/{meterId}", {
path: {
meterId: "meter_0000000000000000"
}
});{
"id": "obj_0000000000000000",
"object": "meter",
"environment": "live",
"event_name": "Example",
"aggregation": "count",
"price": "string",
"currency": "BRL",
"per_units": 1,
"unit_label": "Example",
"description": "string",
"channel": "invoice",
"status": "active",
"created_at": "string",
"updated_at": "string",
"cycle": {
"period": "string",
"usage": "string",
"customers": 0,
"amount_to_date": "2026-01-15",
"amount_to_date_minor": 1000,
"amount_projected": "1000",
"amount_projected_minor": 1000,
"projected_usage": "string",
"last_event_at": "string"
}
}GET /v1/meters/{meterId}/events
https://api.codespar.dev/v1/meters/{meterId}/eventsA meter's latest events
Newest first by arrival, at most limit (1 to 50, default 20).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
meterId | string | yes | — |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | Bad Request. |
404 | object | No meter with that id in this project. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
data | array of object | yes | — |
object | "list" | yes | — |
curl -X GET https://api.codespar.dev/v1/meters/{meterId}/events \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/meters/{meterId}/events HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/meters/{meterId}/events",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/meters/{meterId}/events", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/meters/{meterId}/events", {
path: {
meterId: "meter_0000000000000000"
}
});{
"object": "list",
"data": [
{
"object": "meter_event",
"event_id": "event_0000000000000000",
"meter_id": "meter_0000000000000000",
"customer": "string",
"customer_name": "Example",
"value": "string",
"occurred_at": "string",
"received_at": "string",
"late_of_period": "string",
"adjustment": "none"
}
]
}GET /v1/meters/{meterId}/usage
https://api.codespar.dev/v1/meters/{meterId}/usageA meter's usage on each day of the cycle
One point per Sao Paulo day of the cycle, in order. A day after today in the open cycle has usage and amount null; a past day without events has 0. Per day, usage is summed per customer by the meter's aggregation and then across customers.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
meterId | string | yes | — |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
period | string | no | — |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
400 | object | Bad Request. |
404 | object | No meter with that id in this project. |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
aggregation | "count" | "sum" | "max" | "last" | yes | — |
days | array of object | yes | — |
meter_id | string | yes | — |
object | "meter_usage" | yes | — |
period | string | yes | — |
state | "open" | "closed" | yes | — |
curl -X GET https://api.codespar.dev/v1/meters/{meterId}/usage \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /v1/meters/{meterId}/usage HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/v1/meters/{meterId}/usage",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/v1/meters/{meterId}/usage", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/v1/meters/{meterId}/usage", {
path: {
meterId: "meter_0000000000000000"
}
});{
"object": "meter_usage",
"meter_id": "meter_0000000000000000",
"aggregation": "count",
"period": "string",
"state": "open",
"days": [
{
"day": "string",
"usage": "string",
"amount": "1000"
}
]
}Consumer funding
4 operations under /v1/consumers/{consumerId}/fund (POST GET): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Meter Events
1 operation under /v1/meter-events (POST): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.