Report Usage via API
Overview
You report usage to Suger, against a Suger entitlement; Suger aggregates pending usage and submits it to OCI on an hourly cadence. The endpoints are the same generic Metering API used for every marketplace — this page documents the Oracle-specific field semantics.
The two rules that make Oracle different from AWS/Azure/GCP:
- The value you report is a monetary amount, not a quantity. OCI has no dimension catalog and no
per-dimension rate card, so Suger applies no rate —
"records": {"usage": 10}bills 10 units of the entitlement’s currency. - Usage keys are open. Any key name is accepted and auto-registered on the entitlement the first time it is seen — no dimension configuration in advance. An entitlement may accumulate at most 100 distinct keys.
Endpoints
| Purpose | Endpoint | Operation |
|---|---|---|
Report one group (deduplicated by id) | POST /org/{orgId}/entitlement/{entitlementId}/usageRecordGroup | ReportUsageRecordGroup |
| Report a batch (no dedup key) | POST /org/{orgId}/batchCreateUsageRecordGroups | BatchReportUsageRecordGroups |
| List groups (check status / reported time) | GET /org/{orgId}/usageRecordGroup | ListUsageRecordGroups |
Auth: Authorization: Bearer <access_token> — see OAuth App.
Prefer the single-group endpoint with your own id for anything automated: the id is the idempotency
key, so a retried request cannot double-report. The batch endpoint carries no dedup key.
Supporting operations (aggregate, validate, retry, delete) are documented in the generic Metering API reference.
Request fields — CreateUsageRecordGroupParams
| Field | Required | Type | Notes |
|---|---|---|---|
id | ⬜ | string (≤36 chars) | Your idempotency key, typically a UUID. Reporting the same id again within 15 days is rejected with a 400 instead of double-billing. Omitted → Suger generates one (and you lose dedup protection). |
entitlementID | ✅ | string | The Suger entitlement to bill. Must belong to the org, partner ORACLE. In the single-group endpoint this comes from the URL path instead. |
records | ✅ | map<string, number> | The data. Key → monetary amount (see dictionary below). |
timestamp | ⬜ | RFC-3339 date-time | When the usage was generated (not when it is reported). Defaults to the report time. |
billableRecords | ❌ | MeteringUsageRecord[] | Not supported for Oracle. This is the Metering API v2 field; Oracle reports v1 records only. A group reported with billableRecords instead of records is accepted at ingestion but skipped by the hourly OCI submission — the usage is never billed. Always use records. |
metaInfo | — | object | Read-only. Populated by Suger (origin records, source, invoice linkages). Sending it has no effect. |
usageAllocations | — | map | AWS-only feature; ignored for Oracle. |
records — the field data dictionary
records is a flat map of usage key → amount:
"records": {
"usage": 12.5,
"api-calls": 3.99
}
| Aspect | Rule |
|---|---|
| Key (map key) | Any name you choose — Oracle usage keys are open. The key is auto-registered on the entitlement on first use and becomes the OCI usageDimensionName. Use stable, human-readable names (usage, api-calls, gb-processed). |
| Key cap | At most 100 distinct keys per entitlement, matching OCI’s per-submission record cap. The 101st new key is rejected at ingestion with a usage dimensions … exceeding the 100 cap error. |
| Value (map value) | The monetary amount to bill, in the entitlement’s currency — not a consumption quantity. Decimals are supported; the submitted amount is rounded to 5 decimal places (OCI’s per-record limit), and an amount that rounds to zero is skipped. |
| Zero values | Skipped — OCI rejects zero-amount records. A group whose values are all zero is rejected at ingestion (400: all the quantity of usage records are zero). |
| Negative values | Rejected. Usage can only add charges; corrections go through Oracle support. |
| Commit interaction | None. Amounts are forwarded as-is; no commit/credit/included-usage deduction is applied. |
Response fields — MeteringUsageRecordGroup
| Field | Type | Notes |
|---|---|---|
id | string | The group’s ID (yours, or generated). Used for retry/delete and as the dedup key. |
organizationID / entitlementID / buyerID | string | Ownership. buyerID is resolved from the entitlement. |
partner | string | ORACLE. |
records | map | The records as stored (after any dimension mapping; for Oracle open keys this equals what you sent). |
status | string | Lifecycle state — see below. |
creationTime / lastUpdateTime | date-time | Bookkeeping. |
usageRecordReportID | string | Set once the hourly job folds the group into an OCI submission — joins to GET /org/{orgId}/usageRecordReport. |
reportedTime | date-time | null | When the group was reported to OCI. null until then. |
metaInfo.originRecords | map | The records exactly as you reported them, before any conversion. |
metaInfo.timestamp | date-time | The usage-generation timestamp you supplied. |
metaInfo.source | string | API for groups created this way. |
Status lifecycle
| Status | Meaning |
|---|---|
CREATED | Accepted and queued; the hourly job will pick it up. |
REPORTED | Folded into an OCI submission; usageRecordReportID and reportedTime are set. |
REPORT_FAILED | The OCI submission failed. Inspect, then retry (moves it back to CREATED). |
INVALID | Rejected by validation after creation. |
DELETED | Hard-deleted via the delete endpoint. |
What Suger submits to OCI
Each key/amount pair becomes one record in OCI’s submitUsageRecords call (at most 100 records per call,
all-or-nothing):
| OCI field | Sourced from |
|---|---|
id | Derived deterministically from the group ID + key, so an automatic retry is idempotent. |
marketplaceOfferId | The entitlement’s Oracle private-offer OCID. |
usageDimensionName | The records map key (the literal usage when no specific dimension applies). |
amount | The records map value, at full precision. |
currencyCode | The entitlement’s currency. |
timeUsageStarted / timeUsageEnded | The aggregation window of the hourly report run. |
Example
Report one group, idempotent by id:
curl -L -X POST 'https://api.suger.cloud/org/YOUR_ORG_ID/entitlement/YOUR_ENTITLEMENT_ID/usageRecordGroup' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
--data-raw '{
"id": "2f9c1e58-9a1b-4c7e-b1d2-8f3a5c6d7e90",
"records": {
"usage": 12.5,
"api-calls": 3.99
},
"timestamp": "2026-08-05T00:00:00Z"
}'
Response (trimmed):
{
"id": "2f9c1e58-9a1b-4c7e-b1d2-8f3a5c6d7e90",
"organizationID": "YOUR_ORG_ID",
"partner": "ORACLE",
"entitlementID": "YOUR_ENTITLEMENT_ID",
"buyerID": "gHYis9RKU",
"records": { "usage": 12.5, "api-calls": 3.99 },
"status": "CREATED",
"metaInfo": {
"originRecords": { "usage": 12.5, "api-calls": 3.99 },
"timestamp": "2026-08-05T00:00:00Z",
"source": "API"
}
}
Report a batch — the body is an array; each entry needs entitlementID + records (the response is
the array of created groups, one per entry, with generated ids):
curl -L -X POST 'https://api.suger.cloud/org/YOUR_ORG_ID/batchCreateUsageRecordGroups' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
--data-raw '[
{
"entitlementID": "YOUR_ENTITLEMENT_ID",
"records": { "usage": 0.75 },
"timestamp": "2026-08-05T00:00:00Z"
},
{
"entitlementID": "ANOTHER_ENTITLEMENT_ID",
"records": { "api-calls": 4.2 }
}
]'
Since batch entries carry no caller-supplied id, a retried batch request re-creates the groups —
use the single-group endpoint when you need idempotency.
Errors
| Response | Cause / fix |
|---|---|
400: the record id … has already been reported in the last 15 days | A group with the same id was reported within the 15-day dedup window — this is the dedup working. Do not resend with a new id unless the usage is genuinely new. |
400: all the quantity of usage records are zero | Every value in records is 0. Zero-amount records are never billable; skip the report instead. |
400: … usage dimensions, exceeding the 100 cap … | The report would push the entitlement past 100 distinct usage keys. Reuse existing keys, or prune the entitlement’s registered dimensions. |
400 naming the entitlement | The entitlement doesn’t exist, isn’t in this org, or isn’t in a reportable state. |