TANITARDevelopers
WebhooksEvent catalogRewards

reward.exchange-settled

Version 1 of the reward.exchange-settled webhook event.

One exchange reached a terminal state in Reward Management: settled when the value moved (an internal Red ↔ Blue post, a native payout proven on chain, or a deposit credited from a chain-proven transfer), rejected when admission refused it durably (insufficient funds), released when a reserved payout or an unfunded deposit intent was returned, and unresolved when a successful chain receipt did not prove the transfer or a proven deposit fell below the floor. Every terminal record publishes exactly one of these, in the same transaction as its ledger effect; nothing is published for a non-terminal state.

Event typereward.exchange-settled
Schema version1
Routing keyevent.reward.exchange-settled.v1
Produced byReward Management
Schemaurn:defendr:contract:reward:exchange-settled:1

data fields

FieldTypeRequiredDescription
exchangeIdstringyes
playerIdstringyes
kind"internal", "crypto-payout", "crypto-deposit"yes
outcome"settled", "rejected", "released", "unresolved"yes
chain"solana", "ethereum", "hedera", nullyes
fromobjectyes
from.currencyKeystringyes
from.amountstringyesA whole amount for internal currencies (Red, Blue); base units (lamports, wei, tinybar-equivalent wei) for a native currency.
toobjectyes
to.currencyKeystringyes
to.amountstringyesA whole amount for internal currencies (Red, Blue); base units (lamports, wei, tinybar-equivalent wei) for a native currency.
ratestringyesThe configured rate pinned on the record: Blue per Red (5) or Red per Blue (0.2) for an internal exchange; Red per whole native unit for a native leg.
duststringyesThe fraction of one output unit (a base unit for a payout, a whole Red for a deposit) the integer conversion could not represent, as a decimal below 1 with up to 18 digits; disclosed, never credited.
gameSlugstring or nullyes
destinationstring or nullyes
treasurystring or nullyesThe chain signer a deposit must target; null for a payout and an internal exchange.
noncestring or nullyesThe decimal u64 number carried by the transfer; null for an internal exchange.
txIdstring or nullyes
failureCodestring or nullyes
settledAtstring (date-time)yes

Some fields are required only in particular states. The JSON Schema below is authoritative.

Example delivery

Signed with the example secret whsec_placeholder_example_webhooks_signing_secret, so you can check your verifier against it. See Verifying signatures.

POST /your/webhook/endpoint HTTP/1.1
content-type: application/json
webhook-id: whd_00000000-0000-4000-8000-000000000023
webhook-timestamp: 1800000000
webhook-signature: v1,GZIcPu/XbRrsIFKNU2psjH4TdEJ4cWWJZMS0QVUCmDE=
tanitar-event-type: reward.exchange-settled
tanitar-schema-version: 1
tanitar-delivery-attempt: 1

{"messageId":"evt_00000000-0000-4000-8000-000000000023","kind":"event","type":"reward.exchange-settled","schemaVersion":1,"occurredAt":"2027-01-15T10:00:00.000Z","producer":"reward-management-service","accountId":"acc_11111111-1111-4111-8111-111111111111","correlationId":"corr_00000000-0000-4000-8000-000000000023","data":{"exchangeId":"exch_0123456789abcdef0123456789abcdef","playerId":"player_demo","kind":"crypto-payout","outcome":"settled","chain":"solana","from":{"currencyKey":"red","amount":"150"},"to":{"currencyKey":"sol","amount":"10000000"},"rate":"15000","dust":"0","gameSlug":null,"destination":"7Np41oeYqPefeNQEHSv1UDhYrehxin3NStELsSKCTsxg","treasury":null,"nonce":"1234567890123456789","txId":"5UfDuX7WXY7f5J5xg7pGt8H2N4wYVJ6E1VZ1kY4Wc1u3vXcQm8nHqpLjsVzHt2NhbiPqk3jD5X1hfUJ3rW6z6XwR","failureCode":null,"settledAt":"2026-09-19T00:00:00.000Z"}}

The same body, formatted:

{
  "messageId": "evt_00000000-0000-4000-8000-000000000023",
  "kind": "event",
  "type": "reward.exchange-settled",
  "schemaVersion": 1,
  "occurredAt": "2027-01-15T10:00:00.000Z",
  "producer": "reward-management-service",
  "accountId": "acc_11111111-1111-4111-8111-111111111111",
  "correlationId": "corr_00000000-0000-4000-8000-000000000023",
  "data": {
    "exchangeId": "exch_0123456789abcdef0123456789abcdef",
    "playerId": "player_demo",
    "kind": "crypto-payout",
    "outcome": "settled",
    "chain": "solana",
    "from": {
      "currencyKey": "red",
      "amount": "150"
    },
    "to": {
      "currencyKey": "sol",
      "amount": "10000000"
    },
    "rate": "15000",
    "dust": "0",
    "gameSlug": null,
    "destination": "7Np41oeYqPefeNQEHSv1UDhYrehxin3NStELsSKCTsxg",
    "treasury": null,
    "nonce": "1234567890123456789",
    "txId": "5UfDuX7WXY7f5J5xg7pGt8H2N4wYVJ6E1VZ1kY4Wc1u3vXcQm8nHqpLjsVzHt2NhbiPqk3jD5X1hfUJ3rW6z6XwR",
    "failureCode": null,
    "settledAt": "2026-09-19T00:00:00.000Z"
  }
}

JSON Schema

