#Organization
GET /organization/setup
Get where the organization stands on setting each area up: reviews, loyalty, wishlists and the store itself. It answers in one call: the signals read off the organization's own records (has a site, has a publishable key, has a connected store, has products, has an active survey flow, has received a review, has a loyalty program, has it on a site), and per area whether it still needs setting up. An area that is already running is recorded as set up the first time it is seen, so it never goes back to asking.
Requires the settings:read permission.
curl \
"https://api.cascade.dev/organization/setup" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { getOrganizationSetup } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getOrganizationSetup()require "net/http"
uri = URI("https://api.cascade.dev/organization/setup")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/organization/setup",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"signals": {
"hasSite": true,
"hasPublishableKey": true,
"hasIntegration": true,
"hasProducts": true,
"hasActiveSurveyFlow": true,
"hasReview": true,
"hasLoyaltyProgram": true,
"loyaltyProgramOnSite": true,
"hasWishlist": true
},
"areas": {
"reviews": {
"completedAt": "2025-01-15T09:30:00Z",
"dismissedAt": "2025-01-15T09:30:00Z",
"configured": true,
"needsSetup": true
},
"loyalty": {
"completedAt": "2025-01-15T09:30:00Z",
"dismissedAt": "2025-01-15T09:30:00Z",
"configured": true,
"needsSetup": true
},
"wishlists": {
"completedAt": "2025-01-15T09:30:00Z",
"dismissedAt": "2025-01-15T09:30:00Z",
"configured": true,
"needsSetup": true
},
"site": {
"completedAt": "2025-01-15T09:30:00Z",
"dismissedAt": "2025-01-15T09:30:00Z",
"configured": true,
"needsSetup": true
}
},
"publishableKey": "string",
"embedScriptUrl": "string",
"apiBaseUrl": "string"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"signals": {
"type": "object",
"properties": {
"hasSite": {
"type": "boolean",
"description": "Whether the organization has at least one site."
},
"hasPublishableKey": {
"type": "boolean",
"description": "Whether a publishable key exists for the storefront widgets to authenticate with."
},
"hasIntegration": {
"type": "boolean",
"description": "Whether a store is connected, which today means a Shopify store."
},
"hasProducts": {
"type": "boolean",
"description": "Whether any products have arrived, from a connected store or an import. Without them a review request has nothing to ask about and a loyalty rule has nothing to measure."
},
"hasActiveSurveyFlow": {
"type": "boolean",
"description": "Whether a survey flow is switched on and will send review requests."
},
"hasReview": {
"type": "boolean",
"description": "Whether any review has arrived, however it got here."
},
"hasLoyaltyProgram": {
"type": "boolean",
"description": "Whether a loyalty program exists, draft or live."
},
"loyaltyProgramOnSite": {
"type": "boolean",
"description": "Whether a loyalty program is attached to a site, which is what puts it on a storefront."
},
"hasWishlist": {
"type": "boolean",
"description": "Whether any shopper has saved a wishlist."
}
},
"required": [
"hasSite",
"hasPublishableKey",
"hasIntegration",
"hasProducts",
"hasActiveSurveyFlow",
"hasReview",
"hasLoyaltyProgram",
"loyaltyProgramOnSite",
"hasWishlist"
],
"additionalProperties": false,
"description": "What the organization's own records say, read in one query."
},
"areas": {
"type": "object",
"properties": {
"reviews": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"loyalty": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"wishlists": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"site": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
}
},
"required": ["reviews", "loyalty", "wishlists", "site"],
"additionalProperties": false,
"description": "Where each area stands, on its own. Areas are never judged together."
},
"publishableKey": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The organization's publishable key, so a setup screen can show the storefront snippet already filled in. Null when none has been created yet."
},
"embedScriptUrl": {
"type": "string",
"description": "Where the storefront widget bundle is published."
},
"apiBaseUrl": {
"type": "string",
"description": "The API origin the widgets should call."
}
},
"required": ["signals", "areas", "publishableKey", "embedScriptUrl", "apiBaseUrl"],
"additionalProperties": false,
"description": "Where setup stands afterward."
}PATCH /organization/setup
Record what was decided about one area: it was set up, or it was skipped. Either way the area stops being offered, and neither can be undone. The other areas are left alone. This is also how the store area is settled, since connecting a store creates nothing here.
Requires the settings:write permission.
Body Parameters
areareviews | loyalty | wishlists | siterequiredThe area being settled.
outcomecompleted | dismissedrequiredWhat happened: completed means the area was set up, dismissed means the merchant chose not to run it for now. Either way the area stops being offered, and neither can be undone.
curl \
-X PATCH \
"https://api.cascade.dev/organization/setup" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"area": "reviews",
"outcome": "completed"
}'import { client } from "@cascade-commerce/api/admin/client"
import { patchOrganizationSetup } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await patchOrganizationSetup({
body: {
area: "reviews",
outcome: "completed",
},
})require "net/http"
uri = URI("https://api.cascade.dev/organization/setup")
req = Net::HTTP::Patch.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"area": "reviews",
"outcome": "completed"
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"area": "reviews",
"outcome": "completed"
}"""
req = Request(
"https://api.cascade.dev/organization/setup",
data=body.encode(),
method="PATCH",
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"signals": {
"hasSite": true,
"hasPublishableKey": true,
"hasIntegration": true,
"hasProducts": true,
"hasActiveSurveyFlow": true,
"hasReview": true,
"hasLoyaltyProgram": true,
"loyaltyProgramOnSite": true,
"hasWishlist": true
},
"areas": {
"reviews": {
"completedAt": "2025-01-15T09:30:00Z",
"dismissedAt": "2025-01-15T09:30:00Z",
"configured": true,
"needsSetup": true
},
"loyalty": {
"completedAt": "2025-01-15T09:30:00Z",
"dismissedAt": "2025-01-15T09:30:00Z",
"configured": true,
"needsSetup": true
},
"wishlists": {
"completedAt": "2025-01-15T09:30:00Z",
"dismissedAt": "2025-01-15T09:30:00Z",
"configured": true,
"needsSetup": true
},
"site": {
"completedAt": "2025-01-15T09:30:00Z",
"dismissedAt": "2025-01-15T09:30:00Z",
"configured": true,
"needsSetup": true
}
},
"publishableKey": "string",
"embedScriptUrl": "string",
"apiBaseUrl": "string"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"signals": {
"type": "object",
"properties": {
"hasSite": {
"type": "boolean",
"description": "Whether the organization has at least one site."
},
"hasPublishableKey": {
"type": "boolean",
"description": "Whether a publishable key exists for the storefront widgets to authenticate with."
},
"hasIntegration": {
"type": "boolean",
"description": "Whether a store is connected, which today means a Shopify store."
},
"hasProducts": {
"type": "boolean",
"description": "Whether any products have arrived, from a connected store or an import. Without them a review request has nothing to ask about and a loyalty rule has nothing to measure."
},
"hasActiveSurveyFlow": {
"type": "boolean",
"description": "Whether a survey flow is switched on and will send review requests."
},
"hasReview": {
"type": "boolean",
"description": "Whether any review has arrived, however it got here."
},
"hasLoyaltyProgram": {
"type": "boolean",
"description": "Whether a loyalty program exists, draft or live."
},
"loyaltyProgramOnSite": {
"type": "boolean",
"description": "Whether a loyalty program is attached to a site, which is what puts it on a storefront."
},
"hasWishlist": {
"type": "boolean",
"description": "Whether any shopper has saved a wishlist."
}
},
"required": [
"hasSite",
"hasPublishableKey",
"hasIntegration",
"hasProducts",
"hasActiveSurveyFlow",
"hasReview",
"hasLoyaltyProgram",
"loyaltyProgramOnSite",
"hasWishlist"
],
"additionalProperties": false,
"description": "What the organization's own records say, read in one query."
},
"areas": {
"type": "object",
"properties": {
"reviews": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"loyalty": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"wishlists": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"site": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
}
},
"required": ["reviews", "loyalty", "wishlists", "site"],
"additionalProperties": false,
"description": "Where each area stands, on its own. Areas are never judged together."
},
"publishableKey": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The organization's publishable key, so a setup screen can show the storefront snippet already filled in. Null when none has been created yet."
},
"embedScriptUrl": {
"type": "string",
"description": "Where the storefront widget bundle is published."
},
"apiBaseUrl": {
"type": "string",
"description": "The API origin the widgets should call."
}
},
"required": ["signals", "areas", "publishableKey", "embedScriptUrl", "apiBaseUrl"],
"additionalProperties": false,
"description": "Where setup stands afterward."
}POST /organization/setup/reviews
Set reviews up from a handful of answers. It creates the review request email (and the reminder, if you asked for one), creates the survey flow that sends them on the order event you chose, and writes your auto-publish preference. Everything it writes is an ordinary record afterward, so the flow and the templates are editable on their own screens.
Requires the settings:write permission.
Body Parameters
dataobjectrequiredThe answers to turn into a flow and its emails.
curl \
-X POST \
"https://api.cascade.dev/organization/setup/reviews" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"triggerEvent": "orderCreated",
"delayDays": 7,
"reminder": true,
"reminderDelayDays": 5,
"template": "featuredProduct",
"autoPublish": true,
"autoPublishMinRating": 1
}
}'import { client } from "@cascade-commerce/api/admin/client"
import { postOrganizationSetupReviews } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postOrganizationSetupReviews({
body: {
data: {
triggerEvent: "orderCreated",
delayDays: 7,
reminder: true,
reminderDelayDays: 5,
template: "featuredProduct",
autoPublish: true,
autoPublishMinRating: 1,
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/organization/setup/reviews")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": {
"triggerEvent": "orderCreated",
"delayDays": 7,
"reminder": true,
"reminderDelayDays": 5,
"template": "featuredProduct",
"autoPublish": true,
"autoPublishMinRating": 1
}
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"data": {
"triggerEvent": "orderCreated",
"delayDays": 7,
"reminder": true,
"reminderDelayDays": 5,
"template": "featuredProduct",
"autoPublish": true,
"autoPublishMinRating": 1
}
}"""
req = Request(
"https://api.cascade.dev/organization/setup/reviews",
data=body.encode(),
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"data": {
"signals": {
"hasSite": true,
"hasPublishableKey": true,
"hasIntegration": true,
"hasProducts": true,
"hasActiveSurveyFlow": true,
"hasReview": true,
"hasLoyaltyProgram": true,
"loyaltyProgramOnSite": true,
"hasWishlist": true
},
"areas": {
"reviews": {
"completedAt": "...",
"dismissedAt": "...",
"configured": "...",
"needsSetup": "..."
},
"loyalty": {
"completedAt": "...",
"dismissedAt": "...",
"configured": "...",
"needsSetup": "..."
},
"wishlists": {
"completedAt": "...",
"dismissedAt": "...",
"configured": "...",
"needsSetup": "..."
},
"site": {
"completedAt": "...",
"dismissedAt": "...",
"configured": "...",
"needsSetup": "..."
}
},
"publishableKey": "string",
"embedScriptUrl": "string",
"apiBaseUrl": "string"
},
"surveyFlow": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"isActive": true,
"triggerEvent": "orderCreated",
"condition": "string",
"steps": [
{
"id": "...",
"type": "...",
"position": "...",
"emailTemplateId": "...",
"url": "...",
"waitSeconds": "..."
}
],
"metadata": "...",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"emailTemplates": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"body": "string",
"type": "reviewSurvey",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}
]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"signals": {
"type": "object",
"properties": {
"hasSite": {
"type": "boolean",
"description": "Whether the organization has at least one site."
},
"hasPublishableKey": {
"type": "boolean",
"description": "Whether a publishable key exists for the storefront widgets to authenticate with."
},
"hasIntegration": {
"type": "boolean",
"description": "Whether a store is connected, which today means a Shopify store."
},
"hasProducts": {
"type": "boolean",
"description": "Whether any products have arrived, from a connected store or an import. Without them a review request has nothing to ask about and a loyalty rule has nothing to measure."
},
"hasActiveSurveyFlow": {
"type": "boolean",
"description": "Whether a survey flow is switched on and will send review requests."
},
"hasReview": {
"type": "boolean",
"description": "Whether any review has arrived, however it got here."
},
"hasLoyaltyProgram": {
"type": "boolean",
"description": "Whether a loyalty program exists, draft or live."
},
"loyaltyProgramOnSite": {
"type": "boolean",
"description": "Whether a loyalty program is attached to a site, which is what puts it on a storefront."
},
"hasWishlist": {
"type": "boolean",
"description": "Whether any shopper has saved a wishlist."
}
},
"required": [
"hasSite",
"hasPublishableKey",
"hasIntegration",
"hasProducts",
"hasActiveSurveyFlow",
"hasReview",
"hasLoyaltyProgram",
"loyaltyProgramOnSite",
"hasWishlist"
],
"additionalProperties": false,
"description": "What the organization's own records say, read in one query."
},
"areas": {
"type": "object",
"properties": {
"reviews": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"loyalty": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"wishlists": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"site": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
}
},
"required": ["reviews", "loyalty", "wishlists", "site"],
"additionalProperties": false,
"description": "Where each area stands, on its own. Areas are never judged together."
},
"publishableKey": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The organization's publishable key, so a setup screen can show the storefront snippet already filled in. Null when none has been created yet."
},
"embedScriptUrl": {
"type": "string",
"description": "Where the storefront widget bundle is published."
},
"apiBaseUrl": {
"type": "string",
"description": "The API origin the widgets should call."
}
},
"required": ["signals", "areas", "publishableKey", "embedScriptUrl", "apiBaseUrl"],
"additionalProperties": false,
"description": "Where setup stands afterward."
},
"surveyFlow": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the survey flow."
},
"name": {
"type": "string",
"description": "The flow's display name."
},
"isActive": {
"type": "boolean",
"description": "Whether the flow runs. An inactive flow ignores its trigger event."
},
"triggerEvent": {
"type": "string",
"enum": ["orderCreated", "orderShipped", "orderDelivered"],
"description": "The order event that starts the flow."
},
"condition": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "A JSON filter document the order has to match for the flow to run, or null to run for every order. Can match on `order`, `customer`, `site`, `products` and `variants`."
},
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the step."
},
"type": {
"type": "string",
"enum": ["sendEmail", "sendWebhook"],
"description": "What the step does when it runs."
},
"position": {
"type": "number",
"description": "The step's zero-based place in the flow. Steps run in this order."
},
"emailTemplateId": {
"description": "For `sendEmail` steps, the ID of the template to send.",
"type": "string"
},
"url": {
"description": "For `sendWebhook` steps, the URL to POST to.",
"type": "string"
},
"waitSeconds": {
"type": "number",
"description": "How long to wait after the previous step before running this one, in seconds."
}
},
"required": ["id", "type", "position", "waitSeconds"],
"additionalProperties": false
},
"description": "The flow's steps, in the order they run."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the flow was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the flow was last changed."
}
},
"required": [
"id",
"name",
"isActive",
"triggerEvent",
"condition",
"steps",
"metadata",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The survey flow that was created."
},
"emailTemplates": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the email template."
},
"name": {
"type": "string",
"description": "The template's display name."
},
"body": {
"type": "string",
"description": "The email body, with template variables left in place."
},
"type": {
"type": "string",
"enum": [
"reviewSurvey",
"loyaltyWelcome",
"loyaltyPointsEarned",
"loyaltyRewardsAvailable",
"loyaltyRedeemed",
"loyaltyRewardExpiring",
"loyaltyPointsExpiring",
"wishlistPriceDrop",
"wishlistBackInStock",
"productQuestionAnswered"
],
"description": "Which email the template is used for. It decides the variables the body can use."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the template was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the template was last changed."
}
},
"required": ["id", "name", "body", "type", "createdAt", "updatedAt"],
"additionalProperties": false
},
"description": "The templates that were created, in the order the flow sends them."
}
},
"required": ["data", "surveyFlow", "emailTemplates"],
"additionalProperties": false
}POST /organization/setup/loyalty
Set a loyalty program up from a handful of answers. Every answer writes a working example rather than only turning a section on: points bring an earning rule, a welcome bonus and one redemption, offers bring an offer, stamp cards bring a card, and referrals bring the pair of rules that reward both sides. The program is attached to every site you have, and is created as a draft, so publish it when you are ready for shoppers to see it. Members are enrolled the first time they do something the program rewards.
Requires the settings:write permission.
Body Parameters
dataobjectrequiredThe answers to turn into a program and its records.
curl \
-X POST \
"https://api.cascade.dev/organization/setup/loyalty" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"points": true,
"pointsPerDollar": 1,
"offers": true,
"stampCards": true,
"referrals": true
}
}'import { client } from "@cascade-commerce/api/admin/client"
import { postOrganizationSetupLoyalty } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postOrganizationSetupLoyalty({
body: {
data: {
points: true,
pointsPerDollar: 1,
offers: true,
stampCards: true,
referrals: true,
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/organization/setup/loyalty")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": {
"points": true,
"pointsPerDollar": 1,
"offers": true,
"stampCards": true,
"referrals": true
}
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"data": {
"points": true,
"pointsPerDollar": 1,
"offers": true,
"stampCards": true,
"referrals": true
}
}"""
req = Request(
"https://api.cascade.dev/organization/setup/loyalty",
data=body.encode(),
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"data": {
"signals": {
"hasSite": true,
"hasPublishableKey": true,
"hasIntegration": true,
"hasProducts": true,
"hasActiveSurveyFlow": true,
"hasReview": true,
"hasLoyaltyProgram": true,
"loyaltyProgramOnSite": true,
"hasWishlist": true
},
"areas": {
"reviews": {
"completedAt": "...",
"dismissedAt": "...",
"configured": "...",
"needsSetup": "..."
},
"loyalty": {
"completedAt": "...",
"dismissedAt": "...",
"configured": "...",
"needsSetup": "..."
},
"wishlists": {
"completedAt": "...",
"dismissedAt": "...",
"configured": "...",
"needsSetup": "..."
},
"site": {
"completedAt": "...",
"dismissedAt": "...",
"configured": "...",
"needsSetup": "..."
}
},
"publishableKey": "string",
"embedScriptUrl": "string",
"apiBaseUrl": "string"
},
"program": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"autoMembership": "off",
"settings": {
"loyaltyEmails": {
"welcome": {
"enabled": true,
"emailTemplateId": null
},
"pointsEarned": {
"enabled": false,
"emailTemplateId": null
},
"rewardsAvailable": {
"enabled": true,
"emailTemplateId": null
},
"redeemed": {
"enabled": true,
"emailTemplateId": null
},
"rewardExpiring": {
"enabled": true,
"emailTemplateId": null
},
"pointsExpiring": {
"enabled": true,
"emailTemplateId": null
}
},
"previousPointsNames": null
},
"sectionsEnabled": {
"rules": true,
"offers": true,
"stampCards": true
},
"pointsNames": {
"singular": "string",
"plural": "string"
},
"pointsExpiresAfterDays": 0,
"pointsBalancesExpireAt": "2025-01-15T09:30:00Z",
"rewardsExpireAfterDays": 0,
"expiringSoonDays": 0,
"pausedAt": "2025-01-15T09:30:00Z",
"publishedAt": "2025-01-15T09:30:00Z",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"rules": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
"section": "rules",
"name": "string",
"trigger": {
"type": "...",
"source": "..."
},
"reward": {
"type": "...",
"amount": "..."
},
"maxRedemptionsPerCustomer": 0,
"maxAvailableCount": 0,
"availableCount": 0,
"availableFrom": "2025-01-15T09:30:00Z",
"availableTo": "2025-01-15T09:30:00Z",
"loyaltyTierIds": ["..."],
"metadata": "...",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}
],
"stampCardTypes": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"earningType": {
"type": "...",
"stamps": "..."
},
"overflowPolicy": "carry",
"allowMultipleOpenCards": true,
"availableFrom": "2025-01-15T09:30:00Z",
"availableTo": "2025-01-15T09:30:00Z",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}
],
"sites": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"siteId": "550e8400-e29b-41d4-a716-446655440000",
"siteName": "string",
"siteSlug": "string",
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
"programUrl": "string",
"createdAt": "2025-01-15T09:30:00Z"
}
]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"signals": {
"type": "object",
"properties": {
"hasSite": {
"type": "boolean",
"description": "Whether the organization has at least one site."
},
"hasPublishableKey": {
"type": "boolean",
"description": "Whether a publishable key exists for the storefront widgets to authenticate with."
},
"hasIntegration": {
"type": "boolean",
"description": "Whether a store is connected, which today means a Shopify store."
},
"hasProducts": {
"type": "boolean",
"description": "Whether any products have arrived, from a connected store or an import. Without them a review request has nothing to ask about and a loyalty rule has nothing to measure."
},
"hasActiveSurveyFlow": {
"type": "boolean",
"description": "Whether a survey flow is switched on and will send review requests."
},
"hasReview": {
"type": "boolean",
"description": "Whether any review has arrived, however it got here."
},
"hasLoyaltyProgram": {
"type": "boolean",
"description": "Whether a loyalty program exists, draft or live."
},
"loyaltyProgramOnSite": {
"type": "boolean",
"description": "Whether a loyalty program is attached to a site, which is what puts it on a storefront."
},
"hasWishlist": {
"type": "boolean",
"description": "Whether any shopper has saved a wishlist."
}
},
"required": [
"hasSite",
"hasPublishableKey",
"hasIntegration",
"hasProducts",
"hasActiveSurveyFlow",
"hasReview",
"hasLoyaltyProgram",
"loyaltyProgramOnSite",
"hasWishlist"
],
"additionalProperties": false,
"description": "What the organization's own records say, read in one query."
},
"areas": {
"type": "object",
"properties": {
"reviews": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"loyalty": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"wishlists": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"site": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
}
},
"required": ["reviews", "loyalty", "wishlists", "site"],
"additionalProperties": false,
"description": "Where each area stands, on its own. Areas are never judged together."
},
"publishableKey": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The organization's publishable key, so a setup screen can show the storefront snippet already filled in. Null when none has been created yet."
},
"embedScriptUrl": {
"type": "string",
"description": "Where the storefront widget bundle is published."
},
"apiBaseUrl": {
"type": "string",
"description": "The API origin the widgets should call."
}
},
"required": ["signals", "areas", "publishableKey", "embedScriptUrl", "apiBaseUrl"],
"additionalProperties": false,
"description": "Where setup stands afterward."
},
"program": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the loyalty program."
},
"name": {
"type": "string",
"description": "The program's display name, shown to members."
},
"autoMembership": {
"type": "string",
"enum": ["off", "onFirstActivity"],
"description": "When customers are enrolled in the program automatically. `off` means they join only when enrolled explicitly; `onFirstActivity` enrolls them the first time they do something the program rewards."
},
"settings": {
"type": "object",
"properties": {
"loyaltyEmails": {
"default": {
"welcome": {
"enabled": true,
"emailTemplateId": null
},
"pointsEarned": {
"enabled": false,
"emailTemplateId": null
},
"rewardsAvailable": {
"enabled": true,
"emailTemplateId": null
},
"redeemed": {
"enabled": true,
"emailTemplateId": null
},
"rewardExpiring": {
"enabled": true,
"emailTemplateId": null
},
"pointsExpiring": {
"enabled": true,
"emailTemplateId": null
}
},
"description": "Which member emails this program sends, and the template each one renders.",
"type": "object",
"properties": {
"welcome": {
"default": {
"enabled": true,
"emailTemplateId": null
},
"description": "The email a customer gets when they join the program.",
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this email goes out at all."
},
"emailTemplateId": {
"anyOf": [
{
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
{
"type": "null"
}
],
"description": "The email template to render, which has to be one of this email's type. Null uses the organization's oldest template of that type, or the packaged default when there is none."
}
},
"required": ["enabled", "emailTemplateId"],
"additionalProperties": false
},
"pointsEarned": {
"default": {
"enabled": false,
"emailTemplateId": null
},
"description": "The email a customer gets when they earn points. One email covers everything a single order or visit earned, however many rules paid out. Off by default: it is the noisiest of these emails.",
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this email goes out at all."
},
"emailTemplateId": {
"anyOf": [
{
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
{
"type": "null"
}
],
"description": "The email template to render, which has to be one of this email's type. Null uses the organization's oldest template of that type, or the packaged default when there is none."
}
},
"required": ["enabled", "emailTemplateId"],
"additionalProperties": false
},
"rewardsAvailable": {
"default": {
"enabled": true,
"emailTemplateId": null
},
"description": "The email a customer gets when a reward is issued to them.",
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this email goes out at all."
},
"emailTemplateId": {
"anyOf": [
{
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
{
"type": "null"
}
],
"description": "The email template to render, which has to be one of this email's type. Null uses the organization's oldest template of that type, or the packaged default when there is none."
}
},
"required": ["enabled", "emailTemplateId"],
"additionalProperties": false
},
"redeemed": {
"default": {
"enabled": true,
"emailTemplateId": null
},
"description": "The email carrying a customer's reward code when they spend points on a reward.",
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this email goes out at all."
},
"emailTemplateId": {
"anyOf": [
{
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
{
"type": "null"
}
],
"description": "The email template to render, which has to be one of this email's type. Null uses the organization's oldest template of that type, or the packaged default when there is none."
}
},
"required": ["enabled", "emailTemplateId"],
"additionalProperties": false
},
"rewardExpiring": {
"default": {
"enabled": true,
"emailTemplateId": null
},
"description": "The warning a customer gets before a reward they hold expires. It only ever sends when the program sets a warning window.",
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this email goes out at all."
},
"emailTemplateId": {
"anyOf": [
{
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
{
"type": "null"
}
],
"description": "The email template to render, which has to be one of this email's type. Null uses the organization's oldest template of that type, or the packaged default when there is none."
}
},
"required": ["enabled", "emailTemplateId"],
"additionalProperties": false
},
"pointsExpiring": {
"default": {
"enabled": true,
"emailTemplateId": null
},
"description": "The warning a customer gets before their points expire. It only ever sends when the program sets a warning window.",
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this email goes out at all."
},
"emailTemplateId": {
"anyOf": [
{
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
{
"type": "null"
}
],
"description": "The email template to render, which has to be one of this email's type. Null uses the organization's oldest template of that type, or the packaged default when there is none."
}
},
"required": ["enabled", "emailTemplateId"],
"additionalProperties": false
}
},
"required": [
"welcome",
"pointsEarned",
"rewardsAvailable",
"redeemed",
"rewardExpiring",
"pointsExpiring"
],
"additionalProperties": false
},
"previousPointsNames": {
"default": null,
"description": "The names the points had before they were switched off, kept so switching them back on restores them. Null when points have never been switched off.",
"anyOf": [
{
"type": "object",
"properties": {
"singular": {
"type": "string",
"minLength": 1,
"description": "The name of one point, such as `point`."
},
"plural": {
"type": "string",
"minLength": 1,
"description": "The name of several points, such as `points`."
}
},
"required": ["singular", "plural"],
"additionalProperties": false,
"description": "Unit names a balance is composed from, for example `12 points`."
},
{
"type": "null"
}
]
}
},
"required": ["loyaltyEmails", "previousPointsNames"],
"additionalProperties": false,
"description": "Program-level configuration: which member emails go out and with which template, and the points names kept from before points were switched off."
},
"sectionsEnabled": {
"type": "object",
"properties": {
"rules": {
"type": "boolean",
"description": "Whether the ways-to-earn section is shown to members."
},
"offers": {
"type": "boolean",
"description": "Whether the offers section is shown to members."
},
"stampCards": {
"type": "boolean",
"description": "Whether the stamp cards section is shown to members."
}
},
"required": ["rules", "offers", "stampCards"],
"additionalProperties": false,
"description": "Which optional sections members see in the loyalty widget."
},
"pointsNames": {
"anyOf": [
{
"type": "object",
"properties": {
"singular": {
"type": "string",
"minLength": 1,
"description": "The name of one point, such as `point`."
},
"plural": {
"type": "string",
"minLength": 1,
"description": "The name of several points, such as `points`."
}
},
"required": ["singular", "plural"],
"additionalProperties": false,
"description": "Unit names a balance is composed from, for example `12 points`."
},
{
"type": "null"
}
],
"description": "What the program calls its points, singular and plural. Null means the program has no points, so nothing earns, spends or displays them."
},
"pointsExpiresAfterDays": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Each earn expires this many days after it happened. Null means earns never age out. When `pointsBalancesExpireAt` is also set, the earlier of the two applies."
},
"pointsBalancesExpireAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "Every points balance in the program expires at this moment, the event-tickets case. Null means balances have no fixed end date. When `pointsExpiresAfterDays` is also set, the earlier of the two applies."
},
"rewardsExpireAfterDays": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Rewards issued in this program lapse this many days after a member gets them. Null means they stay redeemable until they are used. A rule that sets its own expiry wins over this."
},
"expiringSoonDays": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many days of warning members get before a reward or a points balance lapses. Null means no warning goes out."
},
"pausedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the program was paused. While it is set the program earns nothing, and members keep their balances, rewards and history."
},
"publishedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the program was published. Null means it is still a draft: no shopper can see it, join it or earn in it, and only a preview link reaches it. Publishing cannot be undone, so pause a program you want to take down."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the program was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the program was last changed."
}
},
"required": [
"id",
"name",
"autoMembership",
"settings",
"sectionsEnabled",
"pointsNames",
"pointsExpiresAfterDays",
"pointsBalancesExpireAt",
"rewardsExpireAfterDays",
"expiringSoonDays",
"pausedAt",
"publishedAt",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The program that was created, still a draft."
},
"rules": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the rule."
},
"loyaltyProgramId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The loyalty program the rule belongs to."
},
"section": {
"type": "string",
"enum": ["rules", "rewards", "offers", "stampCards"],
"description": "Which section of the loyalty widget the rule is presented in."
},
"name": {
"type": "string",
"description": "The rule's display name, shown to members."
},
"trigger": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "joinProgram",
"description": "Fires when a customer joins the loyalty program."
},
"source": {
"type": "string",
"enum": ["direct", "referral", "all"],
"description": "Which sign-ups count: direct joins, referred joins, or both."
}
},
"required": ["type", "source"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "visit",
"description": "Fires when a customer visits the store."
},
"eligibilityPeriod": {
"type": "string",
"enum": ["day", "week", "month"],
"description": "The window the visit allowance resets on."
},
"timesEligiblePerPeriod": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "How many times per period a visit can earn the reward."
}
},
"required": ["type", "eligibilityPeriod", "timesEligiblePerPeriod"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "followSocial",
"description": "Fires when a customer follows the store on social media."
},
"platform": {
"type": "string",
"enum": ["facebook", "x", "instagram", "tiktok"],
"description": "The social platform the customer has to follow on."
}
},
"required": ["type", "platform"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "newsletterSignup",
"description": "Fires when a customer subscribes to the newsletter."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "pointsSpent",
"description": "Fires when a customer redeems points."
},
"unitAmount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many points make up one unit of the reward."
},
"variableAmount": {
"type": "boolean",
"description": "Whether the customer chooses how many points to spend."
}
},
"required": ["type", "unitAmount", "variableAmount"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "stampsEarned",
"description": "Fires when a customer fills a stamp card."
},
"amount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many stamps are needed to fire the rule."
},
"stampCardTypeId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The stamp card type the stamps have to come from."
}
},
"required": ["type", "amount", "stampCardTypeId"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "purchase",
"description": "Fires when a customer places an order."
},
"variableAmount": {
"type": "boolean",
"description": "Whether the reward scales with the amount spent rather than being flat."
},
"basis": {
"type": "string",
"enum": ["subtotal", "total"],
"description": "The order amount earning is computed from. `subtotal` is the discounted subtotal excluding shipping and tax, falling back to the total when an order does not carry one; `total` is the order total. Omitted means `subtotal`."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the rule to matching products. Null means any product qualifies."
}
},
"required": ["type", "variableAmount", "productFilter"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "birthday",
"description": "Fires on the customer's birthday."
},
"awardOn": {
"description": "When the award lands: `day` on the birthday itself, `monthStart` on the first day of the birthday month. Omitted means `day`.",
"type": "string",
"enum": ["day", "monthStart"]
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "referAFriend",
"description": "Fires when a customer's referral converts."
},
"referralCount": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many referrals are needed. Null means every referral counts."
},
"minOrderAmountCents": {
"description": "Minimum size of the completing order, in cents, before the rule fires. Measured against the order's discounted subtotal, falling back to its total. Omitted means any paid order qualifies.",
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"completionWindowDays": {
"description": "How many days after the referral was recorded a completion still fires the rule. Omitted means it never expires.",
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"required": ["type", "referralCount"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "webhook",
"description": "Fires when your own system posts an event to `POST /loyalty-events`."
},
"token": {
"type": "string",
"description": "The routing key that names this rule on `POST /loyalty-events`. Treat it as a secret: anyone holding it and an API key can fire the rule. Leave it empty on a write and one is generated for you."
},
"variableAmount": {
"description": "Whether the reward scales with the `value` the event carries rather than being flat. Omitted means flat. Only points, fixed-amount coupons and gift cards can scale.",
"type": "boolean"
},
"defaultValue": {
"description": "The value used when an event leaves `value` out. Omitted means one, so the rule awards its reward once.",
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"required": ["type", "token"],
"additionalProperties": false
}
],
"description": "What has to happen for the rule to award its reward."
},
"reward": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "points",
"description": "Credits the customer with loyalty points."
},
"amount": {
"type": "number",
"description": "How many points to credit."
}
},
"required": ["type", "amount"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "amountCoupon",
"description": "Issues a fixed-value discount coupon."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Face value of the coupon, in cents."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "productFilter", "minimumOrderTotalCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "percentageCoupon",
"description": "Issues a percentage-off discount coupon."
},
"percentageDiscount": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Percentage taken off the qualifying items, from 1 to 100."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"percentageDiscount",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "freeShipping",
"description": "Waives shipping on the customer's next order."
},
"maximumValueCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Cap on the shipping cost covered, in cents. Null means uncapped."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "maximumValueCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "giftCard",
"description": "Issues a gift card."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Value loaded onto the gift card, in cents."
},
"expirySeconds": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How long the gift card's balance stays spendable, in seconds. Null means the balance never expires."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "expirySeconds"],
"additionalProperties": false
}
],
"description": "What the customer gets when the rule fires."
},
"maxRedemptionsPerCustomer": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many times one customer may earn this reward. Null means unlimited."
},
"maxAvailableCount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many times the reward can be earned in total, across everyone. Null means unlimited."
},
"availableCount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many are left out of `maxAvailableCount`. Null when there is no cap."
},
"availableFrom": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the rule starts firing. Null means straight away."
},
"availableTo": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the rule stops firing. Null means it stays open."
},
"loyaltyTierIds": {
"anyOf": [
{
"type": "array",
"items": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
{
"type": "null"
}
],
"description": "Tiers the rule is restricted to. Null or empty means members of every tier (including none) qualify."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the rule was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the rule was last changed."
}
},
"required": [
"id",
"loyaltyProgramId",
"section",
"name",
"trigger",
"reward",
"maxRedemptionsPerCustomer",
"maxAvailableCount",
"availableCount",
"availableFrom",
"availableTo",
"loyaltyTierIds",
"metadata",
"createdAt",
"updatedAt"
],
"additionalProperties": false
},
"description": "The rules that were created."
},
"stampCardTypes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the stamp card type."
},
"loyaltyProgramId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The loyalty program the card type belongs to."
},
"name": {
"type": "string",
"description": "The card type's display name, shown to members."
},
"earningType": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "stampsPerOrder",
"description": "Awards a fixed number of stamps per order."
},
"stamps": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Stamps awarded for each qualifying order."
}
},
"required": ["type", "stamps"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "stampsPerSpend",
"description": "Awards stamps in proportion to the amount spent."
},
"stamps": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Stamps awarded per unit spent."
},
"unitAmountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "How much a customer has to spend, in cents, to earn one batch of stamps."
},
"basis": {
"type": "string",
"enum": ["subtotal", "total"],
"description": "The order amount earning is computed from. `subtotal` is the discounted subtotal excluding shipping and tax, falling back to the total when an order does not carry one; `total` is the order total. Omitted means `subtotal`."
}
},
"required": ["type", "stamps", "unitAmountCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "custom",
"description": "Stamps are awarded by your own code through `POST /loyalty-stamps` rather than automatically."
}
},
"required": ["type"],
"additionalProperties": false
}
],
"description": "How customers earn stamps on this card."
},
"overflowPolicy": {
"type": "string",
"enum": ["carry", "discard"],
"description": "What happens to stamps earned past the card's completion threshold. `carry` starts the customer's next card with them; `discard` drops them."
},
"allowMultipleOpenCards": {
"type": "boolean",
"description": "Whether a member may hold more than one open card of this type at a time."
},
"availableFrom": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When customers can start collecting on this card. Null means straight away."
},
"availableTo": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When collecting on this card stops. Null means it stays open."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the card type was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the card type was last changed."
}
},
"required": [
"id",
"loyaltyProgramId",
"name",
"earningType",
"overflowPolicy",
"allowMultipleOpenCards",
"availableFrom",
"availableTo",
"createdAt",
"updatedAt"
],
"additionalProperties": false
},
"description": "The stamp cards that were created. Empty unless you asked for stamp cards."
},
"sites": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the link between the site and the program."
},
"siteId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The site the program is attached to."
},
"siteName": {
"type": "string",
"description": "The site's display name."
},
"siteSlug": {
"type": "string",
"description": "The site's slug, which the widget identifies it by."
},
"loyaltyProgramId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The loyalty program the site is attached to."
},
"programUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The page on the site that shows the loyalty program, used by the links in the member emails. Null means the site's base URL, where the launcher embed is expected to be."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the program was attached to the site."
}
},
"required": [
"id",
"siteId",
"siteName",
"siteSlug",
"loyaltyProgramId",
"programUrl",
"createdAt"
],
"additionalProperties": false
},
"description": "The sites the program was attached to."
}
},
"required": ["data", "program", "rules", "stampCardTypes", "sites"],
"additionalProperties": false
}POST /organization/setup/wishlists
Set wishlists up from a handful of answers. Wishlists have no records to create, so this writes the three settings that decide how they behave: whether a customer keeps one list or several, whether lists can be shared, and whether a shared list shows prices.
Requires the settings:write permission.
Body Parameters
dataobjectrequiredThe answers to write to the organization's settings.
curl \
-X POST \
"https://api.cascade.dev/organization/setup/wishlists" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"multipleWishlists": true,
"sharing": true,
"showPrices": true
}
}'import { client } from "@cascade-commerce/api/admin/client"
import { postOrganizationSetupWishlists } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postOrganizationSetupWishlists({
body: {
data: {
multipleWishlists: true,
sharing: true,
showPrices: true,
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/organization/setup/wishlists")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": {
"multipleWishlists": true,
"sharing": true,
"showPrices": true
}
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"data": {
"multipleWishlists": true,
"sharing": true,
"showPrices": true
}
}"""
req = Request(
"https://api.cascade.dev/organization/setup/wishlists",
data=body.encode(),
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"signals": {
"hasSite": true,
"hasPublishableKey": true,
"hasIntegration": true,
"hasProducts": true,
"hasActiveSurveyFlow": true,
"hasReview": true,
"hasLoyaltyProgram": true,
"loyaltyProgramOnSite": true,
"hasWishlist": true
},
"areas": {
"reviews": {
"completedAt": "2025-01-15T09:30:00Z",
"dismissedAt": "2025-01-15T09:30:00Z",
"configured": true,
"needsSetup": true
},
"loyalty": {
"completedAt": "2025-01-15T09:30:00Z",
"dismissedAt": "2025-01-15T09:30:00Z",
"configured": true,
"needsSetup": true
},
"wishlists": {
"completedAt": "2025-01-15T09:30:00Z",
"dismissedAt": "2025-01-15T09:30:00Z",
"configured": true,
"needsSetup": true
},
"site": {
"completedAt": "2025-01-15T09:30:00Z",
"dismissedAt": "2025-01-15T09:30:00Z",
"configured": true,
"needsSetup": true
}
},
"publishableKey": "string",
"embedScriptUrl": "string",
"apiBaseUrl": "string"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"signals": {
"type": "object",
"properties": {
"hasSite": {
"type": "boolean",
"description": "Whether the organization has at least one site."
},
"hasPublishableKey": {
"type": "boolean",
"description": "Whether a publishable key exists for the storefront widgets to authenticate with."
},
"hasIntegration": {
"type": "boolean",
"description": "Whether a store is connected, which today means a Shopify store."
},
"hasProducts": {
"type": "boolean",
"description": "Whether any products have arrived, from a connected store or an import. Without them a review request has nothing to ask about and a loyalty rule has nothing to measure."
},
"hasActiveSurveyFlow": {
"type": "boolean",
"description": "Whether a survey flow is switched on and will send review requests."
},
"hasReview": {
"type": "boolean",
"description": "Whether any review has arrived, however it got here."
},
"hasLoyaltyProgram": {
"type": "boolean",
"description": "Whether a loyalty program exists, draft or live."
},
"loyaltyProgramOnSite": {
"type": "boolean",
"description": "Whether a loyalty program is attached to a site, which is what puts it on a storefront."
},
"hasWishlist": {
"type": "boolean",
"description": "Whether any shopper has saved a wishlist."
}
},
"required": [
"hasSite",
"hasPublishableKey",
"hasIntegration",
"hasProducts",
"hasActiveSurveyFlow",
"hasReview",
"hasLoyaltyProgram",
"loyaltyProgramOnSite",
"hasWishlist"
],
"additionalProperties": false,
"description": "What the organization's own records say, read in one query."
},
"areas": {
"type": "object",
"properties": {
"reviews": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"loyalty": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"wishlists": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
},
"site": {
"type": "object",
"properties": {
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area's setup was finished. Null means it never has been. It is never cleared, so an area that has been set up stays set up."
},
"dismissedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the area was skipped. Null means it never was. A store that deliberately does not run loyalty is a normal steady state, not an unfinished setup."
},
"configured": {
"type": "boolean",
"description": "Whether the area's records say it is already running, ignoring what was decided."
},
"needsSetup": {
"type": "boolean",
"description": "Whether the area has never been set up, skipped or configured. It is what the navigation collapses on, and it goes false for good the first time any of the three is true."
}
},
"required": ["completedAt", "dismissedAt", "configured", "needsSetup"],
"additionalProperties": false,
"description": "Where one area stands."
}
},
"required": ["reviews", "loyalty", "wishlists", "site"],
"additionalProperties": false,
"description": "Where each area stands, on its own. Areas are never judged together."
},
"publishableKey": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The organization's publishable key, so a setup screen can show the storefront snippet already filled in. Null when none has been created yet."
},
"embedScriptUrl": {
"type": "string",
"description": "Where the storefront widget bundle is published."
},
"apiBaseUrl": {
"type": "string",
"description": "The API origin the widgets should call."
}
},
"required": ["signals", "areas", "publishableKey", "embedScriptUrl", "apiBaseUrl"],
"additionalProperties": false,
"description": "Where setup stands afterward."
}GET /organization
Get the organization the request is authenticated against, including its plan, its resolved limits and its settings. The limits already have the plan's defaults resolved against anything set on the organization itself.
Requires the settings:read permission.
curl \
"https://api.cascade.dev/organization" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { getOrganization } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getOrganization()require "net/http"
uri = URI("https://api.cascade.dev/organization")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/organization",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"slug": "string",
"logoUrl": "https://example.com",
"plan": "starter",
"limits": {
"sites": 0,
"products": 0,
"variantsPerProduct": 0,
"customers": 0,
"monthlySurveys": 0,
"monthlyLoyaltyMAU": 0,
"monthlyWishlistMAU": 0,
"apiKeys": 0,
"seats": 0,
"pendingInvitations": 0,
"webhooks": 0,
"loyaltyPrograms": 0,
"currencies": 0,
"rulesPerLoyaltyProgram": 0,
"tiersPerLoyaltyProgram": 0,
"rewardsPerLoyaltyProgram": 0,
"stampCardTypes": 0,
"surveyFlows": 0,
"stepsPerSurveyFlow": 0,
"savedCharts": 0,
"dashboards": 0,
"savedFilters": 0,
"quickFilters": 0,
"tagsPerResource": 0,
"wishlistsPerCustomer": 0,
"itemsPerWishlist": 0,
"brandAssetsMB": 0,
"totalStorageMB": 0,
"singleUploadMB": 0,
"assistantAttachmentBytes": 0,
"mediaPerReview": 0,
"monthlyReviewSubmissions": 0,
"webhookEventRetentionDays": 0,
"auditLogRetentionDays": 0,
"importJobRetentionDays": 0,
"importJobsPerDay": 0,
"exportJobsPerDay": 0,
"rowsPerImport": 0,
"rowsPerExport": 0,
"apiRequestsPerMinute": 0,
"mcpRequestsPerMinute": 0,
"loyaltyEventsPerMinute": 0,
"loyaltyStampsPerMinute": 0,
"widgetRequestsPerSession": 0,
"emailsPerCustomerPerDay": 0,
"aiSummaryMinReviews": 0,
"aiSummaryRegenerateReviews": 0,
"aiSummaryRegeneratePercent": 0,
"aiSummaryMinHoursBetween": 0,
"monthlyReviewAnswers": 0,
"aiReviewSummaries": true,
"aiReviewAnswers": true,
"aiReviewScreening": true,
"sso": true
},
"settings": {
"cascadeDeleteReviews": false,
"autoPublishReviews": false,
"autoPublishMinRating": 1,
"showWishlistPrices": true,
"allowSharedWishlists": true,
"allowWishlistPurchaseMarking": true,
"defaultWishlistName": "My wishlist",
"multipleWishlists": true,
"reviewNotifications": {
"perReview": false,
"perReviewMaxRating": 5,
"digest": false,
"slackWebhookUrl": null
},
"reviewScreening": {
"links": true,
"profanity": true,
"blocklist": [],
"ai": false
},
"reviewSummaries": {
"enabled": true
},
"reviewAnswers": {
"enabled": true
},
"productQuestions": {
"enabled": true,
"allowShopperAnswers": false,
"notifyAsker": true
},
"wishlistAlerts": {
"priceDrop": false,
"backInStock": false,
"priceDropThresholdPercent": 10
}
},
"timezone": "string",
"access": {
"state": "active",
"writable": true,
"message": "string",
"trialEndsAt": "2025-01-15T09:30:00Z",
"readOnlySince": "2025-01-15T09:30:00Z",
"exportsUntil": "2025-01-15T09:30:00Z"
},
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"role": "owner"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the organization."
},
"name": {
"type": "string",
"description": "The organization's display name."
},
"slug": {
"type": "string",
"description": "The organization's unique handle. Your storefront pages are served from it, so it cannot be changed."
},
"logoUrl": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "null"
}
],
"description": "Public URL of the organization's logo image."
},
"plan": {
"type": "string",
"enum": ["starter", "growth", "enterprise"],
"description": "Which set of default limits the organization is held to. It is not a billing record: it only selects the defaults, and any individual limit can still be overridden on the organization."
},
"limits": {
"type": "object",
"properties": {
"sites": {
"type": "number",
"description": "How many sites the organization may connect."
},
"products": {
"type": "number",
"description": "How many products the organization may store."
},
"variantsPerProduct": {
"type": "number",
"description": "How many variants a single product may have."
},
"customers": {
"type": "number",
"description": "How many customers the organization may store."
},
"monthlySurveys": {
"type": "number",
"description": "How many surveys may be sent per calendar month."
},
"monthlyLoyaltyMAU": {
"type": "number",
"description": "How many loyalty members may be active in a calendar month."
},
"monthlyWishlistMAU": {
"type": "number",
"description": "How many customers may be active on a wishlist in a calendar month."
},
"apiKeys": {
"type": "number",
"description": "How many API keys the organization may hold at once."
},
"seats": {
"type": "number",
"description": "How many people may be members of the organization."
},
"pendingInvitations": {
"type": "number",
"description": "How many invitations may be waiting to be accepted at once."
},
"webhooks": {
"type": "number",
"description": "How many webhook endpoints the organization may register."
},
"loyaltyPrograms": {
"type": "number",
"description": "How many loyalty programs the organization may run."
},
"currencies": {
"type": "number",
"description": "How many currencies the organization may trade in."
},
"rulesPerLoyaltyProgram": {
"type": "number",
"description": "How many earning rules a single loyalty program may have."
},
"tiersPerLoyaltyProgram": {
"type": "number",
"description": "How many tiers a single loyalty program may have."
},
"rewardsPerLoyaltyProgram": {
"type": "number",
"description": "How many reward definitions a single loyalty program may offer."
},
"stampCardTypes": {
"type": "number",
"description": "How many stamp card types the organization may define."
},
"surveyFlows": {
"type": "number",
"description": "How many survey flows the organization may define."
},
"stepsPerSurveyFlow": {
"type": "number",
"description": "How many steps a single survey flow may have."
},
"savedCharts": {
"type": "number",
"description": "How many saved charts the organization may keep."
},
"dashboards": {
"type": "number",
"description": "How many dashboards the organization may keep."
},
"savedFilters": {
"type": "number",
"description": "How many saved filters the organization may keep."
},
"quickFilters": {
"type": "number",
"description": "How many quick filters the organization may pin."
},
"tagsPerResource": {
"type": "number",
"description": "How many tags may be attached to a single customer, product, or order."
},
"wishlistsPerCustomer": {
"type": "number",
"description": "How many wishlists a single customer may have."
},
"itemsPerWishlist": {
"type": "number",
"description": "How many items a single wishlist may hold."
},
"brandAssetsMB": {
"type": "number",
"description": "How many megabytes of brand assets each site may hold."
},
"totalStorageMB": {
"type": "number",
"description": "How many megabytes of uploaded files the organization may hold in total."
},
"singleUploadMB": {
"type": "number",
"description": "How many megabytes a single uploaded file may be."
},
"assistantAttachmentBytes": {
"type": "number",
"description": "How many bytes of attachments a single message to the assistant may carry."
},
"mediaPerReview": {
"type": "number",
"description": "How many photos or videos a single review may carry."
},
"monthlyReviewSubmissions": {
"type": "number",
"description": "How many reviews may be submitted per calendar month."
},
"webhookEventRetentionDays": {
"type": "number",
"description": "How many days of webhook delivery history are kept before being purged."
},
"auditLogRetentionDays": {
"type": "number",
"description": "How many days of audit log entries are kept before being purged."
},
"importJobRetentionDays": {
"type": "number",
"description": "How many days an import job is kept, along with the source file and the links to the records it wrote, before being purged."
},
"importJobsPerDay": {
"type": "number",
"description": "How many import jobs may be started per day."
},
"exportJobsPerDay": {
"type": "number",
"description": "How many export jobs may be started per day."
},
"rowsPerImport": {
"type": "number",
"description": "How many rows a single import may carry."
},
"rowsPerExport": {
"type": "number",
"description": "How many rows a single export may produce."
},
"apiRequestsPerMinute": {
"type": "number",
"description": "How many admin API requests every secret key in the organization shares per minute."
},
"mcpRequestsPerMinute": {
"type": "number",
"description": "How many MCP requests every OAuth connection to the organization shares per minute."
},
"loyaltyEventsPerMinute": {
"type": "number",
"description": "How many events `POST /loyalty-events` accepts per minute."
},
"loyaltyStampsPerMinute": {
"type": "number",
"description": "How many stamp awards `POST /loyalty-stamps` accepts per minute."
},
"widgetRequestsPerSession": {
"type": "number",
"description": "How many widget API requests one shopper session may make."
},
"emailsPerCustomerPerDay": {
"type": "number",
"description": "How many emails a single customer may be sent in a day."
},
"aiSummaryMinReviews": {
"type": "number",
"description": "How many published reviews a product needs before a summary is generated or shown."
},
"aiSummaryRegenerateReviews": {
"type": "number",
"description": "How far the published review count has to move from the stored summary's count before it is regenerated."
},
"aiSummaryRegeneratePercent": {
"type": "number",
"description": "How far the published review count has to move, as a percentage of the stored summary's count, before it is regenerated. Either this or `aiSummaryRegenerateReviews` triggers it."
},
"aiSummaryMinHoursBetween": {
"type": "number",
"description": "The shortest gap between two generated summaries for one product. The default of 24 is the one regeneration per product per day ceiling."
},
"monthlyReviewAnswers": {
"type": "number",
"description": "How many review answers may be generated per calendar month. Repeated questions are answered from cache and do not count."
},
"aiReviewSummaries": {
"type": "boolean",
"description": "Whether the organization can generate AI summaries of a product's reviews."
},
"aiReviewAnswers": {
"type": "boolean",
"description": "Whether shoppers can ask a question on a product page and get an answer generated from its published reviews."
},
"aiReviewScreening": {
"type": "boolean",
"description": "Whether arriving reviews are screened by the AI moderation model."
},
"sso": {
"type": "boolean",
"description": "Whether the organization can sign in through its own SSO provider."
}
},
"required": [
"sites",
"products",
"variantsPerProduct",
"customers",
"monthlySurveys",
"monthlyLoyaltyMAU",
"monthlyWishlistMAU",
"apiKeys",
"seats",
"pendingInvitations",
"webhooks",
"loyaltyPrograms",
"currencies",
"rulesPerLoyaltyProgram",
"tiersPerLoyaltyProgram",
"rewardsPerLoyaltyProgram",
"stampCardTypes",
"surveyFlows",
"stepsPerSurveyFlow",
"savedCharts",
"dashboards",
"savedFilters",
"quickFilters",
"tagsPerResource",
"wishlistsPerCustomer",
"itemsPerWishlist",
"brandAssetsMB",
"totalStorageMB",
"singleUploadMB",
"assistantAttachmentBytes",
"mediaPerReview",
"monthlyReviewSubmissions",
"webhookEventRetentionDays",
"auditLogRetentionDays",
"importJobRetentionDays",
"importJobsPerDay",
"exportJobsPerDay",
"rowsPerImport",
"rowsPerExport",
"apiRequestsPerMinute",
"mcpRequestsPerMinute",
"loyaltyEventsPerMinute",
"loyaltyStampsPerMinute",
"widgetRequestsPerSession",
"emailsPerCustomerPerDay",
"aiSummaryMinReviews",
"aiSummaryRegenerateReviews",
"aiSummaryRegeneratePercent",
"aiSummaryMinHoursBetween",
"monthlyReviewAnswers",
"aiReviewSummaries",
"aiReviewAnswers",
"aiReviewScreening",
"sso"
],
"additionalProperties": false,
"description": "The limits this organization is held to, with the plan's defaults already resolved against anything set on the organization itself."
},
"settings": {
"type": "object",
"properties": {
"cascadeDeleteReviews": {
"default": false,
"description": "When true, deleting a product or customer also deletes their reviews. When false, the reviews are kept, unlinked and unpublished.",
"type": "boolean"
},
"autoPublishReviews": {
"default": false,
"description": "When true, a newly-submitted review is published on arrival instead of waiting for a manual publish, as long as it meets `autoPublishMinRating`.",
"type": "boolean"
},
"autoPublishMinRating": {
"default": 1,
"description": "The lowest star rating that auto-publishes. Anything below this is held back for a human even when `autoPublishReviews` is on.",
"type": "integer",
"minimum": 1,
"maximum": 5
},
"showWishlistPrices": {
"default": true,
"description": "When true, the public shared wishlist page shows each item's stored price. When false, no prices are shown.",
"type": "boolean"
},
"allowSharedWishlists": {
"default": true,
"description": "When true, customers can share wishlists with a public link. When false, every share link stops working and sharing cannot be turned on, but each wishlist's own sharing choice is kept for when this is switched back on.",
"type": "boolean"
},
"allowWishlistPurchaseMarking": {
"default": true,
"description": "When true, anyone viewing a shared wishlist can mark an item as purchased so two gift buyers don't buy the same thing. The list's owner is never shown a purchase somebody else made.",
"type": "boolean"
},
"defaultWishlistName": {
"default": "My wishlist",
"description": "The name given to the wishlist that is created automatically the first time a customer saves a product with one click.",
"type": "string",
"minLength": 1
},
"multipleWishlists": {
"default": true,
"description": "When true, a customer can keep several wishlists and name them. When false, everything they save goes on one list and the storefront hides the controls for creating another.",
"type": "boolean"
},
"reviewNotifications": {
"default": {
"perReview": false,
"perReviewMaxRating": 5,
"digest": false,
"slackWebhookUrl": null
},
"description": "Who hears about arriving reviews, and how.",
"type": "object",
"properties": {
"perReview": {
"default": false,
"description": "When true, every arriving review that meets `perReviewMaxRating` emails the organization's members.",
"type": "boolean"
},
"perReviewMaxRating": {
"default": 5,
"description": "The highest star rating that triggers a per-review alert. Set it to 5 to be told about every review, or lower to hear only about the poor ones.",
"type": "integer",
"minimum": 1,
"maximum": 5
},
"digest": {
"default": false,
"description": "When true, a daily summary of the last 24 hours of reviews is emailed to the organization's members. Days with no reviews send nothing.",
"type": "boolean"
},
"slackWebhookUrl": {
"default": null,
"description": "A Slack incoming webhook URL. When set, the alerts you have switched on are also posted to Slack. Null to post nowhere.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["perReview", "perReviewMaxRating", "digest", "slackWebhookUrl"],
"additionalProperties": false
},
"reviewScreening": {
"default": {
"links": true,
"profanity": true,
"blocklist": [],
"ai": false
},
"description": "What flags an arriving review for moderation.",
"type": "object",
"properties": {
"links": {
"default": true,
"description": "When true, a review containing a URL is flagged for moderation. Links in reviews are the strongest spam signal.",
"type": "boolean"
},
"profanity": {
"default": true,
"description": "When true, a review matching the built-in profanity wordlist is flagged for moderation. Flagged reviews are never rejected automatically.",
"type": "boolean"
},
"blocklist": {
"default": [],
"description": "Words and phrases of your own that flag a review for moderation, matched case-insensitively as whole words.",
"type": "array",
"items": {
"type": "string"
}
},
"ai": {
"default": false,
"description": "When true, arriving reviews are also read by a model, which flags anything a person should look at, and longer reviews are checked by an AI-writing detector. It runs after the review is submitted, so a review waits for it rather than publishing and being taken down again. Nothing is ever rejected automatically. Requires the AI review screening feature on your plan.",
"type": "boolean"
}
},
"required": ["links", "profanity", "blocklist", "ai"],
"additionalProperties": false
},
"reviewSummaries": {
"default": {
"enabled": true
},
"description": "Whether products get a generated summary of what their reviewers say.",
"type": "object",
"properties": {
"enabled": {
"default": true,
"description": "When true, products with enough published reviews get a generated summary of what reviewers say, shown above the reviews on your storefront. When false, no summary is generated and none is shown.",
"type": "boolean"
}
},
"required": ["enabled"],
"additionalProperties": false
},
"reviewAnswers": {
"default": {
"enabled": true
},
"description": "Whether shoppers can ask a question answered from a product's reviews.",
"type": "object",
"properties": {
"enabled": {
"default": true,
"description": "When true, shoppers can ask a question on a product page and get an answer generated from its published reviews, shown only to them and labeled as generated. When false, the ask box is hidden everywhere.",
"type": "boolean"
}
},
"required": ["enabled"],
"additionalProperties": false
},
"productQuestions": {
"default": {
"enabled": true,
"allowShopperAnswers": false,
"notifyAsker": true
},
"description": "Whether shoppers can ask a question your team answers and publishes.",
"type": "object",
"properties": {
"enabled": {
"default": true,
"description": "When true, shoppers can ask a question that reaches your moderation queue, and answered questions are published on the product page. When false, no question is recorded and the published Q&A is hidden.",
"type": "boolean"
},
"allowShopperAnswers": {
"default": false,
"description": "When true, shoppers can answer each other's published questions. Their answers wait for moderation like the questions do. Off by default: most stores want to be the only voice answering.",
"type": "boolean"
},
"notifyAsker": {
"default": true,
"description": "When true, a shopper who left their email when asking is emailed once, when the first answer to their question is published.",
"type": "boolean"
}
},
"required": ["enabled", "allowShopperAnswers", "notifyAsker"],
"additionalProperties": false
},
"wishlistAlerts": {
"default": {
"priceDrop": false,
"backInStock": false,
"priceDropThresholdPercent": 10
},
"description": "Whether saved items email their owner when they change.",
"type": "object",
"properties": {
"priceDrop": {
"default": false,
"description": "When true, a customer is emailed when something on one of their wishlists drops in price by at least `priceDropThresholdPercent`.",
"type": "boolean"
},
"backInStock": {
"default": false,
"description": "When true, a customer is emailed when something on one of their wishlists comes back into stock. It needs a connected store that reports availability; a catalog imported by CSV never does.",
"type": "boolean"
},
"priceDropThresholdPercent": {
"default": 10,
"description": "How far a price has to fall, as a percentage of the old price, before a price-drop alert goes out. It keeps a penny off a large item from becoming an email.",
"type": "integer",
"minimum": 1,
"maximum": 90
}
},
"required": ["priceDrop", "backInStock", "priceDropThresholdPercent"],
"additionalProperties": false
}
},
"required": [
"cascadeDeleteReviews",
"autoPublishReviews",
"autoPublishMinRating",
"showWishlistPrices",
"allowSharedWishlists",
"allowWishlistPurchaseMarking",
"defaultWishlistName",
"multipleWishlists",
"reviewNotifications",
"reviewScreening",
"reviewSummaries",
"reviewAnswers",
"productQuestions",
"wishlistAlerts"
],
"additionalProperties": false,
"description": "Organization-wide behavior toggles."
},
"timezone": {
"type": "string",
"description": "The organization's time zone, as an IANA name such as `America/New_York`. Everything decided on a clock (loyalty birthdays, visit allowances, expiry sweeps) uses it."
},
"access": {
"type": "object",
"properties": {
"state": {
"type": "string",
"enum": ["active", "trialing", "trial-ended", "canceled"],
"description": "Where the organization stands with its plan: `active` when something is paying, `trialing` during the free trial, and `trial-ended` or `canceled` once nothing is."
},
"writable": {
"type": "boolean",
"description": "Whether the organization can be changed. When it is false every create, update and delete is refused with a 402, and reads and exports keep working."
},
"message": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Why writes are refused, as a sentence to show the merchant. It is the same wording the API refuses with. Null while the organization is writable."
},
"trialEndsAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the free trial ends, while one is running. Null otherwise."
},
"readOnlySince": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the organization became read-only. Null while it is writable."
},
"exportsUntil": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "The end of the export window a read-only organization is given. Nothing is deleted on that date, and exports carry on working."
}
},
"required": [
"state",
"writable",
"message",
"trialEndsAt",
"readOnlySince",
"exportsUntil"
],
"additionalProperties": false,
"description": "Whether this organization can be changed, and what to do if not."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the organization was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the organization was last changed."
}
},
"required": [
"id",
"name",
"slug",
"logoUrl",
"plan",
"limits",
"settings",
"timezone",
"access",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The current organization."
},
"role": {
"anyOf": [
{
"type": "string",
"enum": ["owner", "member"],
"description": "What a person may do in the organization. An owner can also manage who is in the organization and what each person may do. A member can use everything else."
},
{
"type": "null"
}
],
"description": "Your role in this organization. Null when the request is authenticated with an API key or an OAuth connection, since those act for the whole organization rather than for a person."
}
},
"required": ["data", "role"],
"additionalProperties": false
}PATCH /organization
Update the current organization: its settings, time zone, display name or logo. Only the fields you send are changed; the rest are left alone. Name and logo changes are recorded in the audit trail. The slug cannot be changed, since storefront pages are served from it.
Requires the settings:write permission.
Body Parameters
settingsobjectThe settings to change.
timezonestringThe organization's time zone, as an IANA name such as America/New_York. Everything decided on a clock (loyalty birthdays, visit allowances, expiry sweeps) uses it.
namestringA new display name for the organization.
logostring | nullThe key of an image uploaded through POST /files/upload-url with public access, or null to remove the logo.
curl \
-X PATCH \
"https://api.cascade.dev/organization" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"settings": {
"cascadeDeleteReviews": true,
"autoPublishReviews": true,
"autoPublishMinRating": 0,
"showWishlistPrices": true,
"allowSharedWishlists": true,
"allowWishlistPurchaseMarking": true,
"defaultWishlistName": "string",
"multipleWishlists": true,
"reviewNotifications": {
"perReview": true,
"perReviewMaxRating": 0,
"digest": true,
"slackWebhookUrl": "string"
},
"reviewScreening": {
"links": true,
"profanity": true,
"blocklist": [
"..."
],
"ai": true
},
"reviewSummaries": {
"enabled": true
},
"reviewAnswers": {
"enabled": true
},
"productQuestions": {
"enabled": true,
"allowShopperAnswers": true,
"notifyAsker": true
},
"wishlistAlerts": {
"priceDrop": true,
"backInStock": true,
"priceDropThresholdPercent": 0
}
},
"timezone": "string",
"name": "string",
"logo": "string"
}'import { client } from "@cascade-commerce/api/admin/client"
import { patchOrganization } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await patchOrganization({
body: {
settings: {
cascadeDeleteReviews: true,
autoPublishReviews: true,
autoPublishMinRating: 0,
showWishlistPrices: true,
allowSharedWishlists: true,
allowWishlistPurchaseMarking: true,
defaultWishlistName: "string",
multipleWishlists: true,
reviewNotifications: {
perReview: true,
perReviewMaxRating: 0,
digest: true,
slackWebhookUrl: "string",
},
reviewScreening: {
links: true,
profanity: true,
blocklist: ["..."],
ai: true,
},
reviewSummaries: {
enabled: true,
},
reviewAnswers: {
enabled: true,
},
productQuestions: {
enabled: true,
allowShopperAnswers: true,
notifyAsker: true,
},
wishlistAlerts: {
priceDrop: true,
backInStock: true,
priceDropThresholdPercent: 0,
},
},
timezone: "string",
name: "string",
logo: "string",
},
})require "net/http"
uri = URI("https://api.cascade.dev/organization")
req = Net::HTTP::Patch.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"settings": {
"cascadeDeleteReviews": true,
"autoPublishReviews": true,
"autoPublishMinRating": 0,
"showWishlistPrices": true,
"allowSharedWishlists": true,
"allowWishlistPurchaseMarking": true,
"defaultWishlistName": "string",
"multipleWishlists": true,
"reviewNotifications": {
"perReview": true,
"perReviewMaxRating": 0,
"digest": true,
"slackWebhookUrl": "string"
},
"reviewScreening": {
"links": true,
"profanity": true,
"blocklist": [
"..."
],
"ai": true
},
"reviewSummaries": {
"enabled": true
},
"reviewAnswers": {
"enabled": true
},
"productQuestions": {
"enabled": true,
"allowShopperAnswers": true,
"notifyAsker": true
},
"wishlistAlerts": {
"priceDrop": true,
"backInStock": true,
"priceDropThresholdPercent": 0
}
},
"timezone": "string",
"name": "string",
"logo": "string"
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"settings": {
"cascadeDeleteReviews": true,
"autoPublishReviews": true,
"autoPublishMinRating": 0,
"showWishlistPrices": true,
"allowSharedWishlists": true,
"allowWishlistPurchaseMarking": true,
"defaultWishlistName": "string",
"multipleWishlists": true,
"reviewNotifications": {
"perReview": true,
"perReviewMaxRating": 0,
"digest": true,
"slackWebhookUrl": "string"
},
"reviewScreening": {
"links": true,
"profanity": true,
"blocklist": [
"..."
],
"ai": true
},
"reviewSummaries": {
"enabled": true
},
"reviewAnswers": {
"enabled": true
},
"productQuestions": {
"enabled": true,
"allowShopperAnswers": true,
"notifyAsker": true
},
"wishlistAlerts": {
"priceDrop": true,
"backInStock": true,
"priceDropThresholdPercent": 0
}
},
"timezone": "string",
"name": "string",
"logo": "string"
}"""
req = Request(
"https://api.cascade.dev/organization",
data=body.encode(),
method="PATCH",
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"slug": "string",
"logoUrl": "https://example.com",
"plan": "starter",
"limits": {
"sites": 0,
"products": 0,
"variantsPerProduct": 0,
"customers": 0,
"monthlySurveys": 0,
"monthlyLoyaltyMAU": 0,
"monthlyWishlistMAU": 0,
"apiKeys": 0,
"seats": 0,
"pendingInvitations": 0,
"webhooks": 0,
"loyaltyPrograms": 0,
"currencies": 0,
"rulesPerLoyaltyProgram": 0,
"tiersPerLoyaltyProgram": 0,
"rewardsPerLoyaltyProgram": 0,
"stampCardTypes": 0,
"surveyFlows": 0,
"stepsPerSurveyFlow": 0,
"savedCharts": 0,
"dashboards": 0,
"savedFilters": 0,
"quickFilters": 0,
"tagsPerResource": 0,
"wishlistsPerCustomer": 0,
"itemsPerWishlist": 0,
"brandAssetsMB": 0,
"totalStorageMB": 0,
"singleUploadMB": 0,
"assistantAttachmentBytes": 0,
"mediaPerReview": 0,
"monthlyReviewSubmissions": 0,
"webhookEventRetentionDays": 0,
"auditLogRetentionDays": 0,
"importJobRetentionDays": 0,
"importJobsPerDay": 0,
"exportJobsPerDay": 0,
"rowsPerImport": 0,
"rowsPerExport": 0,
"apiRequestsPerMinute": 0,
"mcpRequestsPerMinute": 0,
"loyaltyEventsPerMinute": 0,
"loyaltyStampsPerMinute": 0,
"widgetRequestsPerSession": 0,
"emailsPerCustomerPerDay": 0,
"aiSummaryMinReviews": 0,
"aiSummaryRegenerateReviews": 0,
"aiSummaryRegeneratePercent": 0,
"aiSummaryMinHoursBetween": 0,
"monthlyReviewAnswers": 0,
"aiReviewSummaries": true,
"aiReviewAnswers": true,
"aiReviewScreening": true,
"sso": true
},
"settings": {
"cascadeDeleteReviews": false,
"autoPublishReviews": false,
"autoPublishMinRating": 1,
"showWishlistPrices": true,
"allowSharedWishlists": true,
"allowWishlistPurchaseMarking": true,
"defaultWishlistName": "My wishlist",
"multipleWishlists": true,
"reviewNotifications": {
"perReview": false,
"perReviewMaxRating": 5,
"digest": false,
"slackWebhookUrl": null
},
"reviewScreening": {
"links": true,
"profanity": true,
"blocklist": [],
"ai": false
},
"reviewSummaries": {
"enabled": true
},
"reviewAnswers": {
"enabled": true
},
"productQuestions": {
"enabled": true,
"allowShopperAnswers": false,
"notifyAsker": true
},
"wishlistAlerts": {
"priceDrop": false,
"backInStock": false,
"priceDropThresholdPercent": 10
}
},
"timezone": "string",
"access": {
"state": "active",
"writable": true,
"message": "string",
"trialEndsAt": "2025-01-15T09:30:00Z",
"readOnlySince": "2025-01-15T09:30:00Z",
"exportsUntil": "2025-01-15T09:30:00Z"
},
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the organization."
},
"name": {
"type": "string",
"description": "The organization's display name."
},
"slug": {
"type": "string",
"description": "The organization's unique handle. Your storefront pages are served from it, so it cannot be changed."
},
"logoUrl": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "null"
}
],
"description": "Public URL of the organization's logo image."
},
"plan": {
"type": "string",
"enum": ["starter", "growth", "enterprise"],
"description": "Which set of default limits the organization is held to. It is not a billing record: it only selects the defaults, and any individual limit can still be overridden on the organization."
},
"limits": {
"type": "object",
"properties": {
"sites": {
"type": "number",
"description": "How many sites the organization may connect."
},
"products": {
"type": "number",
"description": "How many products the organization may store."
},
"variantsPerProduct": {
"type": "number",
"description": "How many variants a single product may have."
},
"customers": {
"type": "number",
"description": "How many customers the organization may store."
},
"monthlySurveys": {
"type": "number",
"description": "How many surveys may be sent per calendar month."
},
"monthlyLoyaltyMAU": {
"type": "number",
"description": "How many loyalty members may be active in a calendar month."
},
"monthlyWishlistMAU": {
"type": "number",
"description": "How many customers may be active on a wishlist in a calendar month."
},
"apiKeys": {
"type": "number",
"description": "How many API keys the organization may hold at once."
},
"seats": {
"type": "number",
"description": "How many people may be members of the organization."
},
"pendingInvitations": {
"type": "number",
"description": "How many invitations may be waiting to be accepted at once."
},
"webhooks": {
"type": "number",
"description": "How many webhook endpoints the organization may register."
},
"loyaltyPrograms": {
"type": "number",
"description": "How many loyalty programs the organization may run."
},
"currencies": {
"type": "number",
"description": "How many currencies the organization may trade in."
},
"rulesPerLoyaltyProgram": {
"type": "number",
"description": "How many earning rules a single loyalty program may have."
},
"tiersPerLoyaltyProgram": {
"type": "number",
"description": "How many tiers a single loyalty program may have."
},
"rewardsPerLoyaltyProgram": {
"type": "number",
"description": "How many reward definitions a single loyalty program may offer."
},
"stampCardTypes": {
"type": "number",
"description": "How many stamp card types the organization may define."
},
"surveyFlows": {
"type": "number",
"description": "How many survey flows the organization may define."
},
"stepsPerSurveyFlow": {
"type": "number",
"description": "How many steps a single survey flow may have."
},
"savedCharts": {
"type": "number",
"description": "How many saved charts the organization may keep."
},
"dashboards": {
"type": "number",
"description": "How many dashboards the organization may keep."
},
"savedFilters": {
"type": "number",
"description": "How many saved filters the organization may keep."
},
"quickFilters": {
"type": "number",
"description": "How many quick filters the organization may pin."
},
"tagsPerResource": {
"type": "number",
"description": "How many tags may be attached to a single customer, product, or order."
},
"wishlistsPerCustomer": {
"type": "number",
"description": "How many wishlists a single customer may have."
},
"itemsPerWishlist": {
"type": "number",
"description": "How many items a single wishlist may hold."
},
"brandAssetsMB": {
"type": "number",
"description": "How many megabytes of brand assets each site may hold."
},
"totalStorageMB": {
"type": "number",
"description": "How many megabytes of uploaded files the organization may hold in total."
},
"singleUploadMB": {
"type": "number",
"description": "How many megabytes a single uploaded file may be."
},
"assistantAttachmentBytes": {
"type": "number",
"description": "How many bytes of attachments a single message to the assistant may carry."
},
"mediaPerReview": {
"type": "number",
"description": "How many photos or videos a single review may carry."
},
"monthlyReviewSubmissions": {
"type": "number",
"description": "How many reviews may be submitted per calendar month."
},
"webhookEventRetentionDays": {
"type": "number",
"description": "How many days of webhook delivery history are kept before being purged."
},
"auditLogRetentionDays": {
"type": "number",
"description": "How many days of audit log entries are kept before being purged."
},
"importJobRetentionDays": {
"type": "number",
"description": "How many days an import job is kept, along with the source file and the links to the records it wrote, before being purged."
},
"importJobsPerDay": {
"type": "number",
"description": "How many import jobs may be started per day."
},
"exportJobsPerDay": {
"type": "number",
"description": "How many export jobs may be started per day."
},
"rowsPerImport": {
"type": "number",
"description": "How many rows a single import may carry."
},
"rowsPerExport": {
"type": "number",
"description": "How many rows a single export may produce."
},
"apiRequestsPerMinute": {
"type": "number",
"description": "How many admin API requests every secret key in the organization shares per minute."
},
"mcpRequestsPerMinute": {
"type": "number",
"description": "How many MCP requests every OAuth connection to the organization shares per minute."
},
"loyaltyEventsPerMinute": {
"type": "number",
"description": "How many events `POST /loyalty-events` accepts per minute."
},
"loyaltyStampsPerMinute": {
"type": "number",
"description": "How many stamp awards `POST /loyalty-stamps` accepts per minute."
},
"widgetRequestsPerSession": {
"type": "number",
"description": "How many widget API requests one shopper session may make."
},
"emailsPerCustomerPerDay": {
"type": "number",
"description": "How many emails a single customer may be sent in a day."
},
"aiSummaryMinReviews": {
"type": "number",
"description": "How many published reviews a product needs before a summary is generated or shown."
},
"aiSummaryRegenerateReviews": {
"type": "number",
"description": "How far the published review count has to move from the stored summary's count before it is regenerated."
},
"aiSummaryRegeneratePercent": {
"type": "number",
"description": "How far the published review count has to move, as a percentage of the stored summary's count, before it is regenerated. Either this or `aiSummaryRegenerateReviews` triggers it."
},
"aiSummaryMinHoursBetween": {
"type": "number",
"description": "The shortest gap between two generated summaries for one product. The default of 24 is the one regeneration per product per day ceiling."
},
"monthlyReviewAnswers": {
"type": "number",
"description": "How many review answers may be generated per calendar month. Repeated questions are answered from cache and do not count."
},
"aiReviewSummaries": {
"type": "boolean",
"description": "Whether the organization can generate AI summaries of a product's reviews."
},
"aiReviewAnswers": {
"type": "boolean",
"description": "Whether shoppers can ask a question on a product page and get an answer generated from its published reviews."
},
"aiReviewScreening": {
"type": "boolean",
"description": "Whether arriving reviews are screened by the AI moderation model."
},
"sso": {
"type": "boolean",
"description": "Whether the organization can sign in through its own SSO provider."
}
},
"required": [
"sites",
"products",
"variantsPerProduct",
"customers",
"monthlySurveys",
"monthlyLoyaltyMAU",
"monthlyWishlistMAU",
"apiKeys",
"seats",
"pendingInvitations",
"webhooks",
"loyaltyPrograms",
"currencies",
"rulesPerLoyaltyProgram",
"tiersPerLoyaltyProgram",
"rewardsPerLoyaltyProgram",
"stampCardTypes",
"surveyFlows",
"stepsPerSurveyFlow",
"savedCharts",
"dashboards",
"savedFilters",
"quickFilters",
"tagsPerResource",
"wishlistsPerCustomer",
"itemsPerWishlist",
"brandAssetsMB",
"totalStorageMB",
"singleUploadMB",
"assistantAttachmentBytes",
"mediaPerReview",
"monthlyReviewSubmissions",
"webhookEventRetentionDays",
"auditLogRetentionDays",
"importJobRetentionDays",
"importJobsPerDay",
"exportJobsPerDay",
"rowsPerImport",
"rowsPerExport",
"apiRequestsPerMinute",
"mcpRequestsPerMinute",
"loyaltyEventsPerMinute",
"loyaltyStampsPerMinute",
"widgetRequestsPerSession",
"emailsPerCustomerPerDay",
"aiSummaryMinReviews",
"aiSummaryRegenerateReviews",
"aiSummaryRegeneratePercent",
"aiSummaryMinHoursBetween",
"monthlyReviewAnswers",
"aiReviewSummaries",
"aiReviewAnswers",
"aiReviewScreening",
"sso"
],
"additionalProperties": false,
"description": "The limits this organization is held to, with the plan's defaults already resolved against anything set on the organization itself."
},
"settings": {
"type": "object",
"properties": {
"cascadeDeleteReviews": {
"default": false,
"description": "When true, deleting a product or customer also deletes their reviews. When false, the reviews are kept, unlinked and unpublished.",
"type": "boolean"
},
"autoPublishReviews": {
"default": false,
"description": "When true, a newly-submitted review is published on arrival instead of waiting for a manual publish, as long as it meets `autoPublishMinRating`.",
"type": "boolean"
},
"autoPublishMinRating": {
"default": 1,
"description": "The lowest star rating that auto-publishes. Anything below this is held back for a human even when `autoPublishReviews` is on.",
"type": "integer",
"minimum": 1,
"maximum": 5
},
"showWishlistPrices": {
"default": true,
"description": "When true, the public shared wishlist page shows each item's stored price. When false, no prices are shown.",
"type": "boolean"
},
"allowSharedWishlists": {
"default": true,
"description": "When true, customers can share wishlists with a public link. When false, every share link stops working and sharing cannot be turned on, but each wishlist's own sharing choice is kept for when this is switched back on.",
"type": "boolean"
},
"allowWishlistPurchaseMarking": {
"default": true,
"description": "When true, anyone viewing a shared wishlist can mark an item as purchased so two gift buyers don't buy the same thing. The list's owner is never shown a purchase somebody else made.",
"type": "boolean"
},
"defaultWishlistName": {
"default": "My wishlist",
"description": "The name given to the wishlist that is created automatically the first time a customer saves a product with one click.",
"type": "string",
"minLength": 1
},
"multipleWishlists": {
"default": true,
"description": "When true, a customer can keep several wishlists and name them. When false, everything they save goes on one list and the storefront hides the controls for creating another.",
"type": "boolean"
},
"reviewNotifications": {
"default": {
"perReview": false,
"perReviewMaxRating": 5,
"digest": false,
"slackWebhookUrl": null
},
"description": "Who hears about arriving reviews, and how.",
"type": "object",
"properties": {
"perReview": {
"default": false,
"description": "When true, every arriving review that meets `perReviewMaxRating` emails the organization's members.",
"type": "boolean"
},
"perReviewMaxRating": {
"default": 5,
"description": "The highest star rating that triggers a per-review alert. Set it to 5 to be told about every review, or lower to hear only about the poor ones.",
"type": "integer",
"minimum": 1,
"maximum": 5
},
"digest": {
"default": false,
"description": "When true, a daily summary of the last 24 hours of reviews is emailed to the organization's members. Days with no reviews send nothing.",
"type": "boolean"
},
"slackWebhookUrl": {
"default": null,
"description": "A Slack incoming webhook URL. When set, the alerts you have switched on are also posted to Slack. Null to post nowhere.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["perReview", "perReviewMaxRating", "digest", "slackWebhookUrl"],
"additionalProperties": false
},
"reviewScreening": {
"default": {
"links": true,
"profanity": true,
"blocklist": [],
"ai": false
},
"description": "What flags an arriving review for moderation.",
"type": "object",
"properties": {
"links": {
"default": true,
"description": "When true, a review containing a URL is flagged for moderation. Links in reviews are the strongest spam signal.",
"type": "boolean"
},
"profanity": {
"default": true,
"description": "When true, a review matching the built-in profanity wordlist is flagged for moderation. Flagged reviews are never rejected automatically.",
"type": "boolean"
},
"blocklist": {
"default": [],
"description": "Words and phrases of your own that flag a review for moderation, matched case-insensitively as whole words.",
"type": "array",
"items": {
"type": "string"
}
},
"ai": {
"default": false,
"description": "When true, arriving reviews are also read by a model, which flags anything a person should look at, and longer reviews are checked by an AI-writing detector. It runs after the review is submitted, so a review waits for it rather than publishing and being taken down again. Nothing is ever rejected automatically. Requires the AI review screening feature on your plan.",
"type": "boolean"
}
},
"required": ["links", "profanity", "blocklist", "ai"],
"additionalProperties": false
},
"reviewSummaries": {
"default": {
"enabled": true
},
"description": "Whether products get a generated summary of what their reviewers say.",
"type": "object",
"properties": {
"enabled": {
"default": true,
"description": "When true, products with enough published reviews get a generated summary of what reviewers say, shown above the reviews on your storefront. When false, no summary is generated and none is shown.",
"type": "boolean"
}
},
"required": ["enabled"],
"additionalProperties": false
},
"reviewAnswers": {
"default": {
"enabled": true
},
"description": "Whether shoppers can ask a question answered from a product's reviews.",
"type": "object",
"properties": {
"enabled": {
"default": true,
"description": "When true, shoppers can ask a question on a product page and get an answer generated from its published reviews, shown only to them and labeled as generated. When false, the ask box is hidden everywhere.",
"type": "boolean"
}
},
"required": ["enabled"],
"additionalProperties": false
},
"productQuestions": {
"default": {
"enabled": true,
"allowShopperAnswers": false,
"notifyAsker": true
},
"description": "Whether shoppers can ask a question your team answers and publishes.",
"type": "object",
"properties": {
"enabled": {
"default": true,
"description": "When true, shoppers can ask a question that reaches your moderation queue, and answered questions are published on the product page. When false, no question is recorded and the published Q&A is hidden.",
"type": "boolean"
},
"allowShopperAnswers": {
"default": false,
"description": "When true, shoppers can answer each other's published questions. Their answers wait for moderation like the questions do. Off by default: most stores want to be the only voice answering.",
"type": "boolean"
},
"notifyAsker": {
"default": true,
"description": "When true, a shopper who left their email when asking is emailed once, when the first answer to their question is published.",
"type": "boolean"
}
},
"required": ["enabled", "allowShopperAnswers", "notifyAsker"],
"additionalProperties": false
},
"wishlistAlerts": {
"default": {
"priceDrop": false,
"backInStock": false,
"priceDropThresholdPercent": 10
},
"description": "Whether saved items email their owner when they change.",
"type": "object",
"properties": {
"priceDrop": {
"default": false,
"description": "When true, a customer is emailed when something on one of their wishlists drops in price by at least `priceDropThresholdPercent`.",
"type": "boolean"
},
"backInStock": {
"default": false,
"description": "When true, a customer is emailed when something on one of their wishlists comes back into stock. It needs a connected store that reports availability; a catalog imported by CSV never does.",
"type": "boolean"
},
"priceDropThresholdPercent": {
"default": 10,
"description": "How far a price has to fall, as a percentage of the old price, before a price-drop alert goes out. It keeps a penny off a large item from becoming an email.",
"type": "integer",
"minimum": 1,
"maximum": 90
}
},
"required": ["priceDrop", "backInStock", "priceDropThresholdPercent"],
"additionalProperties": false
}
},
"required": [
"cascadeDeleteReviews",
"autoPublishReviews",
"autoPublishMinRating",
"showWishlistPrices",
"allowSharedWishlists",
"allowWishlistPurchaseMarking",
"defaultWishlistName",
"multipleWishlists",
"reviewNotifications",
"reviewScreening",
"reviewSummaries",
"reviewAnswers",
"productQuestions",
"wishlistAlerts"
],
"additionalProperties": false,
"description": "Organization-wide behavior toggles."
},
"timezone": {
"type": "string",
"description": "The organization's time zone, as an IANA name such as `America/New_York`. Everything decided on a clock (loyalty birthdays, visit allowances, expiry sweeps) uses it."
},
"access": {
"type": "object",
"properties": {
"state": {
"type": "string",
"enum": ["active", "trialing", "trial-ended", "canceled"],
"description": "Where the organization stands with its plan: `active` when something is paying, `trialing` during the free trial, and `trial-ended` or `canceled` once nothing is."
},
"writable": {
"type": "boolean",
"description": "Whether the organization can be changed. When it is false every create, update and delete is refused with a 402, and reads and exports keep working."
},
"message": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Why writes are refused, as a sentence to show the merchant. It is the same wording the API refuses with. Null while the organization is writable."
},
"trialEndsAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the free trial ends, while one is running. Null otherwise."
},
"readOnlySince": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the organization became read-only. Null while it is writable."
},
"exportsUntil": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "The end of the export window a read-only organization is given. Nothing is deleted on that date, and exports carry on working."
}
},
"required": ["state", "writable", "message", "trialEndsAt", "readOnlySince", "exportsUntil"],
"additionalProperties": false,
"description": "Whether this organization can be changed, and what to do if not."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the organization was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the organization was last changed."
}
},
"required": [
"id",
"name",
"slug",
"logoUrl",
"plan",
"limits",
"settings",
"timezone",
"access",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The updated organization."
}GET /organization/usage
Get the organization's usage against every allowance it is counted on. Three of them are billable (review submissions, active loyalty members, active wishlist customers) and the rest are backstops that are counted but never priced. Each meter says whether it covers the current UTC calendar month or is a running total, and carries its own label so you can render it without a lookup table. These counts are informational, and going over an allowance does not block anything. Counts are cached for a few minutes, so a number can lag a write you just made.
Requires the settings:read permission.
curl \
"https://api.cascade.dev/organization/usage" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { getOrganizationUsage } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getOrganizationUsage()require "net/http"
uri = URI("https://api.cascade.dev/organization/usage")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/organization/usage",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"monthlySurveys": {
"label": "string",
"description": "string",
"used": 0,
"limit": 0,
"usedLabel": "string",
"limitLabel": "string",
"status": "ok",
"group": "billable",
"grace": {
"startedAt": "2025-01-15T09:30:00Z",
"degradesAt": "2025-01-15T09:30:00Z",
"degraded": true
},
"period": "monthly",
"overage": {
"enabled": true,
"units": 0,
"unitsLabel": "string",
"blocks": 0,
"blockSize": 0,
"amount": 0,
"amountLabel": "string",
"rateLabel": "string",
"currency": "string"
}
},
"monthlyLoyaltyMAU": {
"label": "string",
"description": "string",
"used": 0,
"limit": 0,
"usedLabel": "string",
"limitLabel": "string",
"status": "ok",
"group": "billable",
"grace": {
"startedAt": "2025-01-15T09:30:00Z",
"degradesAt": "2025-01-15T09:30:00Z",
"degraded": true
},
"period": "monthly",
"overage": {
"enabled": true,
"units": 0,
"unitsLabel": "string",
"blocks": 0,
"blockSize": 0,
"amount": 0,
"amountLabel": "string",
"rateLabel": "string",
"currency": "string"
}
},
"monthlyWishlistMAU": {
"label": "string",
"description": "string",
"used": 0,
"limit": 0,
"usedLabel": "string",
"limitLabel": "string",
"status": "ok",
"group": "billable",
"grace": {
"startedAt": "2025-01-15T09:30:00Z",
"degradesAt": "2025-01-15T09:30:00Z",
"degraded": true
},
"period": "monthly",
"overage": {
"enabled": true,
"units": 0,
"unitsLabel": "string",
"blocks": 0,
"blockSize": 0,
"amount": 0,
"amountLabel": "string",
"rateLabel": "string",
"currency": "string"
}
},
"monthlyReviewSubmissions": {
"label": "string",
"description": "string",
"used": 0,
"limit": 0,
"usedLabel": "string",
"limitLabel": "string",
"status": "ok",
"group": "billable",
"grace": {
"startedAt": "2025-01-15T09:30:00Z",
"degradesAt": "2025-01-15T09:30:00Z",
"degraded": true
},
"period": "monthly",
"overage": {
"enabled": true,
"units": 0,
"unitsLabel": "string",
"blocks": 0,
"blockSize": 0,
"amount": 0,
"amountLabel": "string",
"rateLabel": "string",
"currency": "string"
}
},
"monthlyReviewAnswers": {
"label": "string",
"description": "string",
"used": 0,
"limit": 0,
"usedLabel": "string",
"limitLabel": "string",
"status": "ok",
"group": "billable",
"grace": {
"startedAt": "2025-01-15T09:30:00Z",
"degradesAt": "2025-01-15T09:30:00Z",
"degraded": true
},
"period": "monthly",
"overage": {
"enabled": true,
"units": 0,
"unitsLabel": "string",
"blocks": 0,
"blockSize": 0,
"amount": 0,
"amountLabel": "string",
"rateLabel": "string",
"currency": "string"
}
},
"products": {
"label": "string",
"description": "string",
"used": 0,
"limit": 0,
"usedLabel": "string",
"limitLabel": "string",
"status": "ok",
"group": "billable",
"grace": {
"startedAt": "2025-01-15T09:30:00Z",
"degradesAt": "2025-01-15T09:30:00Z",
"degraded": true
},
"period": "monthly",
"overage": {
"enabled": true,
"units": 0,
"unitsLabel": "string",
"blocks": 0,
"blockSize": 0,
"amount": 0,
"amountLabel": "string",
"rateLabel": "string",
"currency": "string"
}
},
"customers": {
"label": "string",
"description": "string",
"used": 0,
"limit": 0,
"usedLabel": "string",
"limitLabel": "string",
"status": "ok",
"group": "billable",
"grace": {
"startedAt": "2025-01-15T09:30:00Z",
"degradesAt": "2025-01-15T09:30:00Z",
"degraded": true
},
"period": "monthly",
"overage": {
"enabled": true,
"units": 0,
"unitsLabel": "string",
"blocks": 0,
"blockSize": 0,
"amount": 0,
"amountLabel": "string",
"rateLabel": "string",
"currency": "string"
}
},
"sites": {
"label": "string",
"description": "string",
"used": 0,
"limit": 0,
"usedLabel": "string",
"limitLabel": "string",
"status": "ok",
"group": "billable",
"grace": {
"startedAt": "2025-01-15T09:30:00Z",
"degradesAt": "2025-01-15T09:30:00Z",
"degraded": true
},
"period": "monthly",
"overage": {
"enabled": true,
"units": 0,
"unitsLabel": "string",
"blocks": 0,
"blockSize": 0,
"amount": 0,
"amountLabel": "string",
"rateLabel": "string",
"currency": "string"
}
},
"apiKeys": {
"label": "string",
"description": "string",
"used": 0,
"limit": 0,
"usedLabel": "string",
"limitLabel": "string",
"status": "ok",
"group": "billable",
"grace": {
"startedAt": "2025-01-15T09:30:00Z",
"degradesAt": "2025-01-15T09:30:00Z",
"degraded": true
},
"period": "monthly",
"overage": {
"enabled": true,
"units": 0,
"unitsLabel": "string",
"blocks": 0,
"blockSize": 0,
"amount": 0,
"amountLabel": "string",
"rateLabel": "string",
"currency": "string"
}
},
"seats": {
"label": "string",
"description": "string",
"used": 0,
"limit": 0,
"usedLabel": "string",
"limitLabel": "string",
"status": "ok",
"group": "billable",
"grace": {
"startedAt": "2025-01-15T09:30:00Z",
"degradesAt": "2025-01-15T09:30:00Z",
"degraded": true
},
"period": "monthly",
"overage": {
"enabled": true,
"units": 0,
"unitsLabel": "string",
"blocks": 0,
"blockSize": 0,
"amount": 0,
"amountLabel": "string",
"rateLabel": "string",
"currency": "string"
}
},
"webhooks": {
"label": "string",
"description": "string",
"used": 0,
"limit": 0,
"usedLabel": "string",
"limitLabel": "string",
"status": "ok",
"group": "billable",
"grace": {
"startedAt": "2025-01-15T09:30:00Z",
"degradesAt": "2025-01-15T09:30:00Z",
"degraded": true
},
"period": "monthly",
"overage": {
"enabled": true,
"units": 0,
"unitsLabel": "string",
"blocks": 0,
"blockSize": 0,
"amount": 0,
"amountLabel": "string",
"rateLabel": "string",
"currency": "string"
}
},
"brandAssetsMB": {
"label": "string",
"description": "string",
"used": 0,
"limit": 0,
"usedLabel": "string",
"limitLabel": "string",
"status": "ok",
"group": "billable",
"grace": {
"startedAt": "2025-01-15T09:30:00Z",
"degradesAt": "2025-01-15T09:30:00Z",
"degraded": true
},
"period": "monthly",
"overage": {
"enabled": true,
"units": 0,
"unitsLabel": "string",
"blocks": 0,
"blockSize": 0,
"amount": 0,
"amountLabel": "string",
"rateLabel": "string",
"currency": "string"
}
},
"totalStorageMB": {
"label": "string",
"description": "string",
"used": 0,
"limit": 0,
"usedLabel": "string",
"limitLabel": "string",
"status": "ok",
"group": "billable",
"grace": {
"startedAt": "2025-01-15T09:30:00Z",
"degradesAt": "2025-01-15T09:30:00Z",
"degraded": true
},
"period": "monthly",
"overage": {
"enabled": true,
"units": 0,
"unitsLabel": "string",
"blocks": 0,
"blockSize": 0,
"amount": 0,
"amountLabel": "string",
"rateLabel": "string",
"currency": "string"
}
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"monthlySurveys": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "The meter's name, ready to show as a row label without rewording."
},
"description": {
"type": "string",
"description": "What the meter counts, as a full sentence."
},
"used": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How much of the allowance has been used."
},
"limit": {
"type": "number",
"description": "How much this organization is allowed."
},
"usedLabel": {
"type": "string",
"description": "`used` rendered the way the limit reads it, with any unit attached."
},
"limitLabel": {
"type": "string",
"description": "`limit` rendered the way the limit reads it, with any unit attached."
},
"status": {
"type": "string",
"enum": ["ok", "warning", "over"],
"description": "Where usage stands against the allowance: ok below 80 percent, warning from 80 percent up to the allowance, over beyond it. Going over keeps working. See `grace` for what happens if it stays over."
},
"group": {
"type": "string",
"enum": ["billable", "backstop"],
"description": "Whether the allowance is one a plan is sold on (billable) or an abuse backstop that is counted but never priced (backstop)."
},
"grace": {
"anyOf": [
{
"type": "object",
"properties": {
"startedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the grace period started."
},
"degradesAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When this product starts turning new activity away, measured in the organization's time zone."
},
"degraded": {
"type": "boolean",
"description": "Whether that has already happened."
}
},
"required": ["startedAt", "degradesAt", "degraded"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The grace period this allowance is in, or null if it is not in one. Only a billable allowance that is currently over ever carries one."
},
"period": {
"type": "string",
"enum": ["monthly", "structural"],
"description": "Whether the count covers the current UTC calendar month (monthly) or is a running total of what the account holds right now (structural)."
},
"overage": {
"anyOf": [
{
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this organization is billed for going over rather than degraded after a grace period. It is one switch for all three sold allowances, on by default."
},
"units": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How far past the allowance usage is, so far."
},
"unitsLabel": {
"type": "string",
"description": "`units` with any unit attached."
},
"blocks": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Started blocks of the rate's block size, which is what is charged for."
},
"blockSize": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many units one block covers."
},
"amount": {
"type": "number",
"description": "What this period's overage comes to so far, in whole units of the currency."
},
"amountLabel": {
"type": "string",
"description": "`amount` as money, ready to show."
},
"rateLabel": {
"type": "string",
"description": "The rate as a phrase, for example \"$10 per 1,000 review submissions\"."
},
"currency": {
"type": "string",
"description": "The ISO currency code overage is billed in."
}
},
"required": [
"enabled",
"units",
"unitsLabel",
"blocks",
"blockSize",
"amount",
"amountLabel",
"rateLabel",
"currency"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "What going over this allowance costs, and what it has cost so far this period. Null on an allowance that is not sold, and on a plan priced per contract, where overage is settled in the contract instead."
}
},
"required": [
"label",
"description",
"used",
"limit",
"usedLabel",
"limitLabel",
"status",
"group",
"grace",
"period",
"overage"
],
"additionalProperties": false,
"description": "Review requests sent by a survey flow in the current UTC calendar month."
},
"monthlyLoyaltyMAU": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "The meter's name, ready to show as a row label without rewording."
},
"description": {
"type": "string",
"description": "What the meter counts, as a full sentence."
},
"used": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How much of the allowance has been used."
},
"limit": {
"type": "number",
"description": "How much this organization is allowed."
},
"usedLabel": {
"type": "string",
"description": "`used` rendered the way the limit reads it, with any unit attached."
},
"limitLabel": {
"type": "string",
"description": "`limit` rendered the way the limit reads it, with any unit attached."
},
"status": {
"type": "string",
"enum": ["ok", "warning", "over"],
"description": "Where usage stands against the allowance: ok below 80 percent, warning from 80 percent up to the allowance, over beyond it. Going over keeps working. See `grace` for what happens if it stays over."
},
"group": {
"type": "string",
"enum": ["billable", "backstop"],
"description": "Whether the allowance is one a plan is sold on (billable) or an abuse backstop that is counted but never priced (backstop)."
},
"grace": {
"anyOf": [
{
"type": "object",
"properties": {
"startedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the grace period started."
},
"degradesAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When this product starts turning new activity away, measured in the organization's time zone."
},
"degraded": {
"type": "boolean",
"description": "Whether that has already happened."
}
},
"required": ["startedAt", "degradesAt", "degraded"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The grace period this allowance is in, or null if it is not in one. Only a billable allowance that is currently over ever carries one."
},
"period": {
"type": "string",
"enum": ["monthly", "structural"],
"description": "Whether the count covers the current UTC calendar month (monthly) or is a running total of what the account holds right now (structural)."
},
"overage": {
"anyOf": [
{
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this organization is billed for going over rather than degraded after a grace period. It is one switch for all three sold allowances, on by default."
},
"units": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How far past the allowance usage is, so far."
},
"unitsLabel": {
"type": "string",
"description": "`units` with any unit attached."
},
"blocks": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Started blocks of the rate's block size, which is what is charged for."
},
"blockSize": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many units one block covers."
},
"amount": {
"type": "number",
"description": "What this period's overage comes to so far, in whole units of the currency."
},
"amountLabel": {
"type": "string",
"description": "`amount` as money, ready to show."
},
"rateLabel": {
"type": "string",
"description": "The rate as a phrase, for example \"$10 per 1,000 review submissions\"."
},
"currency": {
"type": "string",
"description": "The ISO currency code overage is billed in."
}
},
"required": [
"enabled",
"units",
"unitsLabel",
"blocks",
"blockSize",
"amount",
"amountLabel",
"rateLabel",
"currency"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "What going over this allowance costs, and what it has cost so far this period. Null on an allowance that is not sold, and on a plan priced per contract, where overage is settled in the contract instead."
}
},
"required": [
"label",
"description",
"used",
"limit",
"usedLabel",
"limitLabel",
"status",
"group",
"grace",
"period",
"overage"
],
"additionalProperties": false,
"description": "Active loyalty members: distinct members with any loyalty activity (points, stamps, or rewards) in the current UTC calendar month."
},
"monthlyWishlistMAU": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "The meter's name, ready to show as a row label without rewording."
},
"description": {
"type": "string",
"description": "What the meter counts, as a full sentence."
},
"used": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How much of the allowance has been used."
},
"limit": {
"type": "number",
"description": "How much this organization is allowed."
},
"usedLabel": {
"type": "string",
"description": "`used` rendered the way the limit reads it, with any unit attached."
},
"limitLabel": {
"type": "string",
"description": "`limit` rendered the way the limit reads it, with any unit attached."
},
"status": {
"type": "string",
"enum": ["ok", "warning", "over"],
"description": "Where usage stands against the allowance: ok below 80 percent, warning from 80 percent up to the allowance, over beyond it. Going over keeps working. See `grace` for what happens if it stays over."
},
"group": {
"type": "string",
"enum": ["billable", "backstop"],
"description": "Whether the allowance is one a plan is sold on (billable) or an abuse backstop that is counted but never priced (backstop)."
},
"grace": {
"anyOf": [
{
"type": "object",
"properties": {
"startedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the grace period started."
},
"degradesAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When this product starts turning new activity away, measured in the organization's time zone."
},
"degraded": {
"type": "boolean",
"description": "Whether that has already happened."
}
},
"required": ["startedAt", "degradesAt", "degraded"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The grace period this allowance is in, or null if it is not in one. Only a billable allowance that is currently over ever carries one."
},
"period": {
"type": "string",
"enum": ["monthly", "structural"],
"description": "Whether the count covers the current UTC calendar month (monthly) or is a running total of what the account holds right now (structural)."
},
"overage": {
"anyOf": [
{
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this organization is billed for going over rather than degraded after a grace period. It is one switch for all three sold allowances, on by default."
},
"units": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How far past the allowance usage is, so far."
},
"unitsLabel": {
"type": "string",
"description": "`units` with any unit attached."
},
"blocks": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Started blocks of the rate's block size, which is what is charged for."
},
"blockSize": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many units one block covers."
},
"amount": {
"type": "number",
"description": "What this period's overage comes to so far, in whole units of the currency."
},
"amountLabel": {
"type": "string",
"description": "`amount` as money, ready to show."
},
"rateLabel": {
"type": "string",
"description": "The rate as a phrase, for example \"$10 per 1,000 review submissions\"."
},
"currency": {
"type": "string",
"description": "The ISO currency code overage is billed in."
}
},
"required": [
"enabled",
"units",
"unitsLabel",
"blocks",
"blockSize",
"amount",
"amountLabel",
"rateLabel",
"currency"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "What going over this allowance costs, and what it has cost so far this period. Null on an allowance that is not sold, and on a plan priced per contract, where overage is settled in the contract instead."
}
},
"required": [
"label",
"description",
"used",
"limit",
"usedLabel",
"limitLabel",
"status",
"group",
"grace",
"period",
"overage"
],
"additionalProperties": false,
"description": "Active wishlist customers: distinct customers who created a wishlist, or added or removed a wishlist item, in the current UTC calendar month. Viewing a shared wishlist does not count."
},
"monthlyReviewSubmissions": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "The meter's name, ready to show as a row label without rewording."
},
"description": {
"type": "string",
"description": "What the meter counts, as a full sentence."
},
"used": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How much of the allowance has been used."
},
"limit": {
"type": "number",
"description": "How much this organization is allowed."
},
"usedLabel": {
"type": "string",
"description": "`used` rendered the way the limit reads it, with any unit attached."
},
"limitLabel": {
"type": "string",
"description": "`limit` rendered the way the limit reads it, with any unit attached."
},
"status": {
"type": "string",
"enum": ["ok", "warning", "over"],
"description": "Where usage stands against the allowance: ok below 80 percent, warning from 80 percent up to the allowance, over beyond it. Going over keeps working. See `grace` for what happens if it stays over."
},
"group": {
"type": "string",
"enum": ["billable", "backstop"],
"description": "Whether the allowance is one a plan is sold on (billable) or an abuse backstop that is counted but never priced (backstop)."
},
"grace": {
"anyOf": [
{
"type": "object",
"properties": {
"startedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the grace period started."
},
"degradesAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When this product starts turning new activity away, measured in the organization's time zone."
},
"degraded": {
"type": "boolean",
"description": "Whether that has already happened."
}
},
"required": ["startedAt", "degradesAt", "degraded"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The grace period this allowance is in, or null if it is not in one. Only a billable allowance that is currently over ever carries one."
},
"period": {
"type": "string",
"enum": ["monthly", "structural"],
"description": "Whether the count covers the current UTC calendar month (monthly) or is a running total of what the account holds right now (structural)."
},
"overage": {
"anyOf": [
{
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this organization is billed for going over rather than degraded after a grace period. It is one switch for all three sold allowances, on by default."
},
"units": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How far past the allowance usage is, so far."
},
"unitsLabel": {
"type": "string",
"description": "`units` with any unit attached."
},
"blocks": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Started blocks of the rate's block size, which is what is charged for."
},
"blockSize": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many units one block covers."
},
"amount": {
"type": "number",
"description": "What this period's overage comes to so far, in whole units of the currency."
},
"amountLabel": {
"type": "string",
"description": "`amount` as money, ready to show."
},
"rateLabel": {
"type": "string",
"description": "The rate as a phrase, for example \"$10 per 1,000 review submissions\"."
},
"currency": {
"type": "string",
"description": "The ISO currency code overage is billed in."
}
},
"required": [
"enabled",
"units",
"unitsLabel",
"blocks",
"blockSize",
"amount",
"amountLabel",
"rateLabel",
"currency"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "What going over this allowance costs, and what it has cost so far this period. Null on an allowance that is not sold, and on a plan priced per contract, where overage is settled in the contract instead."
}
},
"required": [
"label",
"description",
"used",
"limit",
"usedLabel",
"limitLabel",
"status",
"group",
"grace",
"period",
"overage"
],
"additionalProperties": false,
"description": "Reviews submitted in the current UTC calendar month, however they arrived."
},
"monthlyReviewAnswers": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "The meter's name, ready to show as a row label without rewording."
},
"description": {
"type": "string",
"description": "What the meter counts, as a full sentence."
},
"used": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How much of the allowance has been used."
},
"limit": {
"type": "number",
"description": "How much this organization is allowed."
},
"usedLabel": {
"type": "string",
"description": "`used` rendered the way the limit reads it, with any unit attached."
},
"limitLabel": {
"type": "string",
"description": "`limit` rendered the way the limit reads it, with any unit attached."
},
"status": {
"type": "string",
"enum": ["ok", "warning", "over"],
"description": "Where usage stands against the allowance: ok below 80 percent, warning from 80 percent up to the allowance, over beyond it. Going over keeps working. See `grace` for what happens if it stays over."
},
"group": {
"type": "string",
"enum": ["billable", "backstop"],
"description": "Whether the allowance is one a plan is sold on (billable) or an abuse backstop that is counted but never priced (backstop)."
},
"grace": {
"anyOf": [
{
"type": "object",
"properties": {
"startedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the grace period started."
},
"degradesAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When this product starts turning new activity away, measured in the organization's time zone."
},
"degraded": {
"type": "boolean",
"description": "Whether that has already happened."
}
},
"required": ["startedAt", "degradesAt", "degraded"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The grace period this allowance is in, or null if it is not in one. Only a billable allowance that is currently over ever carries one."
},
"period": {
"type": "string",
"enum": ["monthly", "structural"],
"description": "Whether the count covers the current UTC calendar month (monthly) or is a running total of what the account holds right now (structural)."
},
"overage": {
"anyOf": [
{
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this organization is billed for going over rather than degraded after a grace period. It is one switch for all three sold allowances, on by default."
},
"units": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How far past the allowance usage is, so far."
},
"unitsLabel": {
"type": "string",
"description": "`units` with any unit attached."
},
"blocks": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Started blocks of the rate's block size, which is what is charged for."
},
"blockSize": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many units one block covers."
},
"amount": {
"type": "number",
"description": "What this period's overage comes to so far, in whole units of the currency."
},
"amountLabel": {
"type": "string",
"description": "`amount` as money, ready to show."
},
"rateLabel": {
"type": "string",
"description": "The rate as a phrase, for example \"$10 per 1,000 review submissions\"."
},
"currency": {
"type": "string",
"description": "The ISO currency code overage is billed in."
}
},
"required": [
"enabled",
"units",
"unitsLabel",
"blocks",
"blockSize",
"amount",
"amountLabel",
"rateLabel",
"currency"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "What going over this allowance costs, and what it has cost so far this period. Null on an allowance that is not sold, and on a plan priced per contract, where overage is settled in the contract instead."
}
},
"required": [
"label",
"description",
"used",
"limit",
"usedLabel",
"limitLabel",
"status",
"group",
"grace",
"period",
"overage"
],
"additionalProperties": false,
"description": "Review answers generated in the current UTC calendar month. Repeated questions are answered from cache and do not count."
},
"products": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "The meter's name, ready to show as a row label without rewording."
},
"description": {
"type": "string",
"description": "What the meter counts, as a full sentence."
},
"used": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How much of the allowance has been used."
},
"limit": {
"type": "number",
"description": "How much this organization is allowed."
},
"usedLabel": {
"type": "string",
"description": "`used` rendered the way the limit reads it, with any unit attached."
},
"limitLabel": {
"type": "string",
"description": "`limit` rendered the way the limit reads it, with any unit attached."
},
"status": {
"type": "string",
"enum": ["ok", "warning", "over"],
"description": "Where usage stands against the allowance: ok below 80 percent, warning from 80 percent up to the allowance, over beyond it. Going over keeps working. See `grace` for what happens if it stays over."
},
"group": {
"type": "string",
"enum": ["billable", "backstop"],
"description": "Whether the allowance is one a plan is sold on (billable) or an abuse backstop that is counted but never priced (backstop)."
},
"grace": {
"anyOf": [
{
"type": "object",
"properties": {
"startedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the grace period started."
},
"degradesAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When this product starts turning new activity away, measured in the organization's time zone."
},
"degraded": {
"type": "boolean",
"description": "Whether that has already happened."
}
},
"required": ["startedAt", "degradesAt", "degraded"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The grace period this allowance is in, or null if it is not in one. Only a billable allowance that is currently over ever carries one."
},
"period": {
"type": "string",
"enum": ["monthly", "structural"],
"description": "Whether the count covers the current UTC calendar month (monthly) or is a running total of what the account holds right now (structural)."
},
"overage": {
"anyOf": [
{
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this organization is billed for going over rather than degraded after a grace period. It is one switch for all three sold allowances, on by default."
},
"units": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How far past the allowance usage is, so far."
},
"unitsLabel": {
"type": "string",
"description": "`units` with any unit attached."
},
"blocks": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Started blocks of the rate's block size, which is what is charged for."
},
"blockSize": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many units one block covers."
},
"amount": {
"type": "number",
"description": "What this period's overage comes to so far, in whole units of the currency."
},
"amountLabel": {
"type": "string",
"description": "`amount` as money, ready to show."
},
"rateLabel": {
"type": "string",
"description": "The rate as a phrase, for example \"$10 per 1,000 review submissions\"."
},
"currency": {
"type": "string",
"description": "The ISO currency code overage is billed in."
}
},
"required": [
"enabled",
"units",
"unitsLabel",
"blocks",
"blockSize",
"amount",
"amountLabel",
"rateLabel",
"currency"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "What going over this allowance costs, and what it has cost so far this period. Null on an allowance that is not sold, and on a plan priced per contract, where overage is settled in the contract instead."
}
},
"required": [
"label",
"description",
"used",
"limit",
"usedLabel",
"limitLabel",
"status",
"group",
"grace",
"period",
"overage"
],
"additionalProperties": false,
"description": "Products stored, counted across every connected site."
},
"customers": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "The meter's name, ready to show as a row label without rewording."
},
"description": {
"type": "string",
"description": "What the meter counts, as a full sentence."
},
"used": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How much of the allowance has been used."
},
"limit": {
"type": "number",
"description": "How much this organization is allowed."
},
"usedLabel": {
"type": "string",
"description": "`used` rendered the way the limit reads it, with any unit attached."
},
"limitLabel": {
"type": "string",
"description": "`limit` rendered the way the limit reads it, with any unit attached."
},
"status": {
"type": "string",
"enum": ["ok", "warning", "over"],
"description": "Where usage stands against the allowance: ok below 80 percent, warning from 80 percent up to the allowance, over beyond it. Going over keeps working. See `grace` for what happens if it stays over."
},
"group": {
"type": "string",
"enum": ["billable", "backstop"],
"description": "Whether the allowance is one a plan is sold on (billable) or an abuse backstop that is counted but never priced (backstop)."
},
"grace": {
"anyOf": [
{
"type": "object",
"properties": {
"startedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the grace period started."
},
"degradesAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When this product starts turning new activity away, measured in the organization's time zone."
},
"degraded": {
"type": "boolean",
"description": "Whether that has already happened."
}
},
"required": ["startedAt", "degradesAt", "degraded"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The grace period this allowance is in, or null if it is not in one. Only a billable allowance that is currently over ever carries one."
},
"period": {
"type": "string",
"enum": ["monthly", "structural"],
"description": "Whether the count covers the current UTC calendar month (monthly) or is a running total of what the account holds right now (structural)."
},
"overage": {
"anyOf": [
{
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this organization is billed for going over rather than degraded after a grace period. It is one switch for all three sold allowances, on by default."
},
"units": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How far past the allowance usage is, so far."
},
"unitsLabel": {
"type": "string",
"description": "`units` with any unit attached."
},
"blocks": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Started blocks of the rate's block size, which is what is charged for."
},
"blockSize": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many units one block covers."
},
"amount": {
"type": "number",
"description": "What this period's overage comes to so far, in whole units of the currency."
},
"amountLabel": {
"type": "string",
"description": "`amount` as money, ready to show."
},
"rateLabel": {
"type": "string",
"description": "The rate as a phrase, for example \"$10 per 1,000 review submissions\"."
},
"currency": {
"type": "string",
"description": "The ISO currency code overage is billed in."
}
},
"required": [
"enabled",
"units",
"unitsLabel",
"blocks",
"blockSize",
"amount",
"amountLabel",
"rateLabel",
"currency"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "What going over this allowance costs, and what it has cost so far this period. Null on an allowance that is not sold, and on a plan priced per contract, where overage is settled in the contract instead."
}
},
"required": [
"label",
"description",
"used",
"limit",
"usedLabel",
"limitLabel",
"status",
"group",
"grace",
"period",
"overage"
],
"additionalProperties": false,
"description": "Customers stored, counted across reviews, loyalty and wishlists."
},
"sites": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "The meter's name, ready to show as a row label without rewording."
},
"description": {
"type": "string",
"description": "What the meter counts, as a full sentence."
},
"used": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How much of the allowance has been used."
},
"limit": {
"type": "number",
"description": "How much this organization is allowed."
},
"usedLabel": {
"type": "string",
"description": "`used` rendered the way the limit reads it, with any unit attached."
},
"limitLabel": {
"type": "string",
"description": "`limit` rendered the way the limit reads it, with any unit attached."
},
"status": {
"type": "string",
"enum": ["ok", "warning", "over"],
"description": "Where usage stands against the allowance: ok below 80 percent, warning from 80 percent up to the allowance, over beyond it. Going over keeps working. See `grace` for what happens if it stays over."
},
"group": {
"type": "string",
"enum": ["billable", "backstop"],
"description": "Whether the allowance is one a plan is sold on (billable) or an abuse backstop that is counted but never priced (backstop)."
},
"grace": {
"anyOf": [
{
"type": "object",
"properties": {
"startedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the grace period started."
},
"degradesAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When this product starts turning new activity away, measured in the organization's time zone."
},
"degraded": {
"type": "boolean",
"description": "Whether that has already happened."
}
},
"required": ["startedAt", "degradesAt", "degraded"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The grace period this allowance is in, or null if it is not in one. Only a billable allowance that is currently over ever carries one."
},
"period": {
"type": "string",
"enum": ["monthly", "structural"],
"description": "Whether the count covers the current UTC calendar month (monthly) or is a running total of what the account holds right now (structural)."
},
"overage": {
"anyOf": [
{
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this organization is billed for going over rather than degraded after a grace period. It is one switch for all three sold allowances, on by default."
},
"units": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How far past the allowance usage is, so far."
},
"unitsLabel": {
"type": "string",
"description": "`units` with any unit attached."
},
"blocks": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Started blocks of the rate's block size, which is what is charged for."
},
"blockSize": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many units one block covers."
},
"amount": {
"type": "number",
"description": "What this period's overage comes to so far, in whole units of the currency."
},
"amountLabel": {
"type": "string",
"description": "`amount` as money, ready to show."
},
"rateLabel": {
"type": "string",
"description": "The rate as a phrase, for example \"$10 per 1,000 review submissions\"."
},
"currency": {
"type": "string",
"description": "The ISO currency code overage is billed in."
}
},
"required": [
"enabled",
"units",
"unitsLabel",
"blocks",
"blockSize",
"amount",
"amountLabel",
"rateLabel",
"currency"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "What going over this allowance costs, and what it has cost so far this period. Null on an allowance that is not sold, and on a plan priced per contract, where overage is settled in the contract instead."
}
},
"required": [
"label",
"description",
"used",
"limit",
"usedLabel",
"limitLabel",
"status",
"group",
"grace",
"period",
"overage"
],
"additionalProperties": false,
"description": "Sites connected to the organization, whether from Shopify or created by hand."
},
"apiKeys": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "The meter's name, ready to show as a row label without rewording."
},
"description": {
"type": "string",
"description": "What the meter counts, as a full sentence."
},
"used": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How much of the allowance has been used."
},
"limit": {
"type": "number",
"description": "How much this organization is allowed."
},
"usedLabel": {
"type": "string",
"description": "`used` rendered the way the limit reads it, with any unit attached."
},
"limitLabel": {
"type": "string",
"description": "`limit` rendered the way the limit reads it, with any unit attached."
},
"status": {
"type": "string",
"enum": ["ok", "warning", "over"],
"description": "Where usage stands against the allowance: ok below 80 percent, warning from 80 percent up to the allowance, over beyond it. Going over keeps working. See `grace` for what happens if it stays over."
},
"group": {
"type": "string",
"enum": ["billable", "backstop"],
"description": "Whether the allowance is one a plan is sold on (billable) or an abuse backstop that is counted but never priced (backstop)."
},
"grace": {
"anyOf": [
{
"type": "object",
"properties": {
"startedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the grace period started."
},
"degradesAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When this product starts turning new activity away, measured in the organization's time zone."
},
"degraded": {
"type": "boolean",
"description": "Whether that has already happened."
}
},
"required": ["startedAt", "degradesAt", "degraded"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The grace period this allowance is in, or null if it is not in one. Only a billable allowance that is currently over ever carries one."
},
"period": {
"type": "string",
"enum": ["monthly", "structural"],
"description": "Whether the count covers the current UTC calendar month (monthly) or is a running total of what the account holds right now (structural)."
},
"overage": {
"anyOf": [
{
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this organization is billed for going over rather than degraded after a grace period. It is one switch for all three sold allowances, on by default."
},
"units": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How far past the allowance usage is, so far."
},
"unitsLabel": {
"type": "string",
"description": "`units` with any unit attached."
},
"blocks": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Started blocks of the rate's block size, which is what is charged for."
},
"blockSize": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many units one block covers."
},
"amount": {
"type": "number",
"description": "What this period's overage comes to so far, in whole units of the currency."
},
"amountLabel": {
"type": "string",
"description": "`amount` as money, ready to show."
},
"rateLabel": {
"type": "string",
"description": "The rate as a phrase, for example \"$10 per 1,000 review submissions\"."
},
"currency": {
"type": "string",
"description": "The ISO currency code overage is billed in."
}
},
"required": [
"enabled",
"units",
"unitsLabel",
"blocks",
"blockSize",
"amount",
"amountLabel",
"rateLabel",
"currency"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "What going over this allowance costs, and what it has cost so far this period. Null on an allowance that is not sold, and on a plan priced per contract, where overage is settled in the contract instead."
}
},
"required": [
"label",
"description",
"used",
"limit",
"usedLabel",
"limitLabel",
"status",
"group",
"grace",
"period",
"overage"
],
"additionalProperties": false,
"description": "API keys the organization holds, publishable and secret, including expired ones."
},
"seats": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "The meter's name, ready to show as a row label without rewording."
},
"description": {
"type": "string",
"description": "What the meter counts, as a full sentence."
},
"used": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How much of the allowance has been used."
},
"limit": {
"type": "number",
"description": "How much this organization is allowed."
},
"usedLabel": {
"type": "string",
"description": "`used` rendered the way the limit reads it, with any unit attached."
},
"limitLabel": {
"type": "string",
"description": "`limit` rendered the way the limit reads it, with any unit attached."
},
"status": {
"type": "string",
"enum": ["ok", "warning", "over"],
"description": "Where usage stands against the allowance: ok below 80 percent, warning from 80 percent up to the allowance, over beyond it. Going over keeps working. See `grace` for what happens if it stays over."
},
"group": {
"type": "string",
"enum": ["billable", "backstop"],
"description": "Whether the allowance is one a plan is sold on (billable) or an abuse backstop that is counted but never priced (backstop)."
},
"grace": {
"anyOf": [
{
"type": "object",
"properties": {
"startedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the grace period started."
},
"degradesAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When this product starts turning new activity away, measured in the organization's time zone."
},
"degraded": {
"type": "boolean",
"description": "Whether that has already happened."
}
},
"required": ["startedAt", "degradesAt", "degraded"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The grace period this allowance is in, or null if it is not in one. Only a billable allowance that is currently over ever carries one."
},
"period": {
"type": "string",
"enum": ["monthly", "structural"],
"description": "Whether the count covers the current UTC calendar month (monthly) or is a running total of what the account holds right now (structural)."
},
"overage": {
"anyOf": [
{
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this organization is billed for going over rather than degraded after a grace period. It is one switch for all three sold allowances, on by default."
},
"units": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How far past the allowance usage is, so far."
},
"unitsLabel": {
"type": "string",
"description": "`units` with any unit attached."
},
"blocks": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Started blocks of the rate's block size, which is what is charged for."
},
"blockSize": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many units one block covers."
},
"amount": {
"type": "number",
"description": "What this period's overage comes to so far, in whole units of the currency."
},
"amountLabel": {
"type": "string",
"description": "`amount` as money, ready to show."
},
"rateLabel": {
"type": "string",
"description": "The rate as a phrase, for example \"$10 per 1,000 review submissions\"."
},
"currency": {
"type": "string",
"description": "The ISO currency code overage is billed in."
}
},
"required": [
"enabled",
"units",
"unitsLabel",
"blocks",
"blockSize",
"amount",
"amountLabel",
"rateLabel",
"currency"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "What going over this allowance costs, and what it has cost so far this period. Null on an allowance that is not sold, and on a plan priced per contract, where overage is settled in the contract instead."
}
},
"required": [
"label",
"description",
"used",
"limit",
"usedLabel",
"limitLabel",
"status",
"group",
"grace",
"period",
"overage"
],
"additionalProperties": false,
"description": "People who are members of the organization."
},
"webhooks": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "The meter's name, ready to show as a row label without rewording."
},
"description": {
"type": "string",
"description": "What the meter counts, as a full sentence."
},
"used": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How much of the allowance has been used."
},
"limit": {
"type": "number",
"description": "How much this organization is allowed."
},
"usedLabel": {
"type": "string",
"description": "`used` rendered the way the limit reads it, with any unit attached."
},
"limitLabel": {
"type": "string",
"description": "`limit` rendered the way the limit reads it, with any unit attached."
},
"status": {
"type": "string",
"enum": ["ok", "warning", "over"],
"description": "Where usage stands against the allowance: ok below 80 percent, warning from 80 percent up to the allowance, over beyond it. Going over keeps working. See `grace` for what happens if it stays over."
},
"group": {
"type": "string",
"enum": ["billable", "backstop"],
"description": "Whether the allowance is one a plan is sold on (billable) or an abuse backstop that is counted but never priced (backstop)."
},
"grace": {
"anyOf": [
{
"type": "object",
"properties": {
"startedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the grace period started."
},
"degradesAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When this product starts turning new activity away, measured in the organization's time zone."
},
"degraded": {
"type": "boolean",
"description": "Whether that has already happened."
}
},
"required": ["startedAt", "degradesAt", "degraded"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The grace period this allowance is in, or null if it is not in one. Only a billable allowance that is currently over ever carries one."
},
"period": {
"type": "string",
"enum": ["monthly", "structural"],
"description": "Whether the count covers the current UTC calendar month (monthly) or is a running total of what the account holds right now (structural)."
},
"overage": {
"anyOf": [
{
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this organization is billed for going over rather than degraded after a grace period. It is one switch for all three sold allowances, on by default."
},
"units": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How far past the allowance usage is, so far."
},
"unitsLabel": {
"type": "string",
"description": "`units` with any unit attached."
},
"blocks": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Started blocks of the rate's block size, which is what is charged for."
},
"blockSize": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many units one block covers."
},
"amount": {
"type": "number",
"description": "What this period's overage comes to so far, in whole units of the currency."
},
"amountLabel": {
"type": "string",
"description": "`amount` as money, ready to show."
},
"rateLabel": {
"type": "string",
"description": "The rate as a phrase, for example \"$10 per 1,000 review submissions\"."
},
"currency": {
"type": "string",
"description": "The ISO currency code overage is billed in."
}
},
"required": [
"enabled",
"units",
"unitsLabel",
"blocks",
"blockSize",
"amount",
"amountLabel",
"rateLabel",
"currency"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "What going over this allowance costs, and what it has cost so far this period. Null on an allowance that is not sold, and on a plan priced per contract, where overage is settled in the contract instead."
}
},
"required": [
"label",
"description",
"used",
"limit",
"usedLabel",
"limitLabel",
"status",
"group",
"grace",
"period",
"overage"
],
"additionalProperties": false,
"description": "Webhook endpoints registered, active or not."
},
"brandAssetsMB": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "The meter's name, ready to show as a row label without rewording."
},
"description": {
"type": "string",
"description": "What the meter counts, as a full sentence."
},
"used": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How much of the allowance has been used."
},
"limit": {
"type": "number",
"description": "How much this organization is allowed."
},
"usedLabel": {
"type": "string",
"description": "`used` rendered the way the limit reads it, with any unit attached."
},
"limitLabel": {
"type": "string",
"description": "`limit` rendered the way the limit reads it, with any unit attached."
},
"status": {
"type": "string",
"enum": ["ok", "warning", "over"],
"description": "Where usage stands against the allowance: ok below 80 percent, warning from 80 percent up to the allowance, over beyond it. Going over keeps working. See `grace` for what happens if it stays over."
},
"group": {
"type": "string",
"enum": ["billable", "backstop"],
"description": "Whether the allowance is one a plan is sold on (billable) or an abuse backstop that is counted but never priced (backstop)."
},
"grace": {
"anyOf": [
{
"type": "object",
"properties": {
"startedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the grace period started."
},
"degradesAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When this product starts turning new activity away, measured in the organization's time zone."
},
"degraded": {
"type": "boolean",
"description": "Whether that has already happened."
}
},
"required": ["startedAt", "degradesAt", "degraded"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The grace period this allowance is in, or null if it is not in one. Only a billable allowance that is currently over ever carries one."
},
"period": {
"type": "string",
"enum": ["monthly", "structural"],
"description": "Whether the count covers the current UTC calendar month (monthly) or is a running total of what the account holds right now (structural)."
},
"overage": {
"anyOf": [
{
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this organization is billed for going over rather than degraded after a grace period. It is one switch for all three sold allowances, on by default."
},
"units": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How far past the allowance usage is, so far."
},
"unitsLabel": {
"type": "string",
"description": "`units` with any unit attached."
},
"blocks": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Started blocks of the rate's block size, which is what is charged for."
},
"blockSize": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many units one block covers."
},
"amount": {
"type": "number",
"description": "What this period's overage comes to so far, in whole units of the currency."
},
"amountLabel": {
"type": "string",
"description": "`amount` as money, ready to show."
},
"rateLabel": {
"type": "string",
"description": "The rate as a phrase, for example \"$10 per 1,000 review submissions\"."
},
"currency": {
"type": "string",
"description": "The ISO currency code overage is billed in."
}
},
"required": [
"enabled",
"units",
"unitsLabel",
"blocks",
"blockSize",
"amount",
"amountLabel",
"rateLabel",
"currency"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "What going over this allowance costs, and what it has cost so far this period. Null on an allowance that is not sold, and on a plan priced per contract, where overage is settled in the contract instead."
}
},
"required": [
"label",
"description",
"used",
"limit",
"usedLabel",
"limitLabel",
"status",
"group",
"grace",
"period",
"overage"
],
"additionalProperties": false,
"description": "Brand assets held by the fullest single site, in megabytes, since the allowance is per site."
},
"totalStorageMB": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "The meter's name, ready to show as a row label without rewording."
},
"description": {
"type": "string",
"description": "What the meter counts, as a full sentence."
},
"used": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How much of the allowance has been used."
},
"limit": {
"type": "number",
"description": "How much this organization is allowed."
},
"usedLabel": {
"type": "string",
"description": "`used` rendered the way the limit reads it, with any unit attached."
},
"limitLabel": {
"type": "string",
"description": "`limit` rendered the way the limit reads it, with any unit attached."
},
"status": {
"type": "string",
"enum": ["ok", "warning", "over"],
"description": "Where usage stands against the allowance: ok below 80 percent, warning from 80 percent up to the allowance, over beyond it. Going over keeps working. See `grace` for what happens if it stays over."
},
"group": {
"type": "string",
"enum": ["billable", "backstop"],
"description": "Whether the allowance is one a plan is sold on (billable) or an abuse backstop that is counted but never priced (backstop)."
},
"grace": {
"anyOf": [
{
"type": "object",
"properties": {
"startedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the grace period started."
},
"degradesAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When this product starts turning new activity away, measured in the organization's time zone."
},
"degraded": {
"type": "boolean",
"description": "Whether that has already happened."
}
},
"required": ["startedAt", "degradesAt", "degraded"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The grace period this allowance is in, or null if it is not in one. Only a billable allowance that is currently over ever carries one."
},
"period": {
"type": "string",
"enum": ["monthly", "structural"],
"description": "Whether the count covers the current UTC calendar month (monthly) or is a running total of what the account holds right now (structural)."
},
"overage": {
"anyOf": [
{
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether this organization is billed for going over rather than degraded after a grace period. It is one switch for all three sold allowances, on by default."
},
"units": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How far past the allowance usage is, so far."
},
"unitsLabel": {
"type": "string",
"description": "`units` with any unit attached."
},
"blocks": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Started blocks of the rate's block size, which is what is charged for."
},
"blockSize": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many units one block covers."
},
"amount": {
"type": "number",
"description": "What this period's overage comes to so far, in whole units of the currency."
},
"amountLabel": {
"type": "string",
"description": "`amount` as money, ready to show."
},
"rateLabel": {
"type": "string",
"description": "The rate as a phrase, for example \"$10 per 1,000 review submissions\"."
},
"currency": {
"type": "string",
"description": "The ISO currency code overage is billed in."
}
},
"required": [
"enabled",
"units",
"unitsLabel",
"blocks",
"blockSize",
"amount",
"amountLabel",
"rateLabel",
"currency"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "What going over this allowance costs, and what it has cost so far this period. Null on an allowance that is not sold, and on a plan priced per contract, where overage is settled in the contract instead."
}
},
"required": [
"label",
"description",
"used",
"limit",
"usedLabel",
"limitLabel",
"status",
"group",
"grace",
"period",
"overage"
],
"additionalProperties": false,
"description": "Uploaded files across the whole organization, in megabytes."
}
},
"required": [
"monthlySurveys",
"monthlyLoyaltyMAU",
"monthlyWishlistMAU",
"monthlyReviewSubmissions",
"monthlyReviewAnswers",
"products",
"customers",
"sites",
"apiKeys",
"seats",
"webhooks",
"brandAssetsMB",
"totalStorageMB"
],
"additionalProperties": false,
"description": "The current organization's usage."
}