X-OAuth-Client-Id header is requiredEvery external API request must identify its OAuth client.
Juice Reel developer platform
Connect a Juice Reel account with OAuth 2.0, then securely read subscriber relationships and the connected user's open or settled bets from your backend.
https://www.juicereel.com/oauth2/authorizehttps://external-api.juicereel.comOAuth 2.0
Send the user to Juice Reel to sign in and approve access. This is a browser redirect, not a server-to-server API call.
https://www.juicereel.com/oauth2/authorize| Field | Type | Description |
|---|---|---|
client_idrequired | string | Your approved OAuth client ID. |
redirect_urirequired | string | An exact redirect URI registered on the application. |
response_typerequired | string | Must be code. |
scoperequired | string | Space-delimited requested scopes. Send an empty value for identity-only access. |
staterequired | string | Unpredictable value created and validated by your application. |
code_challengerequired | string | Base64url SHA-256 digest of your PKCE verifier. |
code_challenge_methodrequired | string | Must be S256. |
| Field | Type | Description |
|---|---|---|
code | string | Short-lived, single-use authorization code exchanged by your backend. |
state | string | The original state. Reject the callback if it does not exactly match your stored value. |
OAuth 2.0
Exchange the callback code for an access token and rotating refresh token. Perform this request only from your backend.
/oauth2/token| Field | Type | Description |
|---|---|---|
grant_typerequired | string | Must be authorization_code. |
coderequired | string | Code received by your redirect URI. |
redirect_urirequired | string | Must exactly match the URI used during authorization. |
code_verifierrequired | string | Original PKCE verifier used to create the challenge. |
| Field | Type | Description |
|---|---|---|
access_token | string | Bearer token used for resource requests. Store server-side. |
refresh_token | string | Single-use token used to obtain a replacement token pair. |
token_type | string | Bearer. |
expires_in | integer | Access-token lifetime in seconds; currently 3600. |
scope | string | Space-delimited scopes granted by the user. |
OAuth 2.0
Use a refresh token once to receive a new access token and a replacement refresh token.
/oauth2/token| Field | Type | Description |
|---|---|---|
grant_typerequired | string | Must be refresh_token. |
refresh_tokenrequired | string | The current unused refresh token. |
| Field | Type | Description |
|---|---|---|
access_token | string | New one-hour bearer token. |
refresh_token | string | Replacement refresh token. Discard the token used for this request. |
token_type | string | Bearer. |
expires_in | integer | Access-token lifetime in seconds. |
scope | string | Scopes retained by the authorization grant. |
OAuth 2.0
Revoke an access token or refresh-token grant when a user disconnects Juice Reel from your application.
/oauth2/revoke| Field | Type | Description |
|---|---|---|
tokenrequired | string | Access token or refresh token to revoke. |
token_type_hint | string | Optional access_token or refresh_token hint. |
| Field | Type | Description |
|---|---|---|
HTTP 200 | empty object | Revocation is idempotent and does not reveal whether the token existed. |
Checkout
Create a hosted checkout session that wraps the Juice Reel purchase flow. Redirect the user's browser to checkoutUrl; after checkout succeeds, Juice Reel shows the OAuth consent screen and then redirects to successUrl with a short-lived authorization code and the supplied state. A cancelled checkout redirects to cancelUrl with its status and state. Exchange the authorization code at /oauth2/token; access tokens and client secrets must never be placed in URLs.
/checkout/sessions| Field | Type | Description |
|---|---|---|
successUrlrequired | string | Exact redirect URI registered on the OAuth application for a successful checkout. |
cancelUrlrequired | string | Exact redirect URI registered on the OAuth application for a cancelled checkout. |
scopesrequired | string[] | Requested OAuth scopes. May be an empty array for identity-only access; otherwise scopes must be unique and a subset of the scopes approved for the OAuth application. |
staterequired | string | Opaque value generated by your backend and returned unchanged to your successUrl or cancelUrl. |
codeChallengerequired | string | PKCE S256 code challenge generated by your backend for the authorization-code exchange. |
| Field | Type | Description |
|---|---|---|
checkoutSessionId | string | Unique identifier for the checkout session. |
checkoutUrl | string | URL to open in the user's browser to begin checkout. |
expiresAt | string (ISO 8601) | Time at which the checkout session expires. |
Account
Return the connected user ID represented by a bearer token, or the OAuth app owner when using client credentials.
/oauth2/me| Field | Type | Description |
|---|---|---|
id | integer | Stable Juice Reel user identifier for the authorization grant. |
displayName | string | User's current public display name. |
Subscribers
List everyone who has ever subscribed to the OAuth application owner, including expired and sandbox subscriptions.
/oauth2/subscribers/current| Field | Type | Description |
|---|---|---|
subscribers | array | All subscriber records for the OAuth application owner, including expired subscriptions, ordered by latest expiration and creation time. |
subscriptionProductId | string | Stable identifier of the subscription product purchased from the application owner. |
userId | integer | Subscriber's stable Juice Reel user ID. |
expiresDate | datetime | ISO 8601 expiration or renewal boundary. |
isCurrentlySubscribed | boolean | True when expiresDate is later than the time of the API request. |
User.id | integer | Subscriber's stable Juice Reel user ID. |
User.displayName | string | Subscriber's current Juice Reel display name. |
SubscriptionProduct.duration | string | Billing duration: WEEKLY, MONTHLY, or YEARLY. |
Subscribers
Check whether the user represented by the access token is currently subscribed to the OAuth application owner.
/oauth2/subscribers/status| Field | Type | Description |
|---|---|---|
isCurrentlySubscribed | boolean | True when the authenticated user has a non-expired subscription to the OAuth application owner. |
subscription | object | omitted | Active subscription record. Omitted when no active record exists or the authenticated user owns the application. |
subscription.subscriptionProductId | string | Stable identifier of the purchased subscription product. |
subscription.userId | integer | Subscriber's stable Juice Reel user ID. |
subscription.expiresDate | datetime | ISO 8601 expiration or renewal boundary. |
subscription.isCurrentlySubscribed | boolean | True when this subscription record has not expired. |
subscription.SubscriptionProduct.duration | string | Billing duration: WEEKLY, MONTHLY, or YEARLY. |
Bets
Return the connected user's open bets using cursor-based pagination.
/oauth2/bets/open| Field | Type | Description |
|---|---|---|
datePlacedAfter | string | Optional inclusive lower bound for bet placement time, as an ISO 8601 datetime. Omit to include the earliest available bets. |
datePlacedBefore | string | Optional exclusive upper bound for bet placement time, as an ISO 8601 datetime. Omit to include the latest available bets. |
cursor | string | Opaque nextCursor from the previous response. Omit for the first page; reuse the same date filters when requesting the next page. |
| Field | Type | Description |
|---|---|---|
nextCursor | string | null | Pass this opaque value as cursor on the next request. Null means paging is complete. |
hasMore | boolean | True when another page is available. Equivalent to nextCursor being non-null. |
| Bet | A bet in descending ID order for the connected Juice Reel user. |
Bets
Return the connected user's closed and cancelled bets using cursor-based pagination.
/oauth2/bets/settled| Field | Type | Description |
|---|---|---|
dateClosedAfter | string | Optional inclusive lower bound for settlement time, as an ISO 8601 datetime. Omit to include the earliest available settled bets. |
dateClosedBefore | string | Optional exclusive upper bound for settlement time, as an ISO 8601 datetime. Omit to include the latest available settled bets. |
cursor | string | Opaque nextCursor from the previous response. Omit for the first page; reuse the same date filters when requesting the next page. |
| Field | Type | Description |
|---|---|---|
nextCursor | string | null | Pass this opaque value as cursor on the next request. Null means paging is complete. |
hasMore | boolean | True when another page is available. Equivalent to nextCursor being non-null. |
| Bet | A bet in descending ID order for the connected Juice Reel user. |
Bets
Return the connected user's open and settled bets together in one cursor-paginated feed for reconciliation.
/oauth2/bets/changed| Field | Type | Description |
|---|---|---|
updatedAtAfter | string | Optional inclusive lower bound for updatedAt, as an ISO 8601 timestamp. Only bets updated at or after this time are returned. |
cursor | string | Opaque nextCursor from the previous response. Omit for the first page; reuse it until hasMore is false. |
| Field | Type | Description |
|---|---|---|
nextCursor | string | null | Pass this opaque value as cursor on the next request. Null means paging is complete. |
hasMore | boolean | True when another page is available. Equivalent to nextCursor being non-null. |
| Bet | A bet in descending ID order for the connected Juice Reel user. |
Usage
API requests are limited in five-minute buckets.
Need a higher limit? Email support@juiceintegration.com.
Errors
Resource errors use an error field. OAuth protocol errors use error and error_description.
X-OAuth-Client-Id header is requiredEvery external API request must identify its OAuth client.
cursor is invalidThe supplied bet cursor is malformed or was not issued by Juice Reel.
invalid_request / invalid_grantOAuth form fields are malformed, the code is expired or consumed, PKCE failed, or the refresh token is invalid.
Invalid OAuth access tokenThe bearer token is missing, expired, revoked, belongs to another client, or its grant is no longer active.
invalid_clientClient ID, client secret, or HTTP Basic authentication is invalid.
The access token requires the requested scopeThe grant does not contain the scope required by this resource.
Only the OAuth client owner can access this resourceThe subscribers endpoint may only be called with a token issued for the user who owns the OAuth application.
Rate limit exceededThe IP address or OAuth client ID has exceeded its five-minute request limit. Wait for the bucket to reset before retrying.
Internal server errorA transient server failure occurred. Retry idempotent GET requests with exponential backoff.
Webhooks
Webhooks let your server receive real-time events from Juice Reel without polling for updates.
import express from "express";
import crypto from "node:crypto";
const app = express();
function verifyJuiceReelWebhook(rawBody, signature, secret) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
const received = Buffer.from(signature, "utf8");
const computed = Buffer.from(expected, "utf8");
return received.length === computed.length
&& crypto.timingSafeEqual(received, computed);
}
app.post(
"/webhooks/juice-reel",
express.raw({ type: "application/json" }),
(req, res) => {
const signature = req.get("X-Juice-Reel-Signature");
const secret = process.env.JUICE_REEL_WEBHOOK_SIGNING_SECRET;
if (!signature || !secret || !verifyJuiceReelWebhook(req.body, signature, secret)) {
return res.sendStatus(401);
}
// Parse only after the HMAC has been verified.
const event = JSON.parse(req.body.toString("utf8"));
console.log("Juice Reel event:", event.type, event.data);
return res.sendStatus(204);
},
);Webhooks
Receive real-time updates when a subscriber purchases, renews, or cancels a subscription.
| Event type | When it is sent |
|---|---|
| A user completes an initial subscription purchase. | |
| An existing subscription successfully renews. | |
| A subscription is cancelled or will not renew. | |
| A subscription reaches its expiration date and access ends. |
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for this webhook event. |
type | string | The subscription event type selected in the event list above. |
createdAt | datetime | ISO 8601 timestamp for when Juice Reel created the event. |
| object | Subscription details associated with the event. |
The signature uses the signing secret shown once when you create the webhook endpoint.