Partial Usage Logging¶
Use partial usage logging when runtime traffic arrives before Spendware has a matching project, customer, or provider catalog record. Spendware accepts the usage event, stores unresolved partial resources, and lets an operator finalize those resources later so eligible usage can materialize into ledger-backed analytics and billing.
When To Use This¶
Use this flow for imports, staged migrations, or multi-tenant systems that know stable external IDs before the corresponding Spendware records exist. Prefer normal customer-key logging when the customer and project already exist.
Partial logging requires a user API key, allow_unregistered_usage_logging enabled for that user, and explicit project_external_id, customer_external_id, and provider_external_id values.
This is an advanced import and migration path. Normal runtime integrations should create the project and customer first, then use a customer-scoped key; normal logging does not require external IDs, though stable IDs remain recommended for reconciliation.
Concepts¶
- Partial project: unresolved usage grouped by
project_external_id. - Partial customer: unresolved usage grouped by
project_external_idandcustomer_external_id. - Partial provider: unresolved usage grouped by provider/model identity.
- Finalization: creates or links the real resource and transfers eligible partial usage.
- Analytics inclusion: read requests default to finalized usage; use
partials_mode: include_eligible_partialswhen a report should include eligible unresolved partials.
Example¶
curl "$SPENDWARE_BASE_URL/api/v1/usage/log" \
-H "Authorization: Bearer $SPENDWARE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"project_external_id": "acme-ai",
"customer_external_id": "customer-a",
"provider_external_id": "openai:gpt-4o",
"service_provider_name": "openai",
"service_name": "gpt-4o",
"number_requests": 1,
"input_tokens": 120,
"output_tokens": 45,
"cached_tokens": 30,
"idempotency_key": "partial:usage-001"
}'
curl "$SPENDWARE_BASE_URL/api/v1/partial/projects?is_resolved=false" \
-H "Authorization: Bearer $SPENDWARE_API_KEY"
curl "$SPENDWARE_BASE_URL/api/v1/partial/projects/PARTIAL_PROJECT_ID/finalize" \
-H "Authorization: Bearer $SPENDWARE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Acme AI","currency":"USD","external_id":"acme-ai"}'
from spendware_provider_sdk.management import SpendwareManagementClient
client = SpendwareManagementClient(base_url="SPENDWARE_BASE_URL", api_key="SPENDWARE_API_KEY")
client.usage.log(
{
"project_external_id": "acme-ai",
"customer_external_id": "customer-a",
"provider_external_id": "openai:gpt-4o",
"service_provider_name": "openai",
"service_name": "gpt-4o",
"number_requests": 1,
"input_tokens": 120,
"output_tokens": 45,
"cached_tokens": 30,
"idempotency_key": "partial:usage-001",
}
)
partials = client.partial_usage.list_projects()
partial_id = partials["data"][0]["id"]
client.partial_usage.finalize_project(
partial_id,
{"title": "Acme AI", "currency": "USD", "external_id": "acme-ai"},
)
client.close()
import { SpendwareManagementClient } from "@spendware/provider-adapters-sdk/management";
const client = new SpendwareManagementClient({
baseUrl: "SPENDWARE_BASE_URL",
apiKey: "SPENDWARE_API_KEY",
});
await client.usage.log({
project_external_id: "acme-ai",
customer_external_id: "customer-a",
provider_external_id: "openai:gpt-4o",
service_provider_name: "openai",
service_name: "gpt-4o",
number_requests: 1,
input_tokens: 120,
output_tokens: 45,
cached_tokens: 30,
idempotency_key: "partial:usage-001",
});
const partials = await client.partialUsage.listProjects();
const partial = (partials.data as Array<{ id: string }>)[0];
await client.partialUsage.finalizeProject(partial.id, {
title: "Acme AI",
currency: "USD",
external_id: "acme-ai",
});
Expected Result¶
The log call returns an accepted usage response. The partial list shows unresolved resources until they are finalized. After finalization, eligible usage can be queried through normal usage analytics, and future logs with the same external IDs can resolve to the finalized resources.
Common Errors¶
403: unregistered usage logging is disabled for the user API key owner.422: one ofproject_external_id,customer_external_id, orprovider_external_idis missing.422: an external ID conflicts with an already resolved project or provider.404: the partial resource ID was deleted, resolved, or belongs to another user.
Provider finalization is an administrative flow and may require the platform usage-operations permission. Project and customer finalization are the usual developer-facing cleanup path.