Skip to main content
Studio offers are owned by one studio and one API environment. The API accepts discount terms and selectors; Batch Relay checks eligibility, calculates discounts, provisions any payment-provider objects, and records redemptions. All offer amounts use USD cents. Code offers can be entered by customers in hosted Stripe Checkout when code entry is enabled for that environment. Publishing a code through this API does not enable the checkout field. Automatic offers are selected by the checkout service. Creating an offer saves a draft; activate or schedule the returned revision before expecting it to be used.

Access and scopes

Use an account-owned API key, the exact studio ID in each path, and an active grant for that studio. Anonymous sessions cannot manage offers. Test and Live keys select separate offer environments; a Test Mode offer does not become a Live offer. Use a Test Mode key to exercise offer writes in the isolated Test environment before making a Live change. Studio roles also apply: owners and admins can manage offers, while other authorized studio staff can read them. offers:write does not imply offers:read; give a key both scopes only when its workflow needs both. Checkout-link creation, preparation, and recovery also require offers:apply, alongside the checkout-link and print-order scopes. See Checkout Links.

Routes

All routes are under /v1/studios/{studio_id} and require the exact studio grant.

Create a code

The create request accepts a complete set of terms. It does not accept Stripe IDs, a calculated price, or a client-supplied discount amount for an order. Code values must be uppercase, contain no whitespace, and be at most 80 characters. Percentage discounts use whole percentages from 1 through 100; fixed discounts use a positive amount in cents. Currency must be USD. For a fixed amount, set kind to fixed and send amount_off_cents instead of percent_off.
application is code for a customer-entered code or automatic for a server-selected offer. Automatic offers must omit code. Scope can be storewide, event, guardian, product, or package; event and guardian scopes take their matching ID, while product and package scopes take a sellable_ids array. limits.max_redemptions is optional and must be positive when supplied. starts_at, ends_at, and minimum_subtotal_cents are optional. The 201 response contains the offer and its first immutable revision:
The example response is illustrative; provisioning status and timestamps come from the service. Store the returned id, revision, and revision_id for subsequent operations.

Revise terms, then activate that revision

Terms are immutable after creation. PATCH appends a candidate revision and does not activate it. Send the full replacement terms under terms, the revision number you read as expected_revision, and a new idempotency key for this mutation.
The response contains the new candidate revision, for example revision: 2 and revision_id: "revision_example_2". Activate it explicitly:
Activation selects the named immutable revision for new checkouts. Existing checkouts and paid orders retain their frozen revision and amounts. A stale expected_revision returns 409 offer_conflict; read the current offer and repeat the intended change with the current revision and a new idempotency key.

Schedule, pause, resume, or revoke

Scheduling also selects a revision. starts_at must be in the future, and ends_at, when supplied, must be later than starts_at.
Pause, resume, and revoke use the same lifecycle route pattern and require expected_revision plus an idempotency key. A short reason may be included. To remove an offer from future use, revoke it. This action cannot be reversed:
Revocation is the permanent lifecycle action for an offer. It retains the offer, its revisions, redemptions, and audit history. There is no hard-delete route. Pause is reversible; revoke is the terminal state.

List offers and continue a page

GET /v1/studios/{studio_id}/offers returns all offers visible to the studio; the API does not take a status filter. Filter returned items by status when you need a view such as active codes. For active codes, keep items where status is active and application is code. The default page size is 50. Set limit from 1 through 100 and pass next_cursor back as the opaque cursor query parameter until is_done is true.
Request the next page as GET /v1/studios/stu_example/offers?limit=100&cursor=opaque-cursor, URL encoding the cursor value in an HTTP client. next_cursor is null when the page is complete.

Read redemption history

Redemption pages use the same items, next_cursor, and is_done envelope. Each item includes the offer revision, order ID when available, discount cents, currency, status, and creation time. Status is succeeded, partially_refunded, or refunded. Follow every cursor to read the complete history; a single page is not a complete usage report.

Preview margin

Margin preview uses the separate margins:read scope and the server’s current catalog and provider-cost evidence. It is an estimate, not an order quote. Unknown or stale costs remain marked in cost_status.
The response identifies the revision used for the estimate, line totals, and cost status. When cost is unknown, promotion or contribution fields may be omitted:

Idempotency and errors

Every create, revision, and lifecycle write requires an Idempotency-Key with at most 200 characters. Keep the same key and exact request body when retrying an uncertain result. Use a new key for a distinct intended mutation. Reusing a key with different input or sending a stale revision returns 409 offer_conflict. See Authentication, Idempotency, and Errors.