---
title: "Submit an edge installation's signed monthly usage report"
method: POST
path: "/v1/edge/usage-reports"
---

# Submit an edge installation's signed monthly usage report

`POST /v1/edge/usage-reports`

## Request

- Base URL: `https://api.decionis.com`
- URL: `https://api.decionis.com/v1/edge/usage-reports`
- Auth: HTTP bearer

An edge installation counts the governed actions it decided per UTC
month from its hash-chained evidence and reports them as a compact
JWS (`EdgeUsageReportHeader`, `EdgeUsageReportClaims`) signed with an
Ed25519 key the organization registered at
`/v1/execution/provider-keys` with purpose `usage_report` and the
installation id as its `issuer`.

Verified like an effect receipt: the header, the key (this
organization's, this purpose only), the signature, `iss` = the key's
issuer, `aud` = the organization, `iat` at most 5 minutes ahead, a
month that has begun, and claims that add up (each breakdown sums to
`total`, `delegated` <= `total`, the chain range present exactly when
something was counted, `period_start`/`period_end` the month's
bounds). A refused report is answered 422 with its code and not
stored.

One report per installation and month is current. The same report
again is answered 200 with the stored record (`replay: true`). A
different report for the same installation and month is accepted
only when it counts further along the same chain stream (a higher
`chain.last_seq`): it supersedes the current one, which stays on
record pointing at it; otherwise 409.

A stored report adds `total - delegated` governed actions (minus what
earlier reports for that installation and month already metered) to
the organization's usage through the governed-action meter hosted
decisions use, dated in the reported month when the meter still
accepts it, so plans, allowances and overage apply unchanged.
`delegated` decisions were decided by hosted Decionis, which metered
them already. Metering is fail-open: the report is recorded either
way, with `meter_status`.

Requires an organization API key with `policy:read`, `policy:write`,
`policy:*` or `org:*`: the report's signature, not the API key, is
what is trusted.

## Request body

- EdgeUsageReportSubmission
  - `report` string, required: The usage report as a compact JWS.

## Response `200`

The same report was already recorded (`replay` true)

- EdgeUsageReportAccepted
  - `replay` boolean, required
  - `report` EdgeUsageReport, required
    - `id` string, uuid, required
    - `installation_id` string, required
    - `kid` string, required
    - `jti` string, required
    - `period` string, required
    - `period_start` string, date-time, required
    - `period_end` string, date-time, required
    - `issued_at` string, date-time, required
    - `counts` EdgeUsageCounts, required
      - `by_mode` object, required
        - `ENFORCEMENT` integer, required
        - `SHADOW` integer, required
        - `PARALLEL` integer
      - `by_verdict` object, required
        - `ALLOW` integer, required
        - `ESCALATE` integer, required
        - `BLOCK` integer, required
      - `total` integer, required: Every edge decision in the month; `by_mode` and `by_verdict` each sum to it.
    - `delegated` integer, required
    - `billable` integer, required: `total - delegated`.
    - `chain` EdgeUsageChain, required: The evidence-chain range the counts were taken from; the four bounds are null exactly when nothing was counted.
      - `stream` string, required
      - `first_seq` integer, nullable, required
      - `last_seq` integer, nullable, required
      - `first_hash` string, nullable, required
      - `last_hash` string, nullable, required
    - `bundles` string[], required
    - `report_digest` string, required
    - `supersedes` string, uuid, nullable, required
    - `superseded_by` string, uuid, nullable, required
    - `metered_actions` integer, required: What this report added to the meter (beyond earlier reports for its month).
    - `meter_status` 'pending' | 'recorded' | 'not_recorded' | 'not_metered', required: `not_metered`: the plan is not metered (the hard-capped exploration default); `not_recorded`: the meter did not take it (no billing customer, or the meter was unavailable).
    - `received_at` string, date-time, required

## Other responses

- `201`: Recorded (and metered, see `meter_status`)
- `400`: Structured API error
- `401`: Structured API error
- `403`: Structured API error
- `409`: The report does not supersede the current one for its installation and month
- `422`: The report was refused; nothing was recorded
- `503`: `USAGE_REPORT_KEYS_UNAVAILABLE` or `USAGE_REPORT_UNAVAILABLE`: nothing was recorded; send it again

---

[API](https://skmtc.dev/decionis/apis/decionis-policy-evaluation-api.md) · [All operations](https://skmtc.dev/decionis/apis/decionis-policy-evaluation-api/llms.txt) · [OpenAPI document](https://skmtc.dev/decionis/apis/decionis-policy-evaluation-api/revisions/357f5e459d0c?raw)