urn:defendr:contract:reward:exchange-settled:1
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:defendr:contract:reward:exchange-settled:1",
  "title": "Reward exchange settled event payload",
  "description": "One exchange reached a terminal state in Reward Management: `settled` when the value moved (an internal Red ↔ Blue post, a native payout proven on chain, or a deposit credited from a chain-proven transfer), `rejected` when admission refused it durably (insufficient funds), `released` when a reserved payout or an unfunded deposit intent was returned, and `unresolved` when a successful chain receipt did not prove the transfer or a proven deposit fell below the floor. Every terminal record publishes exactly one of these, in the same transaction as its ledger effect; nothing is published for a non-terminal state.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "exchangeId",
    "playerId",
    "kind",
    "outcome",
    "chain",
    "from",
    "to",
    "rate",
    "dust",
    "gameSlug",
    "destination",
    "treasury",
    "nonce",
    "txId",
    "failureCode",
    "settledAt"
  ],
  "properties": {
    "exchangeId": {
      "type": "string",
      "pattern": "^exch_[a-f0-9]{32}$"
    },
    "playerId": {
      "type": "string",
      "minLength": 1
    },
    "kind": {
      "enum": [
        "internal",
        "crypto-payout",
        "crypto-deposit"
      ]
    },
    "outcome": {
      "enum": [
        "settled",
        "rejected",
        "released",
        "unresolved"
      ]
    },
    "chain": {
      "enum": [
        "solana",
        "ethereum",
        "hedera",
        null
      ]
    },
    "from": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "currencyKey",
        "amount"
      ],
      "properties": {
        "currencyKey": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9-]*$"
        },
        "amount": {
          "type": "string",
          "pattern": "^[0-9]+$",
          "description": "A whole amount for internal currencies (Red, Blue); base units (lamports, wei, tinybar-equivalent wei) for a native currency."
        }
      }
    },
    "to": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "currencyKey",
        "amount"
      ],
      "properties": {
        "currencyKey": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9-]*$"
        },
        "amount": {
          "type": "string",
          "pattern": "^[0-9]+$",
          "description": "A whole amount for internal currencies (Red, Blue); base units (lamports, wei, tinybar-equivalent wei) for a native currency."
        }
      }
    },
    "rate": {
      "type": "string",
      "pattern": "^[0-9]+(\\.[0-9]+)?$",
      "description": "The configured rate pinned on the record: Blue per Red (`5`) or Red per Blue (`0.2`) for an internal exchange; Red per whole native unit for a native leg."
    },
    "dust": {
      "type": "string",
      "pattern": "^0(?:\\.[0-9]{1,18})?$",
      "description": "The fraction of one output unit (a base unit for a payout, a whole Red for a deposit) the integer conversion could not represent, as a decimal below 1 with up to 18 digits; disclosed, never credited."
    },
    "gameSlug": {
      "type": [
        "string",
        "null"
      ]
    },
    "destination": {
      "type": [
        "string",
        "null"
      ]
    },
    "treasury": {
      "type": [
        "string",
        "null"
      ],
      "description": "The chain signer a deposit must target; null for a payout and an internal exchange."
    },
    "nonce": {
      "type": [
        "string",
        "null"
      ],
      "pattern": "^[0-9]+$",
      "description": "The decimal u64 number carried by the transfer; null for an internal exchange."
    },
    "txId": {
      "type": [
        "string",
        "null"
      ]
    },
    "failureCode": {
      "type": [
        "string",
        "null"
      ],
      "pattern": "^[A-Z0-9_]+$"
    },
    "settledAt": {
      "type": "string",
      "format": "date-time"
    }
  },
  "allOf": [
    {
      "if": {
        "type": "object",
        "required": [
          "kind"
        ],
        "properties": {
          "kind": {
            "const": "internal"
          }
        }
      },
      "then": {
        "type": "object",
        "properties": {
          "chain": {
            "type": "null"
          },
          "destination": {
            "type": "null"
          },
          "treasury": {
            "type": "null"
          },
          "nonce": {
            "type": "null"
          },
          "txId": {
            "type": "null"
          }
        }
      }
    },
    {
      "if": {
        "type": "object",
        "required": [
          "kind"
        ],
        "properties": {
          "kind": {
            "const": "crypto-payout"
          }
        }
      },
      "then": {
        "type": "object",
        "properties": {
          "chain": {
            "type": "string"
          },
          "nonce": {
            "type": "string"
          },
          "destination": {
            "type": "string"
          },
          "treasury": {
            "type": "null"
          }
        }
      }
    },
    {
      "if": {
        "type": "object",
        "required": [
          "kind"
        ],
        "properties": {
          "kind": {
            "const": "crypto-deposit"
          }
        }
      },
      "then": {
        "type": "object",
        "properties": {
          "chain": {
            "type": "string"
          },
          "nonce": {
            "type": "string"
          },
          "treasury": {
            "type": "string"
          },
          "destination": {
            "type": "null"
          }
        }
      }
    },
    {
      "if": {
        "type": "object",
        "required": [
          "outcome"
        ],
        "properties": {
          "outcome": {
            "const": "settled"
          }
        }
      },
      "then": {
        "type": "object",
        "properties": {
          "failureCode": {
            "type": "null"
          }
        }
      }
    },
    {
      "if": {
        "type": "object",
        "required": [
          "outcome"
        ],
        "properties": {
          "outcome": {
            "enum": [
              "rejected",
              "unresolved"
            ]
          }
        }
      },
      "then": {
        "type": "object",
        "properties": {
          "failureCode": {
            "type": "string"
          }
        }
      }
    }
  ]
}

On this page