Skip to content
Developer docs
⌘ K
Dashboard ↗

Webhook events

milestone.reported

Sent once for each new milestone report, rewarded or not.

Example payload

This illustrative payload follows the current event validator. Amounts use minor units.

json
{
  "id": "00000000-0000-4000-8000-000000000001",
  "companyId": "company-example",
  "type": "milestone.reported",
  "data": {
    "partners": [
      {
        "partnerId": "partner-example",
        "partnerExternalId": "merchant-partner-001"
      }
    ],
    "eventId": "payment-example",
    "customerId": "customer-example",
    "milestone": "first_booking",
    "status": "recorded",
    "programIds": [
      "program-example"
    ],
    "rewardIds": [
      "reward-example"
    ],
    "occurredAt": "2026-10-11T12:00:00.000Z",
    "decisions": [
      {
        "programId": "program-example",
        "programName": "programName-example",
        "outcome": "rewarded",
        "reason": "Merchant review",
        "amountCents": 500
      }
    ]
  },
  "createdAt": "2026-10-11T12:00:00.000Z"
}

Payload fields

FieldTypeRequiredDetails
partnersarrayYes
partnerIdstringNo
partnerExternalIdstringNo
eventIdstringYes
customerIdstringYes
milestonestringYes
status"recorded", "revoked", "reinstated", "reversed"Yes
programIdsarrayYes
rewardIdsarrayYes
countintegerNominimum 0
valueCentsintegerNominimum 0. maximum 1000000000000
occurredAtstringYesdate-time
decisionsarrayYes
restoredCentsintegerNominimum 0. maximum 9007199254740991

partners[]

FieldTypeRequiredDetails
partners.partnerIdstringYes
partners.partnerExternalIdstringYes

decisions[]

FieldTypeRequiredDetails
decisions.programIdstringYes
decisions.programNamestringYes
decisions.partnerIdstringNo
decisions.outcome"rewarded", "skipped"Yes
decisions.reasonstringYes
decisions.reasonPartsarrayNo
decisions.amountCentsintegerYesminimum 0. maximum 1000000000000
decisions.milestoneCountintegerNominimum 0
decisions.milestoneThresholdintegerNominimum 1. maximum 10000

decisions.reasonParts[]

FieldTypeRequiredDetails
decisions.reasonParts.codestringYes
decisions.reasonParts.paramsobjectNo

Payload schema

json
{
  "type": "object",
  "properties": {
    "partners": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "partnerId": {
            "type": "string",
            "minLength": 1
          },
          "partnerExternalId": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "partnerId",
          "partnerExternalId"
        ],
        "additionalProperties": true
      }
    },
    "partnerId": {
      "type": "string",
      "minLength": 1
    },
    "partnerExternalId": {
      "type": "string",
      "minLength": 1
    },
    "eventId": {
      "type": "string",
      "minLength": 1
    },
    "customerId": {
      "type": "string",
      "minLength": 1
    },
    "milestone": {
      "type": "string",
      "minLength": 1
    },
    "status": {
      "type": "string",
      "enum": [
        "recorded",
        "revoked",
        "reinstated",
        "reversed"
      ]
    },
    "programIds": {
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      }
    },
    "rewardIds": {
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      }
    },
    "count": {
      "type": "integer",
      "minimum": 0
    },
    "valueCents": {
      "type": "integer",
      "minimum": 0,
      "maximum": 1000000000000
    },
    "occurredAt": {
      "type": "string",
      "format": "date-time"
    },
    "decisions": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "programId": {
            "type": "string",
            "minLength": 1
          },
          "programName": {
            "type": "string"
          },
          "partnerId": {
            "type": "string",
            "minLength": 1
          },
          "outcome": {
            "type": "string",
            "enum": [
              "rewarded",
              "skipped"
            ]
          },
          "reason": {
            "type": "string"
          },
          "reasonParts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "minLength": 1
                },
                "params": {
                  "type": "object",
                  "additionalProperties": {}
                }
              },
              "required": [
                "code"
              ],
              "additionalProperties": true
            }
          },
          "amountCents": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1000000000000
          },
          "milestoneCount": {
            "type": "integer",
            "minimum": 0
          },
          "milestoneThreshold": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10000
          }
        },
        "required": [
          "programId",
          "programName",
          "outcome",
          "reason",
          "amountCents"
        ],
        "additionalProperties": true
      }
    },
    "restoredCents": {
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "partners",
    "eventId",
    "customerId",
    "milestone",
    "status",
    "programIds",
    "rewardIds",
    "occurredAt",
    "decisions"
  ],
  "additionalProperties": true
}

Handle delivery

Verify the signature over the raw body before parsing JSON. Check that the body ID matches Grizzlee-Event-Id. Process each ID once, since delivery can repeat.