GET /v1/credits/events
Per-event credit ledger — every charge, refund, grant, and purchase on the calling org. Filter by project for attribution.
GET
/v1/credits/eventsPhase 1stable
- Auth
- Bearer
Returns one row per wallet movement. Use this to attribute spend by project, reconcile your own counter against ours, or build a credit-usage dashboard.
GET /v1/credits gives you the wallet snapshot; /v1/credits/events gives you the audit trail. They complement each other — the snapshot is the answer to "how much do I have right now?", events are the answer to "what happened?"
Query parameters
projectIdstringoptionalFilter to events attributed to one project. `null`-projectId events (grants, purchases, admin adjustments) are excluded when this filter is set.eventTypestringoptionalFilter to one event type. See "Event types" below.One of:usage,refund,grant,purchase,adjustment,allocationsincestring (ISO 8601 Z)optionalOnly events `>= since`. UTC required (Z suffix).untilstring (ISO 8601 Z)optionalOnly events `<= until`. UTC required (Z suffix).cursorstringoptionalOpaque cursor from the previous page.limitintegeroptionaldefault: 25Page size, 1-100.
Example request
curl "https://api.layers.com/v1/credits/events?projectId=prj_13fd8406-387a-4472-b6a2-531860557a6e&eventType=usage" \
-H "Authorization: Bearer $LAYERS_API_KEY"const url = new URL("https://api.layers.com/v1/credits/events");
url.searchParams.set("projectId", "prj_13fd8406-387a-4472-b6a2-531860557a6e");
url.searchParams.set("eventType", "usage");
const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.LAYERS_API_KEY}` },
});
const { items, nextCursor } = await res.json();import os, requests
res = requests.get(
"https://api.layers.com/v1/credits/events",
params={"projectId": "prj_…", "eventType": "usage"},
headers={"Authorization": f"Bearer {os.environ['LAYERS_API_KEY']}"},
)
data = res.json()Response
200OK
{
"items": [
{
"eventId": "a962df6d-2ea8-4dff-8a68-d49b1dc74d33",
"projectId": "prj_13fd8406-387a-4472-b6a2-531860557a6e",
"credits": -50,
"eventType": "usage",
"format": "slideshow-builder",
"containerId": "cnt_a861adb5-3a48-48a4-a18d-c70129ebefa7",
"workflowId": "partner-content-a861adb5-3a48-48a4-a18d-c70129ebefa7-v0",
"balanceAfterPrepaid": 4200,
"usageAfterPeriod": 3000,
"createdAt": "2026-05-12T21:17:15.903Z"
},
{
"eventId": "1133c79a-e49e-4e52-81e7-ac13ef77d72a",
"projectId": null,
"credits": 1000,
"eventType": "grant",
"format": null,
"containerId": null,
"workflowId": null,
"balanceAfterPrepaid": null,
"usageAfterPeriod": null,
"createdAt": "2026-05-12T15:00:00.000Z"
}
],
"nextCursor": null
}Event types
| Type | Sign of credits | When |
|---|---|---|
usage | always negative | Content-generation charge (slideshow-builder, slideshow-remix, video-remix, ugc-remix). Charged synchronously when the workflow starts rendering. |
refund | always positive | Automatic refund when a paid generation fails post-charge, OR an admin-initiated reversal. |
grant | always positive | Free credits granted by Layers — onboarding, promo, velocity bonus, manual admin grant. |
purchase | always positive | Prepaid credit pack purchased via Stripe. |
adjustment | either sign | Admin manual adjustment. Direction is on the sign of credits. |
allocation | either sign | A sub-org credit transfer between you and one of your children. On your own ledger it's negative when you fund a child (allocate) and positive when an archived child's unspent credits are reclaimed to you. metadata.direction is allocate or reclaim; metadata.counterpartyOrgId (org_-prefixed) names the child, and metadata.transferId (txn_-prefixed) matches the id the allocate response returns. |
Field notes
creditsis signed — debits negative, credits positive. Sum them for a wallet-level reconciliation.projectIdisnullfor org-level events (grants, purchases, admin adjustments, allocations). When filtering byprojectId, those rows are excluded by design.formatandcontainerIdpopulate when the event is tied to a partner-API content-generation workflow. In-app generations from the Layers dashboard also charge but don't surface aformat/containerIdhere.balanceAfterPrepaid/usageAfterPeriodare reconciliation breadcrumbs — what the wallet looked like immediately after this event.nullwhen the event didn't touch that side of the wallet.- Ordered newest first, paginated via
nextCursor. The cursor is opaque — pass it back verbatim on the next call.
Common patterns
Project spend this billing period:
curl "https://api.layers.com/v1/credits/events\
?projectId=prj_…&eventType=usage&since=$(date -u +%Y-%m-01T00:00:00Z)" \
-H "Authorization: Bearer $LAYERS_API_KEY" \
| jq '[.items[] | .credits] | add'Per-format breakdown for a project:
curl "https://api.layers.com/v1/credits/events?projectId=prj_…&eventType=usage" \
-H "Authorization: Bearer $LAYERS_API_KEY" \
| jq -r '.items | group_by(.format) | map({format: .[0].format, total: ([.[].credits] | add)})'Errors
| Status | Code | When |
|---|---|---|
| 422 | VALIDATION | Bad projectId shape, malformed timestamp (offset form like +00:00 is rejected — Z-suffix UTC only), invalid eventType. |
| 401 | UNAUTHENTICATED | Missing or invalid key. |
| 503 | KILL_SWITCH | Key or org disabled. |
See also
- GET /v1/credits — wallet snapshot (the answer to "how much do I have now?")