# Export KINTRA_API for your environment first; the base URL is on the introduction page.
KEY="YOUR_TENANT_KEY_ID"
SECRET="YOUR_SIGNING_SECRET"
# The signature covers the path and query exactly as sent, so both are split
# off the base URL and reused for the request line below.
API_PATH="/${KINTRA_API#*://*/}"
API_HOST="${KINTRA_API%"$API_PATH"}"
METHOD="PUT"
PATH_AND_QUERY="$API_PATH/gateway/rewards/YOUR_REWARD_ID"
# One compact line, signed and sent unchanged. The server signs a
# re-serialization of the body it parsed, so reformatting this fails with a 401.
BODY='{"title":"YOUR_TITLE","description":"YOUR_DESCRIPTION","xp_cost":0,"is_active":false,"metadata":{},"status":"initialized","min_tier_id":"YOUR_MIN_TIER_ID"}'
TIMESTAMP="$(date +%s)"
SIGNATURE="$(printf '%s' "$TIMESTAMP$METHOD$PATH_AND_QUERY$BODY" \
| openssl dgst -sha256 -hmac "$SECRET" | awk '{print $NF}')"
curl -sS -w '\n%{http_code}' -X "$METHOD" "$API_HOST$PATH_AND_QUERY" \
-H "X-Tenant-Key: $KEY" \
-H "X-Tenant-Timestamp: $TIMESTAMP" \
-H "X-Tenant-Signature: $SIGNATURE" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: YOUR_IDEMPOTENCY_KEY" \
--data-binary "$BODY"const options = {
method: 'PUT',
headers: {
'X-Tenant-Signature': '<x-tenant-signature>',
'X-Tenant-Timestamp': '<x-tenant-timestamp>',
'X-Idempotency-Key': '<x-idempotency-key>',
'x-tenant-key': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
description: '<string>',
is_active: true,
metadata: {},
min_tier_id: '<string>',
title: '<string>',
xp_cost: 123
})
};
fetch('https://api.kintra.io/api/v1/gateway/rewards/{rewardId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.kintra.io/api/v1/gateway/rewards/{rewardId}"
payload = {
"description": "<string>",
"is_active": True,
"metadata": {},
"min_tier_id": "<string>",
"title": "<string>",
"xp_cost": 123
}
headers = {
"X-Tenant-Signature": "<x-tenant-signature>",
"X-Tenant-Timestamp": "<x-tenant-timestamp>",
"X-Idempotency-Key": "<x-idempotency-key>",
"x-tenant-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text){
"success": true,
"data": {
"success": true
},
"errors": [
{
"message": "<string>",
"path": "<string>"
}
],
"message": "<string>"
}{
"success": true,
"data": null,
"errors": [
{
"message": "<string>",
"path": "<string>"
}
],
"message": "<string>"
}{
"success": true,
"data": null,
"errors": [
{
"message": "<string>",
"path": "<string>"
}
],
"message": "<string>"
}{
"success": true,
"data": null,
"errors": [
{
"message": "<string>",
"path": "<string>"
}
],
"message": "<string>"
}Update a reward
Updates a reward in place. Every field is optional: send title, description, xp_cost, is_active, metadata, min_tier_id or status, and leave out what you are not changing. This is the route that moves a reward from initialized to published, and the route that flips is_active. Publishing is a one-way move: once a reward is published, is_active is the only field still mutable and it has to be the only field in the body, so an update that carries anything else returns 400 REWARD_PUBLISHED. The response data carries success. Returns 404 when no reward with that id belongs to your tenant. X-Idempotency-Key is required: a missing key returns 400 IDEMPOTENCY_KEY_REQUIRED, and a replay returns the stored response. Reusing a key with a different request returns 422 IDEMPOTENCY_KEY_CONFLICT, and replaying a key whose first request is still in flight returns 409 with the same code. A first request that never recorded a response keeps returning 409 until the key ages out, so send a fresh key rather than retrying that one.
# Export KINTRA_API for your environment first; the base URL is on the introduction page.
KEY="YOUR_TENANT_KEY_ID"
SECRET="YOUR_SIGNING_SECRET"
# The signature covers the path and query exactly as sent, so both are split
# off the base URL and reused for the request line below.
API_PATH="/${KINTRA_API#*://*/}"
API_HOST="${KINTRA_API%"$API_PATH"}"
METHOD="PUT"
PATH_AND_QUERY="$API_PATH/gateway/rewards/YOUR_REWARD_ID"
# One compact line, signed and sent unchanged. The server signs a
# re-serialization of the body it parsed, so reformatting this fails with a 401.
BODY='{"title":"YOUR_TITLE","description":"YOUR_DESCRIPTION","xp_cost":0,"is_active":false,"metadata":{},"status":"initialized","min_tier_id":"YOUR_MIN_TIER_ID"}'
TIMESTAMP="$(date +%s)"
SIGNATURE="$(printf '%s' "$TIMESTAMP$METHOD$PATH_AND_QUERY$BODY" \
| openssl dgst -sha256 -hmac "$SECRET" | awk '{print $NF}')"
curl -sS -w '\n%{http_code}' -X "$METHOD" "$API_HOST$PATH_AND_QUERY" \
-H "X-Tenant-Key: $KEY" \
-H "X-Tenant-Timestamp: $TIMESTAMP" \
-H "X-Tenant-Signature: $SIGNATURE" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: YOUR_IDEMPOTENCY_KEY" \
--data-binary "$BODY"const options = {
method: 'PUT',
headers: {
'X-Tenant-Signature': '<x-tenant-signature>',
'X-Tenant-Timestamp': '<x-tenant-timestamp>',
'X-Idempotency-Key': '<x-idempotency-key>',
'x-tenant-key': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
description: '<string>',
is_active: true,
metadata: {},
min_tier_id: '<string>',
title: '<string>',
xp_cost: 123
})
};
fetch('https://api.kintra.io/api/v1/gateway/rewards/{rewardId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.kintra.io/api/v1/gateway/rewards/{rewardId}"
payload = {
"description": "<string>",
"is_active": True,
"metadata": {},
"min_tier_id": "<string>",
"title": "<string>",
"xp_cost": 123
}
headers = {
"X-Tenant-Signature": "<x-tenant-signature>",
"X-Tenant-Timestamp": "<x-tenant-timestamp>",
"X-Idempotency-Key": "<x-idempotency-key>",
"x-tenant-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text){
"success": true,
"data": {
"success": true
},
"errors": [
{
"message": "<string>",
"path": "<string>"
}
],
"message": "<string>"
}{
"success": true,
"data": null,
"errors": [
{
"message": "<string>",
"path": "<string>"
}
],
"message": "<string>"
}{
"success": true,
"data": null,
"errors": [
{
"message": "<string>",
"path": "<string>"
}
],
"message": "<string>"
}{
"success": true,
"data": null,
"errors": [
{
"message": "<string>",
"path": "<string>"
}
],
"message": "<string>"
}Authorizations
Tenant API key
Headers
Hex HMAC-SHA256 of the timestamp, the uppercase method, the url as sent and the request body, keyed by the signing secret.
Unix seconds. Checked before the signature.
Unique key for this write. A replay returns the stored response; the same key with a different body conflicts.
Path Parameters
Body
Visibility/redeemability flag. This is the only reward field mutable after publish.
Freeform reward metadata (full-replace of the jsonb on update). May carry a tenant-scoped
image_key; any client-supplied image_url is rejected — the URL is derived on read.
Show child attributes
Show child attributes
Tier gate — UUID of the minimum tier, or ''/null to clear (all tiers).
initialized, published 
