HTTP endpoints
completeOrder
Retain qualified customer attribution or claim the newest valid candidate. Consume an optional friend or earned offer reservation and record payment atomically. Report the server-priced discount in incentiveAppliedCents. An earned hold keeps its copied terms even if the earning reward was later reversed. Without a hold, an applied discount can use an available earned offer and is flagged no_reservation. With newCustomer from your own history, report referred purchases and purchases that use earned offers. Otherwise report every completed order and import earlier purchases. The result has Grizzlee's customerOrderNumber and, when the discount did not match the reservation, incentiveFlag. signals are the customer's identity signals for the self-referral check.
POST /api/v1/orders/complete
Requires events:write. Keep the Bearer key on your server.
Claiming candidates, including an order's saved selection, also requires partners:write. Your server supplies payment amounts and the authenticated customer's ID.
SDK
grizzlee.completeOrder(event: { type: "order.completed"; customerId: string; orderId: string; occurredAt: string; eventId: string; commissionableCents: number; currency: "CAD" | "USD" | "EUR" | "GBP"; customerDisplayName?: string | undefined; newCustomer?: boolean | undefined; categories?: string[] | undefined; customerOrderNumber?: number | undefined; totalCents?: number | undefined; customerEmail?: string | undefined; incentiveAppliedCents?: number | undefined; }, options?: { candidates?: ({ type: "receipt"; receipt: string; } | { type: "code"; referralCode: string; programId?: string | undefined; providerPromoCodeId?: string | undefined; enteredAt?: string | undefined; })[] | undefined; reservationId?: string | undefined; signals?: { email?: string | undefined; phone?: string | undefined; phoneHash?: string | undefined; paymentFingerprint?: string | undefined; deviceId?: string | undefined; ipAddress?: string | undefined; } | undefined; } | undefined): Promise<EventResult>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/orders/complete' \
-H "Authorization: Bearer $GRIZZLEE_SERVER_KEY" \
-H 'Content-Type: application/json' \
--data '{"event":{"type":"order.completed","eventId":"payment-example","orderId":"order-example","customerId":"customer-example","currency":"CAD","totalCents":10000,"commissionableCents":10000,"occurredAt":"2026-10-11T12:00:00.000Z"}}'Request body
| Field | Type | Required | Details |
|---|---|---|---|
event | object | Yes | |
candidates | array | No | |
reservationId | string | No | |
signals | object | No |
event
| Field | Type | Required | Details |
|---|---|---|---|
event.type | "order.completed" | Yes | |
event.eventId | string | Yes | |
event.orderId | string | Yes | |
event.customerId | string | Yes | |
event.customerDisplayName | string | No | |
event.customerEmail | string | No | |
event.newCustomer | boolean | No | |
event.customerOrderNumber | integer | No | minimum 1. maximum 1000000 |
event.currency | "CAD", "USD", "EUR", "GBP" | Yes | |
event.totalCents | integer | No | minimum 0. maximum 1000000000000 |
event.commissionableCents | integer | Yes | minimum 0. maximum 1000000000000 |
event.incentiveAppliedCents | integer | No | minimum 0. maximum 1000000000000 |
event.categories | array | No | |
event.occurredAt | string | Yes | date-time |
event.categories[]
Type: string.
Type: string.
candidates[]
Variant 1
| Field | Type | Required | Details |
|---|---|---|---|
candidates.type | "receipt" | Yes | |
candidates.receipt | string | Yes |
Variant 2
| Field | Type | Required | Details |
|---|---|---|---|
candidates.type | "code" | Yes | |
candidates.referralCode | string | Yes | |
candidates.programId | string | No | |
candidates.providerPromoCodeId | string | No | |
candidates.enteredAt | string | No | date-time |
signals
| Field | Type | Required | Details |
|---|---|---|---|
signals.email | string | No | |
signals.phone | string | No | |
signals.phoneHash | string | No | |
signals.paymentFingerprint | string | No | |
signals.deviceId | string | No | |
signals.ipAddress | string | No |
Request schema
{
"type": "object",
"properties": {
"event": {
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "order.completed"
},
"eventId": {
"type": "string",
"minLength": 1,
"maxLength": 120
},
"orderId": {
"type": "string",
"minLength": 1,
"maxLength": 120
},
"customerId": {
"type": "string",
"minLength": 1,
"maxLength": 120
},
"customerDisplayName": {
"type": "string",
"minLength": 1,
"maxLength": 80
},
"customerEmail": {
"type": "string",
"format": "email",
"maxLength": 254
},
"newCustomer": {
"type": "boolean"
},
"customerOrderNumber": {
"type": "integer",
"minimum": 1,
"maximum": 1000000
},
"currency": {
"type": "string",
"enum": [
"CAD",
"USD",
"EUR",
"GBP"
]
},
"totalCents": {
"type": "integer",
"minimum": 0,
"maximum": 1000000000000
},
"commissionableCents": {
"type": "integer",
"minimum": 0,
"maximum": 1000000000000
},
"incentiveAppliedCents": {
"type": "integer",
"minimum": 0,
"maximum": 1000000000000
},
"categories": {
"type": "array",
"items": {
"allOf": [
{
"type": "string"
},
{
"type": "string",
"pattern": "^[a-z0-9][a-z0-9_-]{0,39}$"
}
]
},
"maxItems": 20
},
"occurredAt": {
"type": "string",
"format": "date-time"
}
},
"required": [
"type",
"eventId",
"orderId",
"customerId",
"currency",
"commissionableCents",
"occurredAt"
],
"additionalProperties": false
},
"candidates": {
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "receipt"
},
"receipt": {
"type": "string",
"maxLength": 4096
}
},
"required": [
"type",
"receipt"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "code"
},
"referralCode": {
"type": "string",
"maxLength": 120
},
"programId": {
"type": "string",
"minLength": 1,
"maxLength": 120
},
"providerPromoCodeId": {
"type": "string",
"minLength": 1,
"maxLength": 120
},
"enteredAt": {
"type": "string",
"format": "date-time"
}
},
"required": [
"type",
"referralCode"
],
"additionalProperties": false
}
]
},
"maxItems": 50
},
"reservationId": {
"type": "string",
"minLength": 1,
"maxLength": 120
},
"signals": {
"type": "object",
"properties": {
"email": {
"type": "string",
"maxLength": 254
},
"phone": {
"type": "string",
"maxLength": 40
},
"phoneHash": {
"type": "string",
"pattern": "^[0-9a-fA-F]{64}$"
},
"paymentFingerprint": {
"type": "string",
"minLength": 1,
"maxLength": 255
},
"deviceId": {
"type": "string",
"minLength": 1,
"maxLength": 255
},
"ipAddress": {
"type": "string",
"maxLength": 64
}
},
"additionalProperties": false
}
},
"required": [
"event"
],
"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 | |
orderId | string | Yes | |
duplicate | boolean | Yes | |
customerOrderNumber | number | No | Completions only. The purchase number Grizzlee gave the order. |
incentiveFlag | object | No | Completions only. Present when incentiveAppliedCents did not match the reservation. |
decisions | array | Yes | |
rewardIds | array | Yes | |
reversalCents | number | Yes | |
restoredCents | number | No | Reward amount given back because the order's cumulative refund total went down. |
increasedCents | number | No | Adjustments only. Reward value a higher order total added. Lower totals report reversalCents. |
stale | union | No | True when a refund or adjustment event was older than the latest applied one of its kind for the order and changed nothing. |
referralSelection | object | No |
incentiveFlag
| Field | Type | Required | Details |
|---|---|---|---|
incentiveFlag.reason | union | Yes | |
incentiveFlag.rewardId | string | No | |
incentiveFlag.programId | string | No | Program of the reservation or referral checked. Absent for no_referral. |
incentiveFlag.reservationId | string | No | The reservation sent with the order, when there was one. |
incentiveFlag.maxCents | union | Yes | Largest discount the terms allow on one order. Null when they set no cap or there are no terms. |
incentiveFlag.reportedCents | number | Yes | incentiveAppliedCents as the merchant reported it. |
incentiveFlag.reason
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.
incentiveFlag.maxCents
Variant 1
Type: null.
Variant 2
Type: number.
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.
stale
Variant 1
Type: boolean.
Variant 2
Type: boolean.
referralSelection
| Field | Type | Required | Details |
|---|---|---|---|
referralSelection.winner | union | Yes | |
referralSelection.skipped | array | Yes |
referralSelection.winner
Variant 1
Type: null.
Variant 2
| Field | Type | Required | Details |
|---|---|---|---|
referralSelection.winner.programId | string | Yes | |
referralSelection.winner.partnerId | string | Yes | |
referralSelection.winner.occurredAt | string | Yes | |
referralSelection.winner.type | union | No | |
referralSelection.winner.providerPromoCodeId | string | No | |
referralSelection.winner.index | number | No | Absent when the customer's recorded attribution is retained. |
referralSelection.winner.type
Variant 1
Type: string.
Variant 2
Type: string.
referralSelection.skipped[]
| Field | Type | Required | Details |
|---|---|---|---|
referralSelection.skipped.index | number | Yes | |
referralSelection.skipped.reason | union | Yes |
referralSelection.skipped.reason
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.
Response schema
{
"type": "object",
"properties": {
"eventId": {
"type": "string"
},
"orderId": {
"type": "string"
},
"duplicate": {
"type": "boolean"
},
"customerOrderNumber": {
"type": "number",
"description": "Completions only. The purchase number Grizzlee gave the order."
},
"incentiveFlag": {
"type": "object",
"properties": {
"reason": {
"anyOf": [
{
"type": "string",
"const": "no_reservation"
},
{
"type": "string",
"const": "reservation_expired"
},
{
"type": "string",
"const": "reservation_released"
},
{
"type": "string",
"const": "reservation_consumed"
},
{
"type": "string",
"const": "reservation_locked"
},
{
"type": "string",
"const": "no_referral"
},
{
"type": "string",
"const": "exceeds_terms"
},
{
"type": "string",
"const": "category_not_eligible"
}
]
},
"rewardId": {
"type": "string"
},
"programId": {
"type": "string",
"description": "Program of the reservation or referral checked. Absent for no_referral."
},
"reservationId": {
"type": "string",
"description": "The reservation sent with the order, when there was one."
},
"maxCents": {
"anyOf": [
{
"type": "null"
},
{
"type": "number"
}
],
"description": "Largest discount the terms allow on one order. Null when they set no cap or there are no terms."
},
"reportedCents": {
"type": "number",
"description": "incentiveAppliedCents as the merchant reported it."
}
},
"required": [
"reason",
"maxCents",
"reportedCents"
],
"description": "Completions only. Present when incentiveAppliedCents did not match the reservation."
},
"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"
]
}
},
"rewardIds": {
"type": "array",
"items": {
"type": "string"
}
},
"reversalCents": {
"type": "number"
},
"restoredCents": {
"type": "number",
"description": "Reward amount given back because the order's cumulative refund total went down."
},
"increasedCents": {
"type": "number",
"description": "Adjustments only. Reward value a higher order total added. Lower totals report reversalCents."
},
"stale": {
"anyOf": [
{
"type": "boolean",
"const": false
},
{
"type": "boolean",
"const": true
}
],
"description": "True when a refund or adjustment event was older than the latest applied one of its kind for\nthe order and changed nothing."
},
"referralSelection": {
"type": "object",
"properties": {
"winner": {
"anyOf": [
{
"type": "null"
},
{
"type": "object",
"properties": {
"programId": {
"type": "string"
},
"partnerId": {
"type": "string"
},
"occurredAt": {
"type": "string"
},
"type": {
"anyOf": [
{
"type": "string",
"const": "code"
},
{
"type": "string",
"const": "receipt"
}
]
},
"providerPromoCodeId": {
"type": "string"
},
"index": {
"type": "number",
"description": "Absent when the customer's recorded attribution is retained."
}
},
"required": [
"programId",
"partnerId",
"occurredAt"
]
}
]
},
"skipped": {
"type": "array",
"items": {
"type": "object",
"properties": {
"index": {
"type": "number"
},
"reason": {
"anyOf": [
{
"type": "string",
"const": "expired"
},
{
"type": "string",
"const": "invalid"
},
{
"type": "string",
"const": "self_referral"
},
{
"type": "string",
"const": "wrong_program"
},
{
"type": "string",
"const": "program_inactive"
},
{
"type": "string",
"const": "partner_inactive"
},
{
"type": "string",
"const": "fraud"
},
{
"type": "string",
"const": "customer_already_referred"
},
{
"type": "string",
"const": "older"
}
]
}
},
"required": [
"index",
"reason"
]
}
}
},
"required": [
"winner",
"skipped"
]
}
},
"required": [
"eventId",
"orderId",
"duplicate",
"decisions",
"rewardIds",
"reversalCents"
]
}Errors
Failures return { "error": { "code": "...", "message": "...", "details": {} } }. Details are optional. See error statuses. Reuse an unchanged event payload when retrying.