{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://fingertrackid.dev/schemas/event-assessment-request.v1.schema.json",
  "title": "EventAssessmentRequestV1",
  "description": "Request server-to-server fechado para avaliar um LOGIN, WITHDRAWAL ou TRADE específico.",
  "$defs": {
    "external_event_id": {
      "type": "string",
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
    },
    "idempotency_key": {
      "type": "string",
      "minLength": 8,
      "maxLength": 255,
      "pattern": "^[\\x21-\\x7e]+$"
    },
    "instant": {
      "type": "string",
      "format": "date-time",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,3})?(?:Z|[+-]\\d{2}:\\d{2})$"
    },
    "opaque_ref": {
      "type": "string",
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$",
      "description": "Pseudônimo opaco e estável no escopo do tenant, apresentado por um integration client autenticado. Não envie CPF, e-mail, telefone, nome, segredo nem identificador diretamente reversível."
    },
    "nullable_opaque_ref": {
      "anyOf": [{ "$ref": "#/$defs/opaque_ref" }, { "type": "null" }]
    },
    "event_context_token": {
      "description": "Campo obrigatório e nullable. null significa ausência de coleta e produz fallback inconclusivo; token presente mas inválido é rejeitado.",
      "anyOf": [
        {
          "type": "string",
          "minLength": 1,
          "maxLength": 4096,
          "pattern": "^[\\x21-\\x7e]+$"
        },
        { "type": "null" }
      ]
    }
  },
  "oneOf": [
    {
      "title": "LoginAssessmentRequestV1",
      "type": "object",
      "properties": {
        "schema_version": { "const": "event-assessment-request.v1" },
        "external_event_id": { "$ref": "#/$defs/external_event_id" },
        "idempotency_key": { "$ref": "#/$defs/idempotency_key" },
        "occurred_at": { "$ref": "#/$defs/instant" },
        "subject_ref": { "$ref": "#/$defs/nullable_opaque_ref" },
        "account_ref": { "$ref": "#/$defs/nullable_opaque_ref" },
        "event_context_token": { "$ref": "#/$defs/event_context_token" },
        "journey": { "const": "LOGIN" },
        "action": { "const": "LOGIN_ATTEMPT" },
        "journey_context": {
          "type": "object",
          "properties": {},
          "additionalProperties": false
        },
        "deadline_ms": {
          "description": "Budget técnico contado desde received_at. null seleciona 5000 ms; não é SLA comercial.",
          "anyOf": [{ "type": "integer", "minimum": 250, "maximum": 5000 }, { "type": "null" }]
        }
      },
      "required": [
        "schema_version",
        "external_event_id",
        "idempotency_key",
        "occurred_at",
        "subject_ref",
        "account_ref",
        "event_context_token",
        "journey",
        "action",
        "journey_context",
        "deadline_ms"
      ],
      "additionalProperties": false,
      "allOf": [
        {
          "oneOf": [
            {
              "type": "object",
              "properties": {
                "subject_ref": { "type": "null" },
                "account_ref": { "type": "null" }
              },
              "required": ["subject_ref", "account_ref"]
            },
            {
              "type": "object",
              "properties": {
                "subject_ref": { "$ref": "#/$defs/opaque_ref" },
                "account_ref": { "type": "null" }
              },
              "required": ["subject_ref", "account_ref"]
            },
            {
              "type": "object",
              "properties": {
                "subject_ref": { "$ref": "#/$defs/opaque_ref" },
                "account_ref": { "$ref": "#/$defs/opaque_ref" }
              },
              "required": ["subject_ref", "account_ref"]
            }
          ]
        }
      ]
    },
    {
      "title": "WithdrawalAssessmentRequestV1",
      "type": "object",
      "properties": {
        "schema_version": { "const": "event-assessment-request.v1" },
        "external_event_id": { "$ref": "#/$defs/external_event_id" },
        "idempotency_key": { "$ref": "#/$defs/idempotency_key" },
        "occurred_at": { "$ref": "#/$defs/instant" },
        "subject_ref": { "$ref": "#/$defs/opaque_ref" },
        "account_ref": { "$ref": "#/$defs/opaque_ref" },
        "event_context_token": { "$ref": "#/$defs/event_context_token" },
        "journey": { "const": "WITHDRAWAL" },
        "action": { "const": "WITHDRAWAL_REQUEST" },
        "journey_context": {
          "type": "object",
          "properties": {
            "amount_minor": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "currency": {
              "type": "string",
              "minLength": 3,
              "maxLength": 3,
              "pattern": "^[A-Z]{3}$",
              "description": "Código ISO 4217 da moeda da unidade mínima. A policy versionada da aplicação mantém a allowlist e o expoente suportados; o pattern sozinho não comprova ISO 4217. Criptoativos não usam este campo."
            },
            "beneficiary_ref": { "$ref": "#/$defs/opaque_ref" }
          },
          "required": ["amount_minor", "currency", "beneficiary_ref"],
          "additionalProperties": false
        },
        "deadline_ms": {
          "description": "Budget técnico contado desde received_at. null seleciona 1000 ms; não é SLA comercial.",
          "anyOf": [{ "type": "integer", "minimum": 500, "maximum": 1000 }, { "type": "null" }]
        }
      },
      "required": [
        "schema_version",
        "external_event_id",
        "idempotency_key",
        "occurred_at",
        "subject_ref",
        "account_ref",
        "event_context_token",
        "journey",
        "action",
        "journey_context",
        "deadline_ms"
      ],
      "additionalProperties": false
    },
    {
      "title": "TradeAssessmentRequestV1",
      "type": "object",
      "properties": {
        "schema_version": { "const": "event-assessment-request.v1" },
        "external_event_id": { "$ref": "#/$defs/external_event_id" },
        "idempotency_key": { "$ref": "#/$defs/idempotency_key" },
        "occurred_at": { "$ref": "#/$defs/instant" },
        "subject_ref": { "$ref": "#/$defs/opaque_ref" },
        "account_ref": { "$ref": "#/$defs/opaque_ref" },
        "event_context_token": { "$ref": "#/$defs/event_context_token" },
        "journey": { "const": "TRADE" },
        "action": { "const": "TRADE_REQUEST" },
        "journey_context": {
          "type": "object",
          "properties": {},
          "additionalProperties": false
        },
        "deadline_ms": {
          "description": "Budget técnico contado desde received_at. null seleciona 750 ms; não é SLA comercial.",
          "anyOf": [{ "type": "integer", "minimum": 250, "maximum": 750 }, { "type": "null" }]
        }
      },
      "required": [
        "schema_version",
        "external_event_id",
        "idempotency_key",
        "occurred_at",
        "subject_ref",
        "account_ref",
        "event_context_token",
        "journey",
        "action",
        "journey_context",
        "deadline_ms"
      ],
      "additionalProperties": false
    }
  ],
  "x-ftid-runtime-invariants": [
    "only a NEW logical operation requires occurred_at within plus or minus 300 seconds of server received_at; an idempotent SAME hit is resolved first",
    "event_context_token is authenticated, single-scope and consumed atomically after idempotent replay lookup",
    "deadline_ms is authenticated by the raw-body HMAC but excluded from the semantic idempotency digest because it only budgets the transport attempt",
    "same idempotency_key and semantic request digest return the persisted assessment; divergent reuse is conflict",
    "idempotency_key and external_event_id are never reused during the resolved integration_client lifetime, including after payload retention; tenant erasure ends the namespace",
    "withdrawal currency belongs to the application policy ISO 4217 allowlist and amount_minor uses that currency minor-unit exponent"
  ]
}
