Information
- OpenAPI version:
3.0.3
The surface an outside system (a POS or ordering platform) uses to enrol its merchants’ stores with CreatoRain, and later to read those stores’ reservations and published content.
One long-lived API key per partner, sent as Authorization: Bearer <key>. Keys look like
crp_live_... on production and crp_test_... everywhere else; the tag is a label for
humans and is not what grants access. A key may also carry an IP allowlist, in which case a
request from any other address is refused with 403 forbidden_ip.
A key is scoped to ONE account and ONE campaign. It cannot read another customer’s data, and it is not an administrative credential.
THE HEADER IS Authorization, on every route and for both credentials. There is no x-api-key
header on this API and a key sent in one is not read, so a request carrying only that answers
401 unauthorized. Said plainly because it is the wrong guess an integrator makes once and
then spends an afternoon on: the refusal looks identical to a bad key.
Every failing response carries the same envelope, with a stable machine-readable code:
{ "error": { "code": "location_already_exists", "message": "...", "field": "partner_location_id" } }
Branch on code, never on message. field is present when one request field is at fault.
600 requests per minute per key by default, over a 60 second window. Over the ceiling the
answer is 429 with code: rate_limited and the standard RateLimit-* headers.
Every time value is RFC 3339 with the STORE’S offset (2026-09-11T15:00:00-07:00), and the
object carries a sibling timezone field holding the IANA name (America/Los_Angeles).
Stores span time zones, so a UTC-only value renders “3 pm today” wrongly in a merchant UI.
A store’s plan is paced by the month. Months are calendar months on the STORE’S clock, and a
season is three of them, counted from the month that holds the season start (the store-local
day of approved_at). In each month a store is offered to creators until the season’s slots
booked or in flight reach videos_per_month for each month of the season so far (so a
month that booked fewer than its share leaves the rest to the next month of the same season),
never more than the season total. Then the store is not offered again until the 1st of the
next month, store time. Nothing carries across a season boundary. A plan change by PATCH
applies from the 1st of the next month. The whole-term total (videos_per_month x
term_months) still applies on top.
quarter_target is renamed season_target (videos per season: videos_per_month x 3) in
GET /locations/{id} progress and in the location.activated webhook. On the
location.quarter_summary webhook the target is sent as season_target, and the old target
field carries the same value. For ONE release both names are sent with the same value; read
season_target. Before this release quarter_target and target held the whole-term total.
The partner API key, issued out of band. Authorization: Bearer crp_live_....
Not a JWT: it is an opaque credential and carries no claims.
Security scheme type: http
A PER-STORE, 30 minute, READ-ONLY token from
POST /locations/{partner_location_id}/dashboard-token, sent as
Authorization: Bearer <token>.
Accepted by the three GET /locations/{partner_location_id}... routes and NOWHERE ELSE.
Presented anywhere else, or for any other store, it answers 401 unauthorized. This is
what lets a merchant’s browser read their own store without ever holding your master key.
Security scheme type: http
The same per-store token in the query string, because an embedded iframe cannot set a header. When both are present the query string wins, since that is the URL the page was loaded with.
Security scheme type: apiKey
Query parameter name: token