Skip to main content

Meters

9 operations under /v1/meters (GET POST): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.

10 min read
View MarkdownEdit on GitHub

Base URL: https://api.codespar.dev

Every operation below requires a Bearer token. See Authentication.

GET /v1/meters

GEThttps://api.codespar.dev/v1/meters

List 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

NameTypeRequiredDescription
periodstringno—

Responses

StatusBodyDescription
200objectOK
400objectBad Request.

Response 200

FieldTypeRequiredDescription
dataarray of objectyes—
object"list"yes—
periodstringyes—
Example request
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_KEY
import 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");
Example response 200
application/json
{
  "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

POSThttps://api.codespar.dev/v1/meters

Define 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

FieldTypeRequiredDescription
aggregation"count" | "sum" | "max" | "last"yes—
channel"invoice" | "collect" | "gate"yes—
descriptionstringno—
event_namestringyes—
per_units1 | 1000no—
pricenumber | stringyes—
unit_labelstringno—

Responses

StatusBodyDescription
201objectOK
400objectBad Request.
409objectThe project already has a meter for that event name.

Response 201

FieldTypeRequiredDescription
aggregation"count" | "sum" | "max" | "last"yes—
channel"invoice" | "collect" | "gate"yesThe 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_atstringyes—
currency"BRL"yes—
cycleobjectyes—
descriptionstring,nullyes—
environment"live" | "test"yesThe project's environment.
event_namestringyesThe event each usage event names. Unique in the project.
idstringyesmtr_ + 128 random bits, minted by the server.
object"meter"yes—
per_units1 | 1000yes—
pricestringyesBRL per per_units units, up to six decimals.
status"active"yes—
unit_labelstring,nullyes—
updated_atstringyes—
Example request
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"
  }
});
Example response 201
application/json
{
  "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

GEThttps://api.codespar.dev/v1/meters/adjustments

Late 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

NameTypeRequiredDescription
limitintegerno—
periodstringno—

Responses

StatusBodyDescription
200objectOK
400objectBad Request.

Response 200

FieldTypeRequiredDescription
dataarray of objectyes—
object"list"yes—
Example request
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_KEY
import 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");
Example response 200
application/json
{
  "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

GEThttps://api.codespar.dev/v1/meters/cycle

The 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

NameTypeRequiredDescription
periodstringno—

Responses

StatusBodyDescription
200objectOK
400objectBad Request.

Response 200

FieldTypeRequiredDescription
active_metersintegeryes—
by_channelobjectyes—
currency"BRL"yes—
day_of_cycleintegeryes—
days_in_cycleintegeryes—
days_remainingintegeryes—
endstringyesFirst instant after the cycle.
object"meter_cycle"yes—
periodstringyes—
startstringyes—
state"open" | "closed"yes—
totalsobjectyes—
Example request
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_KEY
import 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");
Example response 200
application/json
{
  "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

GEThttps://api.codespar.dev/v1/meters/statements

A 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

NameTypeRequiredDescription
periodstringno—

Responses

StatusBodyDescription
200objectOK
400objectBad Request.

Response 200

FieldTypeRequiredDescription
closureobject | objectyes—
dataarray of objectyes—
object"list"yes—
periodstringyes—
Example request
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_KEY
import 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");
Example response 200
application/json
{
  "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}

GEThttps://api.codespar.dev/v1/meters/statements/{statementId}

Read one statement with its lines

Path parameters

NameTypeRequiredDescription
statementIdstringyes—

Responses

StatusBodyDescription
200objectOK
400objectBad Request.
404objectNo statement with that id in this project.

Response 200

FieldTypeRequiredDescription
carried_to_statement_idstring,nullyesFor a below-minimum statement: the later statement that carries it.
channel"invoice" | "collect" | "gate"yes—
chargeobjectyes—
created_atstringyes—
currency"BRL"yes—
customerstringyes—
environment"live" | "test"yes—
exact_totalstringyesThe unrounded total, BRL, exact to the micro-real.
idstringyesbst_ + 128 random bits.
invoiceobject,nullyesThe 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.
linesarray of objectyes—
minimum_minorinteger,nullyesThe channel's minimum in centavos when its contract states one; null otherwise.
object"bill_statement"yes—
periodstringyesThe closed cycle, YYYY-MM.
rounding_microsintegeryesWhat the per-line rounding added (+) or removed (-), in micro-reais.
state"eligible" | "below_minimum" | "eligibility_undetermined" | "nothing_to_charge"yesThe 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_documentbooleanyesTrue for a Test project's statement: it, its invoice and its charge are test documents, with no real charge.
total_minorintegeryesSum of the lines' centavos; each line rounded half up once.
updated_atstringyes—
Example request
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_KEY
import 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"
  }
});
Example response 200
application/json
{
  "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}

GEThttps://api.codespar.dev/v1/meters/{meterId}

Read one meter with its cycle figures

Path parameters

NameTypeRequiredDescription
meterIdstringyes—

Query parameters

NameTypeRequiredDescription
periodstringno—

Responses

StatusBodyDescription
200objectOK
400objectBad Request.
404objectNo meter with that id in this project.

Response 200

FieldTypeRequiredDescription
aggregation"count" | "sum" | "max" | "last"yes—
channel"invoice" | "collect" | "gate"yesThe 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_atstringyes—
currency"BRL"yes—
cycleobjectyes—
descriptionstring,nullyes—
environment"live" | "test"yesThe project's environment.
event_namestringyesThe event each usage event names. Unique in the project.
idstringyesmtr_ + 128 random bits, minted by the server.
object"meter"yes—
per_units1 | 1000yes—
pricestringyesBRL per per_units units, up to six decimals.
status"active"yes—
unit_labelstring,nullyes—
updated_atstringyes—
Example request
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_KEY
import 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"
  }
});
Example response 200
application/json
{
  "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

GEThttps://api.codespar.dev/v1/meters/{meterId}/events

A meter's latest events

Newest first by arrival, at most limit (1 to 50, default 20).

Path parameters

NameTypeRequiredDescription
meterIdstringyes—

Query parameters

NameTypeRequiredDescription
limitintegerno—

Responses

StatusBodyDescription
200objectOK
400objectBad Request.
404objectNo meter with that id in this project.

Response 200

FieldTypeRequiredDescription
dataarray of objectyes—
object"list"yes—
Example request
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_KEY
import 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"
  }
});
Example response 200
application/json
{
  "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

GEThttps://api.codespar.dev/v1/meters/{meterId}/usage

A 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

NameTypeRequiredDescription
meterIdstringyes—

Query parameters

NameTypeRequiredDescription
periodstringno—

Responses

StatusBodyDescription
200objectOK
400objectBad Request.
404objectNo meter with that id in this project.

Response 200

FieldTypeRequiredDescription
aggregation"count" | "sum" | "max" | "last"yes—
daysarray of objectyes—
meter_idstringyes—
object"meter_usage"yes—
periodstringyes—
state"open" | "closed"yes—
Example request
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_KEY
import 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"
  }
});
Example response 200
application/json
{
  "object": "meter_usage",
  "meter_id": "meter_0000000000000000",
  "aggregation": "count",
  "period": "string",
  "state": "open",
  "days": [
    {
      "day": "string",
      "usage": "string",
      "amount": "1000"
    }
  ]
}
Meters | CodeSpar