Skip to content

That month's reconciliation detail, per store

GET
/billing/monthly
curl --request GET \
--url 'https://data.creatorain.com/partner/v1/billing/monthly?month=2026-09&limit=50' \
--header 'Authorization: Bearer <token>'

One row per store enrolled under your key, except that a TERMINATED store with nothing to reconcile in the month (no video published, no visit completed and no no-show) is left out instead of listed as a line of zeros. A terminated store that did have any of those stays, with status: terminated. A page can therefore hold fewer rows than limit: follow next_cursor until it is null. Each row: videos published in the month, the plan the store is on, completed visits and no-shows, and the finished amounts for the month. This is what an invoice is checked against. You do no price maths: amount_due is what we bill you for the store, merchant_amount is what the merchant owes with your surcharge included.

THE AMOUNTS. billed_videos = videos_published - makeup_videos (free replacement videos are never billed). merchant_amount = billed_videos x (the flat merchant price + your surcharge, both locked on the store for THAT month). amount_due is what we bill you: for each billed video, that video’s actual creator cost plus our fee per video (fee_per_video in the pricing block), summed. A video whose creator was paid nothing (comped) still carries the fee. It is not a fixed price per video. A plan or approval change re-locks from the next month, so each month uses its own lock. A store with no locked price (confirmed before pricing) is billed the same way from the current default terms: creator cost plus our fee, and merchant_amount from the default merchant price. Both are null only when pricing is not set up at all. While a billed video’s creator cost is not known yet, amount_due is null and cost_pending_videos says how many videos are waiting; it is never a 0 that under-bills. See amounts_note. Currency units, like every price on this API.

THE MONTH IS THE STORE’S MONTH. A video published at 23:30 on 30 September in Los Angeles is a September video; bucketed in UTC it would be 06:30 on 1 October and would land on the wrong invoice, with both sides looking at a real timestamp and reading it in different clocks. Each store’s window is resolved on that store’s own clock, and it is half open: [local 1st 00:00, local 1st of next month 00:00), so no video is counted twice or lost.

Visits are counted by VISIT DATE, which our reservations already store in the venue’s own calendar, so a visit marked completed in October for a September booking is a September visit.

month is REQUIRED and has no default. “This month” would be the obvious default and is the wrong one here: a scheduled job whose parameter went missing would silently start reporting whichever month it ran in, and the first sign of that is a merchant charged twice.

note states the one limitation of these numbers and is on every response. Our per-store pin on content landed on 2026-09-09, so anything published before that date carries no store and is not counted here.

PARTNER KEY ONLY. A store token cannot read this: a chain’s billing is your business, not one merchant’s.

month
required
string
/^\d{4}-(0[1-9]|1[0-2])$/

The month to report, YYYY-MM, in each STORE’S own calendar. Required, with no default.

Example

2026-09
cursor
string

The next_cursor from the previous page, sent back UNCHANGED. Omit it for the first page.

It is opaque: do not build one yourself. It names a POSITION in the sort order rather than a count of rows to skip, which is why a page stays stable when a booking is touched between two requests. A cursor we did not issue is refused with 400 validation_error, rather than silently treated as the first page, so a broken client fails loudly instead of looping over page one for ever.

limit
integer
default: 50 >= 1 <= 200

Rows per page. Default 50, maximum 200. A larger value is CLAMPED to 200 rather than refused, so you never have to guess the ceiling.

A page of stores, with the month’s counts for each.

Media typeapplication/json
object
month
required

The month asked for, echoed back, so a stored report says what it is of.

string
note
required

The one limitation of these numbers, on every response rather than in a document somebody has to remember to read.

string
amounts_note
required

When the amounts are null, and that make-up videos are never billed. On every response.

string
generated_at
required

When this was computed, RFC 3339 in UTC. A fact about our run rather than a time at any store, so it carries no store offset.

string format: date-time
items
required
Array<object>

One store’s month. Counts only: there is no personal data anywhere in this report.

object
partner_location_id

YOUR id for the store, which is what your own billing is keyed on.

string
location_id

Our id for the same store.

string format: uuid
videos_published

