HTTP endpoints
revokeMilestone
Reverses every reward the person earned for the milestone. A reward you already delivered becomes partner debt. The milestone earns nothing for this person until you reinstate it.
POST /api/v1/milestones/revoke
Requires events:write. Keep the Bearer key on your server.
SDK
grizzlee.revokeMilestone(input: { customerId: string; reason: string; milestone: string; eventId: string; }): Promise<MilestoneResult>HTTP
Use your backend's HTTP client. The cURL request shows the wire format. Replace path parameters and example values.
curl --fail-with-body --request POST \
'https://app.grizzlee.ca/api/v1/milestones/revoke' \
-H "Authorization: Bearer $GRIZZLEE_SERVER_KEY" \
-H 'Content-Type: application/json' \
--data '{"eventId":"payment-example","customerId":"customer-example","milestone":"first_booking","reason":"Merchant review"}'Request body
| Field | Type | Required | Details |
|---|---|---|---|
eventId | string | Yes | |
customerId | string | Yes | |
milestone | string | Yes | |
reason | string | Yes |
Request schema
{
"type": "object",
"properties": {
"eventId": {
"type": "string",
"minLength": 1,
"maxLength": 120
},
"customerId": {
"type": "string",
"minLength": 1,
"maxLength": 120
},
"milestone": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]{0,79}$"
},
"reason": {
"type": "string",
"minLength": 1,
"maxLength": 500
}
},
"required": [
"eventId",
"customerId",
"milestone",
"reason"
],
"additionalProperties": false
}Response
HTTP responses wrap this value in data. The SDK returns the value directly unless the note above describes a wrapper.
| Field | Type | Required | Details |
|---|---|---|---|
eventId | string | Yes | |
customerId | string | Yes | |
milestone | string | Yes | |
duplicate | boolean | Yes | True when this event ID was already processed with the same data. |
status | union | Yes | |
decisions | array | Yes | Reports: one entry per evaluated reward rule in each active program. Empty otherwise. |
rewardIds | array | Yes | Reports: rewards this report created. Revocations, reinstatements and reversals: every reward for this person and milestone. |
reversalCents | number | Yes | Revocations and reversals: reward value this event reversed. 0 for reports and reinstatements. |
restoredCents | number | No | Reinstatements and reports: reward value this event gave back. A report gives value back when it brings the count up to a reward's threshold again. Absent on revocations, reversals and reports saved before milestones were counted. |
count | number | No | The person's reports of this milestone that are not reversed, from any time and including this event's. Each decision's milestoneCount is the part that counts for its program. Absent on results saved before milestones were counted. |
reportEventId | string | No | Reversals: the eventId of the report this event took back. |
status
Variant 1
Type: string.
Variant 2
Type: string.
Variant 3
Type: string.
Variant 4
Type: string.
decisions[]
| Field | Type | Required | Details |
|---|---|---|---|
decisions.programId | string | Yes | |
decisions.programName | string | Yes | |
decisions.partnerId | string | No | |
decisions.outcome | union | Yes | |
decisions.reason | string | Yes | |
decisions.reasonParts | array | No | reason as codes. Absent on decisions made before codes existed. |
decisions.amountCents | number | Yes | |
decisions.selfReferral | object | No | Present when the customer's identity signals matched the partner's. blocked comes with a skipped outcome. review comes with a reward held as pending. flagged comes with a reward that follows the program's usual approval. |
decisions.fraud | object | No | Present when fraud rules fired. blocked comes with a skipped outcome. review comes with a reward held as pending. flagged comes with a reward that follows the program's usual approval. |
decisions.milestoneCount | number | No | Milestone decisions that reached the count check: the person's reports that count for this program, at or after the referral and not reversed, this report included. |
decisions.milestoneThreshold | number | No | The effective bonus count, or 1 for each-report-only programs. |
decisions.outcome
Variant 1
Type: string.
Variant 2
Type: string.
decisions.reasonParts[]
| Field | Type | Required | Details |
|---|---|---|---|
decisions.reasonParts.code | union | Yes | |
decisions.reasonParts.params | object | No |
decisions.reasonParts.code
Variant 1
Type: string.
Variant 2
Type: string.
Variant 3
Type: string.
Variant 4
Type: string.
Variant 5
Type: string.
Variant 6
Type: string.
Variant 7
Type: string.
Variant 8
Type: string.
Variant 9
Type: string.
Variant 10
Type: string.
Variant 11
Type: string.
Variant 12
Type: string.
Variant 13
Type: string.
Variant 14
Type: string.
Variant 15
Type: string.
Variant 16
Type: string.
Variant 17
Type: string.
Variant 18
Type: string.
Variant 19
Type: string.
Variant 20
Type: string.
Variant 21
Type: string.
Variant 22
Type: string.
Variant 23
Type: string.
Variant 24
Type: string.
Variant 25
Type: string.
Variant 26
Type: string.
Variant 27
Type: string.
Variant 28
Type: string.
Variant 29
Type: string.
Variant 30
Type: string.
Variant 31
Type: string.
Variant 32
Type: string.
Variant 33
Type: string.
Variant 34
Type: string.
Variant 35
Type: string.
Variant 36
Type: string.
Variant 37
Type: string.
Variant 38
Type: string.
decisions.reasonParts.params
Type: object.
decisions.selfReferral
| Field | Type | Required | Details |
|---|---|---|---|
decisions.selfReferral.action | union | Yes | |
decisions.selfReferral.signals | array | Yes |
decisions.selfReferral.action
Variant 1
Type: string.
Variant 2
Type: string.
Variant 3
Type: string.
decisions.selfReferral.signals[]
Variant 1
Type: string.
Variant 2
Type: string.
Variant 3
Type: string.
Variant 4
Type: string.
Variant 5
Type: string.
Variant 6
Type: string.
decisions.fraud
| Field | Type | Required | Details |
|---|---|---|---|
decisions.fraud.action | union | Yes | |
decisions.fraud.rules | array | Yes |
decisions.fraud.action
Variant 1
Type: string.
Variant 2
Type: string.
Variant 3
Type: string.
decisions.fraud.rules[]
| Field | Type | Required | Details |
|---|---|---|---|
decisions.fraud.rules.action | union | Yes | |
decisions.fraud.rules.rule | union | Yes | |
decisions.fraud.rules.signals | array | Yes | Matching signal types, never values. Empty for disposable_email. |
decisions.fraud.rules.count | number | No | Customers counted, this one included when referred inside the window. Velocity rules only. |
decisions.fraud.rules.action
Variant 1
Type: string.
Variant 2
Type: string.
Variant 3
Type: string.
decisions.fraud.rules.rule
Variant 1
Type: string.
Variant 2
Type: string.
Variant 3
Type: string.
Variant 4
Type: string.
decisions.fraud.rules.signals[]
Variant 1
Type: string.
Variant 2
Type: string.
Variant 3
Type: string.
Variant 4
Type: string.
Variant 5
Type: string.
Response schema
{
"type": "object",
"properties": {
"eventId": {
"type": "string"
},
"customerId": {
"type": "string"
},
"milestone": {
"type": "string"
},
"duplicate": {
"type": "boolean",
"description": "True when this event ID was already processed with the same data."
},
"status": {
"anyOf": [
{
"type": "string",
"const": "recorded"
},
{
"type": "string",
"const": "revoked"
},
{
"type": "string",
"const": "reinstated"
},
{
"type": "string",
"const": "reversed"
}
]
},
"decisions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"programId": {
"type": "string"
},
"programName": {
"type": "string"
},
"partnerId": {
"type": "string"
},
"outcome": {
"anyOf": [
{
"type": "string",
"const": "skipped"
},
{
"type": "string",
"const": "rewarded"
}
]
},
"reason": {
"type": "string"
},
"reasonParts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"code": {
"anyOf": [
{
"type": "string",
"const": "fixed"
},
{
"type": "string",
"const": "no_referral"
},
{
"type": "string",
"const": "percentage"
},
{
"type": "string",
"const": "offer"
},
{
"type": "string",
"const": "report"
},
{
"type": "string",
"const": "report_percentage"
},
{
"type": "string",
"const": "milestone_bonus"
},
{
"type": "string",
"const": "schedule_month"
},
{
"type": "string",
"const": "tier_bonus"
},
{
"type": "string",
"const": "custom_partner_terms"
},
{
"type": "string",
"const": "custom_customer_terms"
},
{
"type": "string",
"const": "capped"
},
{
"type": "string",
"const": "no_order_referral"
},
{
"type": "string",
"const": "no_customer_referral"
},
{
"type": "string",
"const": "partner_not_in_program"
},
{
"type": "string",
"const": "self_referral_blocked"
},
{
"type": "string",
"const": "fraud_blocked"
},
{
"type": "string",
"const": "milestone_before_referral"
},
{
"type": "string",
"const": "order_before_referral"
},
{
"type": "string",
"const": "report_value_missing"
},
{
"type": "string",
"const": "milestone_window_expired"
},
{
"type": "string",
"const": "order_window_expired"
},
{
"type": "string",
"const": "milestone_progress"
},
{
"type": "string",
"const": "report_limit_reached"
},
{
"type": "string",
"const": "milestone_already_rewarded"
},
{
"type": "string",
"const": "report_value_no_reward"
},
{
"type": "string",
"const": "customer_cap_reached"
},
{
"type": "string",
"const": "partner_cap_reached"
},
{
"type": "string",
"const": "category_excluded"
},
{
"type": "string",
"const": "categories_missing"
},
{
"type": "string",
"const": "categories_required"
},
{
"type": "string",
"const": "first_order_only"
},
{
"type": "string",
"const": "first_order_after_referral_only"
},
{
"type": "string",
"const": "first_orders_only"
},
{
"type": "string",
"const": "first_orders_after_referral_only"
},
{
"type": "string",
"const": "duration_ended"
},
{
"type": "string",
"const": "rate_no_reward"
},
{
"type": "string",
"const": "order_no_reward"
}
]
},
"params": {
"type": "object",
"properties": {},
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
}
}
},
"required": [
"code"
]
},
"description": "reason as codes. Absent on decisions made before codes existed."
},
"amountCents": {
"type": "number"
},
"selfReferral": {
"type": "object",
"properties": {
"action": {
"anyOf": [
{
"type": "string",
"const": "blocked"
},
{
"type": "string",
"const": "review"
},
{
"type": "string",
"const": "flagged"
}
]
},
"signals": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string",
"const": "email"
},
{
"type": "string",
"const": "phone"
},
{
"type": "string",
"const": "card"
},
{
"type": "string",
"const": "device"
},
{
"type": "string",
"const": "ip"
},
{
"type": "string",
"const": "customer_id"
}
]
}
}
},
"required": [
"action",
"signals"
],
"description": "Present when the customer's identity signals matched the partner's. blocked comes with a\nskipped outcome. review comes with a reward held as pending. flagged comes with a reward that\nfollows the program's usual approval."
},
"fraud": {
"type": "object",
"properties": {
"action": {
"anyOf": [
{
"type": "string",
"const": "blocked"
},
{
"type": "string",
"const": "review"
},
{
"type": "string",
"const": "flagged"
}
]
},
"rules": {
"type": "array",
"items": {
"type": "object",
"properties": {
"action": {
"anyOf": [
{
"type": "string",
"const": "blocked"
},
{
"type": "string",
"const": "review"
},
{
"type": "string",
"const": "flagged"
}
]
},
"rule": {
"anyOf": [
{
"type": "string",
"const": "duplicate_accounts"
},
{
"type": "string",
"const": "rapid_repeats"
},
{
"type": "string",
"const": "repeat_customer"
},
{
"type": "string",
"const": "disposable_email"
}
]
},
"signals": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string",
"const": "email"
},
{
"type": "string",
"const": "phone"
},
{
"type": "string",
"const": "card"
},
{
"type": "string",
"const": "device"
},
{
"type": "string",
"const": "ip"
}
]
},
"description": "Matching signal types, never values. Empty for disposable_email."
},
"count": {
"type": "number",
"description": "Customers counted, this one included when referred inside the window. Velocity rules only."
}
},
"required": [
"action",
"rule",
"signals"
]
}
}
},
"required": [
"action",
"rules"
],
"description": "Present when fraud rules fired. blocked comes with a skipped outcome. review comes with a\nreward held as pending. flagged comes with a reward that follows the program's usual approval."
},
"milestoneCount": {
"type": "number",
"description": "Milestone decisions that reached the count check: the person's reports that count for this\nprogram, at or after the referral and not reversed, this report included."
},
"milestoneThreshold": {
"type": "number",
"description": "The effective bonus count, or 1 for each-report-only programs."
}
},
"required": [
"programId",
"programName",
"outcome",
"reason",
"amountCents"
]
},
"description": "Reports: one entry per evaluated reward rule in each active program. Empty otherwise."
},
"rewardIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Reports: rewards this report created. Revocations, reinstatements and reversals: every reward\nfor this person and milestone."
},
"reversalCents": {
"type": "number",
"description": "Revocations and reversals: reward value this event reversed. 0 for reports and\nreinstatements."
},
"restoredCents": {
"type": "number",
"description": "Reinstatements and reports: reward value this event gave back. A report gives value back when\nit brings the count up to a reward's threshold again. Absent on revocations, reversals and\nreports saved before milestones were counted."
},
"count": {
"type": "number",
"description": "The person's reports of this milestone that are not reversed, from any time and including\nthis event's. Each decision's milestoneCount is the part that counts for its program. Absent\non results saved before milestones were counted."
},
"reportEventId": {
"type": "string",
"description": "Reversals: the eventId of the report this event took back."
}
},
"required": [
"eventId",
"customerId",
"milestone",
"duplicate",
"status",
"decisions",
"rewardIds",
"reversalCents"
]
}Errors
Failures return { "error": { "code": "...", "message": "...", "details": {} } }. Details are optional. See error statuses. Reuse an unchanged event payload when retrying.