#Loyalty stamps
POST /loyalty-stamps
Award stamps on a card type that earns them from your own code, which is what a custom earning type means. The stamps behave exactly like order-driven ones: stampsEarned rules fire, and the card completes and carries overflow the same way. Sending the same idempotencyKey twice returns the same card without stamping it again. Triggers the stampCard.updated webhook, plus stampCard.completed if the award fills the card.
Requires the loyalty:write permission.
Body Parameters
dataobjectrequiredThe stamps to award.
Request
curl \
-X POST \
"https://api.cascade.dev/loyalty-stamps" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"stampCardTypeId": "550e8400-e29b-41d4-a716-446655440000",
"customerEmail": "[email protected]",
"count": 0,
"idempotencyKey": "string",
"createCustomer": true
}
}'Request
import { client } from "@cascade-commerce/api/admin/client"
import { postLoyaltyStamps } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postLoyaltyStamps({
body: {
data: {
stampCardTypeId: "550e8400-e29b-41d4-a716-446655440000",
customerEmail: "[email protected]",
count: 0,
idempotencyKey: "string",
createCustomer: true,
},
},
})Request
require "net/http"
uri = URI("https://api.cascade.dev/loyalty-stamps")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": {
"stampCardTypeId": "550e8400-e29b-41d4-a716-446655440000",
"customerEmail": "[email protected]",
"count": 0,
"idempotencyKey": "string",
"createCustomer": true
}
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyRequest
import json
from urllib.request import Request, urlopen
body = """{
"data": {
"stampCardTypeId": "550e8400-e29b-41d4-a716-446655440000",
"customerEmail": "[email protected]",
"count": 0,
"idempotencyKey": "string",
"createCustomer": true
}
}"""
req = Request(
"https://api.cascade.dev/loyalty-stamps",
data=body.encode(),
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res))Response
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"stampCardTypeId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"stampCount": 0,
"completedAt": "2025-01-15T09:30:00Z",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"awarded": true,
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"completedStampCardIds": ["550e8400-e29b-41d4-a716-446655440000"]
}Response schema
{
"$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 stamp card."
},
"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 this card is an instance of."
},
"loyaltyMembershipId": {
"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 membership the card belongs to."
},
"stampCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many stamps the customer has collected so far."
},
"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 card was filled. Null while the card is still 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 was started."
},
"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 was last stamped or changed."
}
},
"required": [
"id",
"stampCardTypeId",
"loyaltyMembershipId",
"stampCount",
"completedAt",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The card the stamps landed on, after the award."
},
"awarded": {
"type": "boolean",
"description": "False when the idempotency key had already been used, so nothing changed."
},
"loyaltyMembershipId": {
"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 membership the card belongs to."
},
"completedStampCardIds": {
"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)$"
},
"description": "Cards this award filled, oldest first. Empty when nothing completed."
}
},
"required": ["data", "awarded", "loyaltyMembershipId", "completedStampCardIds"],
"additionalProperties": false
}