Videos filmed at this store that are LIVE and were published inside the month IN THIS STORE’S OWN CLOCK. See the endpoint description for why that matters. Only videos made on your key’s campaign count: a video another campaign made at the same restaurant is not on your invoice.

integer
plan_videos_per_month

The plan in force in THAT month (a plan change applies from the next month), echoed so an invoice line can be checked without a second call.

integer
nullable
plan_term_months
integer
nullable
reservations_completed

Visits at this store marked completed, by VISIT DATE in the store’s calendar. Only visits booked on your key’s campaign count.

integer
no_shows

Visits at this store marked no_show, by the same date and the same campaign.

integer
status

The store’s state at the moment of the read, so a row of zeroes can be explained without a second call. A terminated store appears only if it had a video or a visit in the month.

string
Allowed values: active paused terminated
makeup_videos

Of videos_published, the free replacement videos (after a no-show, or for a video the merchant rejected). Never billed.

integer
billed_videos

videos_published - makeup_videos: the videos the two amounts are for.

integer
cost_pending_videos

Billed videos whose creator cost is not known yet. While above 0, amount_due is null.

integer
currency

The currency of the amounts. Null when the store has no locked price.

string
nullable
amount_due

What CreatoRain bills you for this store this month: the sum over billed videos of (that video’s creator cost + our locked fee per video). Null for a store with no locked price, or while cost_pending_videos is above 0.

number
nullable
merchant_amount

What the merchant owes for the month with your surcharge included, ready for your invoice: billed_videos x (the flat merchant price + your locked surcharge). Null for a store with no locked price.

number
nullable
next_cursor
required

Send this back as cursor for the next page of STORES. The stores are ordered by partner_location_id ascending rather than by recency, because a reconciliation file is diffed against last month’s and a stable order is worth more here than newness.

string
nullable

Example

{
"month": "2026-09",
"note": "content published before 2026-09-09 carries no store and is not counted",
"amounts_note": "amount_due and merchant_amount are null for a store with no locked price (confirmed before pricing); amount_due is also null while a billed video has no known creator cost (see cost_pending_videos); make-up videos are never billed",
"items": [
{
"partner_location_id": "store_123",
"videos_published": 2,
"plan_videos_per_month": 2,
"plan_term_months": 3,
"reservations_completed": 3,
"no_shows": 1,
"status": "active",
"makeup_videos": 0,
"billed_videos": 3,
"cost_pending_videos": 0,
"currency": "USD",
"amount_due": 450,
"merchant_amount": 1125
}
]
}

validation_error. field names the offending parameter.

Media typeapplication/json
object
error
required
object
code
required

The stable value to branch on.

string
Allowed values: unauthorized forbidden_ip rate_limited validation_error location_already_exists location_not_found location_terminated reservation_not_found conflict internal_error
message
required

A human sentence. Copy, not contract; it may be reworded.

string
field

The request field at fault, dotted for a nested one (plan.term_months).

string

Example

{
"error": {
"code": "unauthorized"
}
}

unauthorized. A missing, malformed, unknown or revoked key; or an invalid, expired or wrong-store dashboard token. One answer for every case on purpose: the holder of a bad credential learns nothing from the difference.

Media typeapplication/json
object
error
required
object
code
required

The stable value to branch on.

string
Allowed values: unauthorized forbidden_ip rate_limited validation_error location_already_exists location_not_found location_terminated reservation_not_found conflict internal_error
message
required

A human sentence. Copy, not contract; it may be reworded.

string
field

The request field at fault, dotted for a nested one (plan.term_months).

string

Example

{
"error": {
"code": "unauthorized"
}
}

rate_limited. Over this key’s requests-per-minute ceiling.

Media typeapplication/json
object
error
required
object
code
required

The stable value to branch on.

string
Allowed values: unauthorized forbidden_ip rate_limited validation_error location_already_exists location_not_found location_terminated reservation_not_found conflict internal_error
message
required

A human sentence. Copy, not contract; it may be reworded.

string
field

The request field at fault, dotted for a nested one (plan.term_months).

string

Example

{
"error": {
"code": "unauthorized"
}
}