Skip to content

Overview

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.

Authentication

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.

Errors

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.

Rate limit

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.

Time

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.

Monthly pacing

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.

Deprecations

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.

Information

  • OpenAPI version: 3.0.3

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