{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://docs.get-flowie.com/schemas/fr-lifecycle-status.schema.json",
  "title": "French e-invoicing lifecycle status referential",
  "description": "Machine-readable schema for the French B2B e-invoicing lifecycle status referential (AFNOR XP Z12-012, CDAR element MDT-105, codes 200-213). The conforming dataset is published at https://docs.get-flowie.com/schemas/fr-lifecycle-statuses.json and mirrors the human-readable reference at https://docs.get-flowie.com/compliance/fr/lifecycle.html.",
  "type": "object",
  "required": ["$schema", "version", "specVersions", "statuses"],
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string",
      "description": "URI of this schema."
    },
    "version": {
      "type": "string",
      "description": "Version of this dataset (bumped whenever the referential or the Flowie mapping changes).",
      "pattern": "^\\d+\\.\\d+\\.\\d+$"
    },
    "specVersions": {
      "type": "object",
      "description": "The official specification versions this dataset was verified against.",
      "required": ["dgfipExternalSpecifications", "afnorNorm"],
      "additionalProperties": false,
      "properties": {
        "dgfipExternalSpecifications": { "type": "string" },
        "afnorNorm": { "type": "string" }
      }
    },
    "orderingRule": {
      "type": "string",
      "description": "Human-readable statement of the ordering semantics (chronology is respected, but optional statuses may be skipped — the transitions below describe the canonical graph, not mandatory checkpoints)."
    },
    "statuses": {
      "type": "array",
      "minItems": 14,
      "maxItems": 14,
      "items": { "$ref": "#/$defs/status" }
    }
  },
  "$defs": {
    "status": {
      "type": "object",
      "required": [
        "code", "labelFr", "labelEn", "tier", "tierFr", "phase", "mdt77",
        "emittedBy", "terminal", "transmittedToDgfip", "reasonRequired",
        "untdid1373", "transitionsTo", "flowie", "description"
      ],
      "additionalProperties": false,
      "properties": {
        "code": {
          "type": "integer",
          "minimum": 200,
          "maximum": 213,
          "description": "The MDT-105 ProcessConditionCode from the 200-213 referential."
        },
        "labelFr": {
          "type": "string",
          "description": "Official French status label (MDT-106)."
        },
        "labelEn": { "type": "string" },
        "tier": {
          "enum": ["mandatory", "recommended", "free"],
          "description": "Normative tier per the 'Transmission' column of the DGFiP external specifications: mandatory (obligatoire — always produced, always transmitted to the DGFiP concentrator), recommended (recommandé), or free (libre — coded but at each platform's discretion)."
        },
        "tierFr": { "enum": ["Obligatoire", "Recommandé", "Libre"] },
        "tierNote": {
          "type": "string",
          "description": "Caveat when the tier classification is contested between spec versions or readings (e.g. 207 En litige)."
        },
        "phase": {
          "enum": ["transmission", "traitement"],
          "description": "transmission = generated automatically by platforms as the invoice moves (MDT-77 = 305); traitement = business decision by buyer or supplier (MDT-77 = 23)."
        },
        "mdt77": { "enum": [305, 23] },
        "emittedBy": {
          "enum": ["seller_platform", "buyer_platform", "any_platform", "buyer", "supplier"],
          "description": "Who produces the status. Platform emitters are automatic; buyer/supplier emitters are business decisions."
        },
        "terminal": {
          "type": "boolean",
          "description": "True when the status ends the lifecycle (210, 212, 213)."
        },
        "terminalOutcome": {
          "enum": ["success", "failure"],
          "description": "Only present when terminal: 212 is the happy terminal; 210/213 cancel the invoice."
        },
        "transmittedToDgfip": {
          "type": "boolean",
          "description": "True for the 4 mandatory statuses, which always reach the PPF concentrator (within the 24-hour window for 200)."
        },
        "reasonRequired": {
          "type": "boolean",
          "description": "True when the CDAR must (210, 213) or is expected to (206, 207, 208) carry a motif — MDT-113 ReasonCode from the restricted AFNOR annex vocabulary, optionally MDT-114 free text."
        },
        "amountBlocks": {
          "type": "array",
          "items": { "enum": ["MEN", "MPA", "RAP", "MAP", "MAPTTC", "MNA", "MNATTC", "MAJ"] },
          "description": "MDG-43 / MDT-207 characteristic blocks this status carries: MEN collected amount (mandatory on 212, rule BR-FR-CDV-14), MPA paid / RAP remainder (211), MAP/MNA approval amounts (206), MAJ replacement data (209)."
        },
        "untdid1373": {
          "type": "object",
          "description": "Generic international equivalent carried in MDT-88 (rule BR-FR-CDV-CL-05).",
          "required": ["code", "name"],
          "additionalProperties": false,
          "properties": {
            "code": { "type": "integer" },
            "name": { "type": "string" }
          }
        },
        "transitionsTo": {
          "type": "array",
          "items": { "type": "integer", "minimum": 200, "maximum": 213 },
          "uniqueItems": true,
          "description": "Canonical next statuses. Not checkpoints: optional statuses may be skipped, so consumers must accept jumps (e.g. 200 directly to 210, 212 or 213)."
        },
        "flowie": {
          "type": "object",
          "description": "How this status maps onto the Flowie Exchange API (PA n° 0040).",
          "required": ["emission"],
          "additionalProperties": false,
          "properties": {
            "emission": {
              "enum": ["automatic", "api_call"],
              "description": "automatic = Flowie emits it for you; api_call = your integration triggers it."
            },
            "request": {
              "type": "object",
              "description": "The API call that emits this status (present when emission = api_call).",
              "required": ["method", "path"],
              "additionalProperties": false,
              "properties": {
                "method": { "enum": ["POST"] },
                "path": { "type": "string" },
                "body": { "type": "object" },
                "party": {
                  "enum": ["buyer", "supplier"],
                  "description": "Which org must make the call — the same paid call emits 211 from the buyer org and 212 from the supplier org."
                }
              }
            },
            "webhook": {
              "type": "string",
              "description": "The webhook event that surfaces this status to your integration."
            },
            "notes": { "type": "string" }
          }
        },
        "description": { "type": "string" }
      },
      "allOf": [
        {
          "if": { "properties": { "terminal": { "const": true } } },
          "then": { "required": ["terminalOutcome"] }
        }
      ]
    }
  }
}
