fixRAgent · Schema

TriageResponse

CompanyArtificial IntelligenceProperty ManagementPhoto TriageMaintenanceReal Estate

Properties

Name Type Description
diagnosis_id string uuid4, minted by the server BEFORE the engine runs, because it is the path segment in /c/ and /o/ and the join key into outcomes. Clients never supply it.
engine_version string Which engine build produced this. Recorded by the wrapper, never asked of the model.
rubric_version string Which rubric assigned the tier. Without it a stored tier is uninterpretable the moment the rubric is bumped.
config_id string The config snapshot that produced the answer. An id beginning "fallback-" means the candidates file did not answer and a hardcoded config did.
config_source object Where the config came from. Read `fallback_used` before treating a response as a measured run.
agreement object How sure the engine is, as AGREEMENT between independent reads of the same photo — never the model's own number. n is the number of reads attempted, k the parsed reads that gave the majority yes/no fa
callback_url string The /api/outcome path for this diagnosis_id.
callback object How to report the outcome. /api/outcome reads diagnosis_id from the JSON BODY on POST; the query string on callback_url identifies the diagnosis to a human and in logs, it is not what the endpoint rea
core object
extended object
provenance object
stored object
warnings array Anything the caller should not miss: an open demo-key gate, a config fallback. Empty array when there is nothing to say.
contract_validation object What CONTRACT-v1.json said about this core — ALL of it — and what was done about it. Always present on a 200. TWO HALVES. (1) rules.semantic: the eight rule SENTENCES are read from the contract file a
channel string The door this read came through, as stored on the row (ai_result.channel).
channel_source string Which derivation rule answered: the request body's channel, the ?channel= query parameter, the door's 'source:' variant, a first-party Origin, a key, or the default (api).
language object Which language the prose fields were asked for and served in. `requested` is what the caller sent (body, then ?lang=, then Accept-Language; null when nothing was sent), `served` is en or es, `source`
customer object
data_use object
schema_version string The response schema this body answers under, e.g. "triage-response-v1@1.1.0". Same value as the X-Triage-Schema header. Schema: https://fixragent.com/schemas/triage-response-v1.json — additive only wi
View JSON Schema on GitHub

JSON Schema

fixragent-com-triage-response-schema.json Raw ↑
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/fixragent-com/json-schema/fixragent-com-triage-response-schema.json",
  "title": "TriageResponse",
  "x-generated": "2026-11-08",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/fixragent-com-openapi.json#/components/schemas/TriageResponse",
  "type": "object",
  "required": [
    "diagnosis_id",
    "engine_version",
    "rubric_version",
    "config_id",
    "config_source",
    "agreement",
    "callback_url",
    "core",
    "extended",
    "provenance",
    "stored",
    "warnings",
    "contract_validation",
    "customer",
    "data_use",
    "schema_version"
  ],
  "properties": {
    "diagnosis_id": {
      "type": "string",
      "format": "uuid",
      "description": "uuid4, minted by the server BEFORE the engine runs, because it is the path segment in /c/<uuid> and /o/<uuid> and the join key into outcomes. Clients never supply it."
    },
    "engine_version": {
      "type": "string",
      "description": "Which engine build produced this. Recorded by the wrapper, never asked of the model.",
      "enum": [
        "engine-v2-triage-door-1.0.0",
        "engine-v2-vote-1.0.0"
      ]
    },
    "rubric_version": {
      "type": "string",
      "const": "v1.1-2026-09-11",
      "description": "Which rubric assigned the tier. Without it a stored tier is uninterpretable the moment the rubric is bumped."
    },
    "config_id": {
      "type": "string",
      "description": "The config snapshot that produced the answer. An id beginning \"fallback-\" means the candidates file did not answer and a hardcoded config did."
    },
    "config_source": {
      "type": "object",
      "description": "Where the config came from. Read `fallback_used` before treating a response as a measured run.",
      "required": [
        "requested",
        "fallback_used"
      ],
      "properties": {
        "candidates_file": {
          "type": "string",
          "enum": [
            "qa/engine-v2/ENGINE-V2-CANDIDATES.json",
            "api/_engine-v2-candidates.json"
          ]
        },
        "candidates_file_present": {
          "type": "boolean",
          "description": "Whether the file named in `candidates_file` was readable at request time."
        },
        "requested": {
          "type": "string"
        },
        "fallback_used": {
          "type": "boolean"
        },
        "fallback_reason": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "candidates_file_absent",
            "candidates_file_unparseable",
            "config_not_in_candidates_file",
            "candidate_missing_model_ids",
            "candidate_pin_mismatch",
            "vote_module_unavailable",
            null
          ]
        },
        "warning": {
          "type": [
            "string",
            "null"
          ],
          "description": "A sentence, present whenever a fallback was used, saying this response was not produced by a measured candidate and must not be reported as one."
        },
        "mismatch": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Vote path only, with fallback_reason candidate_pin_mismatch: which pins disagreed with the code (config_id, model, prompt_sha256, schema_sha256, rubric_version, engine_version, n, temperature — the file may not name a temperature other than the one api/_engine-v2-fast.json sends)."
        },
        "vote": {
          "type": "boolean",
          "description": "true when the vote loader answered this request, served or fallen back."
        }
      }
    },
    "agreement": {
      "type": "object",
      "description": "How sure the engine is, as AGREEMENT between independent reads of the same photo — never the model's own number. n is the number of reads attempted, k the parsed reads that gave the majority yes/no fault verdict (null on a tie). decided is true only when k >= ceil(n/2): a read that failed or returned no verdict counts in n and never in k; a tie is never decided. On the vote path an undecided first round is re-voted ONCE (another n reads) and both rounds ride in `rounds`; on the two-pass path n is 1 (fast: a single read, nothing to agree) or 9 (deep) and there is no re-vote. The Triage Profile prints these as landlord sentences (\"two of three reads agreed\", \"undecided — read again\"). When neither round decides, `reported_round` names the round the counts and the delivered core come from.",
      "required": [
        "mode",
        "n",
        "k",
        "decided",
        "votes",
        "applicable"
      ],
      "properties": {
        "mode": {
          "type": "string",
          "enum": [
            "fast",
            "deep"
          ],
          "description": "the ?config requested"
        },
        "n": {
          "type": "integer",
          "minimum": 1,
          "description": "reads attempted in the round this response reports"
        },
        "k": {
          "type": [
            "integer",
            "null"
          ],
          "description": "parsed reads for the majority verdict; null on a tie or a single read"
        },
        "decided": {
          "type": [
            "boolean",
            "null"
          ],
          "description": "k >= ceil(n/2) and not a tie; null on a single read"
        },
        "votes": {
          "type": [
            "object",
            "null"
          ],
          "properties": {
            "fault": {
              "type": "integer"
            },
            "no_fault": {
              "type": "integer"
            },
            "no_verdict": {
              "type": "integer"
            }
          },
          "description": "how the reads fell; no_verdict = reads that failed or returned no boolean verdict"
        },
        "vote_rule": {
          "type": "string",
          "description": "the decided rule in words, stamped wherever a vote is read"
        },
        "re_voted": {
          "type": "boolean",
          "description": "true when the first round was undecided and a second round of n reads was taken (vote path only; always false on the two-pass path)"
        },
        "rounds": {
          "type": "array",
          "items": {
            "type": "object"
          },
          "description": "vote path only: every round attempted (or skipped for lack of time budget), with its counts and reason"
        },
        "majority_verdict": {
          "type": [
            "boolean",
            "null"
          ],
          "description": "true = fault, false = no fault, null when undecided or single read"
        },
        "delivered_matches_majority": {
          "type": [
            "boolean",
            "null"
          ],
          "description": "whether the delivered core still carries the majority verdict after contract repair; false makes the Triage Profile refuse to print the count"
        },
        "applicable": {
          "type": "boolean",
          "description": "false when the photo is not a building asset — agreement is only counted on photos of something in the building"
        },
        "verdict_field": {
          "type": "string"
        },
        "reads_attempted_total": {
          "type": "integer"
        },
        "need": {
          "type": "integer",
          "description": "ceil(n/2)"
        },
        "reported_round": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Vote path only: which round this response reports (1 or 2). A decided round is always the last one attempted. When neither round decided, the round with the LARGER plurality is reported (then the one with more parsed reads, then the later one) — never a round that parsed nothing, and never a worse second round over a better first. The Triage Profile says \"the round this Triage Profile shows\". Absent on the two-pass path."
        }
      }
    },
    "callback_url": {
      "type": "string",
      "description": "The /api/outcome path for this diagnosis_id."
    },
    "callback": {
      "type": "object",
      "description": "How to report the outcome. /api/outcome reads diagnosis_id from the JSON BODY on POST; the query string on callback_url identifies the diagnosis to a human and in logs, it is not what the endpoint reads.",
      "properties": {
        "method": {
          "type": "string",
          "const": "POST"
        },
        "url": {
          "type": "string",
          "const": "/api/outcome"
        },
        "note": {
          "type": "string"
        },
        "required_body": {
          "type": "object"
        }
      }
    },
    "core": {
      "$ref": "#/$defs/TriageCore"
    },
    "extended": {
      "$ref": "#/$defs/TriageExtended"
    },
    "provenance": {
      "$ref": "#/$defs/TriageProvenance"
    },
    "stored": {
      "$ref": "#/$defs/TriageStored"
    },
    "warnings": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Anything the caller should not miss: an open demo-key gate, a config fallback. Empty array when there is nothing to say."
    },
    "contract_validation": {
      "type": "object",
      "description": "What CONTRACT-v1.json said about this core — ALL of it — and what was done about it. Always present on a 200. TWO HALVES. (1) rules.semantic: the eight rule SENTENCES are read from the contract file at module load, never transcribed, and each is bound to a predicate; a sentence with no predicate, or a predicate with no sentence, is reported as drift. (2) json_schema: every field the door emits is walked against the contract's own type, enum, pattern, bounds, string length, array shape, uniqueness and nested required keys — read from the JSON, so a contract change changes what is enforced with no code edit. The validator DECLARES which JSON-Schema keywords it can execute; a keyword the contract uses that it cannot is reported in rules_source.shape_drift and in `warnings` as UNENFORCED, because a constraint that stopped being checked must not read like one that is. Before this existed the handler enforced the eight sentences and nothing else: an engine reply carrying asset_category:'BANANA' came back HTTP 200 with that value intact. Policy is repair-and-record: a violation is repaired, named here, and echoed into `warnings`; a repair never invents a photo-grounded fact, a taxonomy class or prose, and where the engine gave no support the assertion is RETRACTED (preserved under `retracted`) rather than dressed up. A repair that moved severity says so in `repairs_changed_severity` and in an X-Triage-Contract-Repair response header. If any violation survives repair the response is refused with 502 contract_unrepairable instead.",
      "required": [
        "contract_version",
        "policy",
        "conformant",
        "violations_found",
        "repairs",
        "residual_violations"
      ],
      "properties": {
        "contract_version": {
          "type": "string",
          "const": "v1-DRAFT-2026-09-10"
        },
        "policy": {
          "type": "string",
          "const": "repair-and-record"
        },
        "policy_note": {
          "type": "string"
        },
        "rules_source": {
          "type": "object",
          "description": "Where the rules came from and how completely they are enforced: the contract file, how many semantic sentences it supplied, how many predicates are implemented, `drift` between the two, how many json_schema field definitions were found, which JSON-Schema keywords this validator EXECUTES, which it deliberately ignores as annotations, and `shape_drift` — any keyword the contract uses that the validator cannot execute, i.e. any constraint that is currently UNENFORCED."
        },
        "scope_checked": {
          "type": "object",
          "description": "Which clauses were actually evaluated, and on which fields. Clauses that live in `extended` cannot be checked when `extended` was not requested. `core_fields_not_governed_by_contract` names the five RUBRIC-v1.1 additions (tier, criterion_1_fired, tier_forced_by, visible_wiring, water_near_electrical) that CONTRACT-v1 predates and therefore cannot check. `contract_required_fields_this_door_does_not_emit` names the consumer-surface render keys in json_schema.required that this door is not a surface for — their absence is scope, not a violation."
        },
        "violations_found": {
          "type": "array",
          "description": "What was wrong, with the contract's own sentence attached.",
          "items": {
            "type": "object"
          }
        },
        "repairs": {
          "type": "array",
          "description": "Field-by-field: was, now, and why.",
          "items": {
            "type": "object"
          }
        },
        "exemptions": {
          "type": "array",
          "description": "A rule that legitimately does not apply — currently only RUBRIC-v1.1 §2.2's criterion-1 carve-out, the one legal P1 with fault_detected=false. Recorded, never silent.",
          "items": {
            "type": "object"
          }
        },
        "retracted": {
          "type": "object",
          "description": "What an unsupported assertion said before it was withdrawn. Nothing is lost."
        },
        "engine_classes_rejected": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "fault_classes the engine offered that are not taxonomy ids. These used to be dropped silently."
        },
        "repairs_changed_severity": {
          "type": "boolean"
        },
        "severity_before_repair": {
          "type": "string"
        },
        "severity_after_repair": {
          "type": "string"
        },
        "conformant": {
          "type": "boolean",
          "description": "The result of re-running every rule AFTER repair — the after half of a before/after pair, not a hope."
        },
        "residual_violations": {
          "type": "array",
          "items": {
            "type": "object"
          }
        },
        "shape_violations_found": {
          "type": "array",
          "items": {
            "type": "object"
          },
          "description": "Field-shape violations found by walking json_schema: { path, keyword, detail, declared_conflict }. `keyword` is the contract construct that was broken (type, enum, minLength, maximum, uniqueItems, required, ...). `declared_conflict: true` marks the one carve-out described under contract_conflicts."
        },
        "contract_conflicts": {
          "type": "array",
          "items": {
            "type": "object"
          },
          "description": "Places where CONTRACT-v1 and the shipped pipeline genuinely disagree and no repair exists that invents nothing. Currently exactly one: `core.confidence` is typed {number, 0..1} with no null, while clampConf() emits null whenever the engine gave no usable number. Such a case is NOT counted as a residual violation — but it is listed here and pushed onto `warnings` every time it occurs, so it can never read as conformance. It is a named open item, not a fix."
        }
      }
    },
    "channel": {
      "type": "string",
      "enum": [
        "page",
        "embed",
        "handyman",
        "shortcut",
        "email-in",
        "mcp",
        "api"
      ],
      "description": "The door this read came through, as stored on the row (ai_result.channel)."
    },
    "channel_source": {
      "type": "string",
      "enum": [
        "client",
        "query",
        "variant",
        "origin",
        "key",
        "default"
      ],
      "description": "Which derivation rule answered: the request body's channel, the ?channel= query parameter, the door's 'source:<door>' variant, a first-party Origin, a key, or the default (api)."
    },
    "language": {
      "type": "object",
      "description": "Which language the prose fields were asked for and served in. `requested` is what the caller sent (body, then ?lang=, then Accept-Language; null when nothing was sent), `served` is en or es, `source` names the rule that answered, and `hazard_text` is what hazard_detail actually reads as: hazard_detail, resident_do_not and every *_reason field are requested in English on every path, and on the es path the server reads the line back and reports en or not-en. A `note` appears when the two-pass fallback path answered (it is English).",
      "properties": {
        "requested": {
          "type": [
            "string",
            "null"
          ]
        },
        "served": {
          "type": "string",
          "enum": [
            "en",
            "es"
          ]
        },
        "source": {
          "type": "string",
          "enum": [
            "body",
            "query",
            "header",
            "default"
          ]
        },
        "hazard_text": {
          "type": "string",
          "enum": [
            "en",
            "not-en"
          ],
          "description": "A READING of hazard_detail's language on the es path, never a promise: en when the line reads English (or is empty), not-en when the engine wrote it in another language despite being asked for English — the Triage Profile then withholds the line and says so. Always en on the en path."
        },
        "note": {
          "type": "string"
        }
      },
      "required": [
        "served",
        "source",
        "hazard_text"
      ]
    },
    "customer": {
      "$ref": "#/$defs/TriageCustomer"
    },
    "data_use": {
      "$ref": "#/$defs/TriageDataUse"
    },
    "schema_version": {
      "type": "string",
      "description": "The response schema this body answers under, e.g. \"triage-response-v1@1.1.0\". Same value as the X-Triage-Schema header. Schema: https://fixragent.com/schemas/triage-response-v1.json — additive only within v1."
    }
  },
  "$defs": {
    "TriageCore": {
      "type": "object",
      "description": "CONTRACT v1 CORE — exactly 20 fields, in this order. 15 are CONTRACT-v1.json fields under their contract names; 5 (tier, criterion_1_fired, tier_forced_by, visible_wiring, water_near_electrical) are RUBRIC v1.1-2026-09-11 additions that CONTRACT-v1 predates. Everything the engine produced beyond these rides in `extended`; nothing is dropped.",
      "required": [
        "is_building_asset",
        "asset_type",
        "asset_category",
        "photo_subject",
        "report_photo_agreement",
        "fault_detected",
        "fault_visible_in_photo",
        "fault_summary",
        "fault_classes",
        "severity",
        "tier",
        "criterion_1_fired",
        "tier_forced_by",
        "visible_wiring",
        "water_near_electrical",
        "safety_hazard",
        "hazard_detail",
        "trade_required",
        "confidence",
        "resident_explanation"
      ],
      "additionalProperties": false,
      "properties": {
        "is_building_asset": {
          "description": "False when the photo is a person, pet, vehicle, food or document. False forces severity P4 before anything else is evaluated.",
          "type": "boolean"
        },
        "asset_type": {
          "description": "The specific asset, named precisely (\"Gas water heater\", not \"appliance\"). Null only when is_building_asset is false.",
          "type": [
            "string",
            "null"
          ]
        },
        "asset_category": {
          "description": "HVAC | PLUMBING | ELECTRICAL | APPLIANCE | STRUCTURAL | OTHER | NOT_AN_ASSET.",
          "type": "string",
          "enum": [
            "HVAC",
            "PLUMBING",
            "ELECTRICAL",
            "APPLIANCE",
            "STRUCTURAL",
            "OTHER",
            "NOT_AN_ASSET"
          ]
        },
        "photo_subject": {
          "description": "What is actually in frame, named from the pixels alone and independent of anything the reporter said. This is the field that catches a fabricated diagnosis of an asset that is not in the photo.",
          "type": "string"
        },
        "report_photo_agreement": {
          "description": "\"true\" | \"false\" | \"uncertain\" — does the reported problem describe what the photo shows. Null ONLY when no problem_text accompanied the photo.",
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "true",
            "false",
            "uncertain",
            null
          ]
        },
        "fault_detected": {
          "description": "The scored verdict. Rides the photo: a report-only symptom is a hazard input, never a fault.",
          "type": "boolean"
        },
        "fault_visible_in_photo": {
          "description": "Whether the named fault is actually visible. Null when the engine did not answer.",
          "type": [
            "boolean",
            "null"
          ]
        },
        "fault_summary": {
          "description": "One paragraph for the person dispatching. Empty string when no fault.",
          "type": "string"
        },
        "fault_classes": {
          "description": "Taxonomy-v0 class ids, primary first. Empty array whenever fault_detected is false. Ids not in the taxonomy are dropped by the server rather than passed through.",
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "panel-fault",
              "burnt-charred-wiring",
              "damaged-outlet-switch",
              "deteriorated-legacy-wiring",
              "exposed-improper-wiring",
              "electrical-fixture-fault",
              "other-electrical",
              "gas-leak-piping-fault",
              "water-heater-gas-control-fault",
              "water-heater-tank-leak-corrosion",
              "tpr-valve-discharge-fault",
              "active-plumbing-leak",
              "frozen-pipe",
              "sewage-drain-backup",
              "toilet-leak-running",
              "fixture-wear-fault",
              "other-plumbing",
              "combustion-venting-co-fault",
              "heating-cooling-outage",
              "condensate-leak-blockage",
              "refrigerant-circuit-fault",
              "condenser-coil-degradation",
              "ductwork-fault",
              "radon-mitigation-failure",
              "other-hvac",
              "gas-appliance-fault",
              "appliance-electrical-burn",
              "battery-swelling-hazard",
              "appliance-water-leak",
              "refrigerator-cooling-fault",
              "refrigerator-water-system-fault",
              "laundry-mechanical-fault",
              "dryer-vent-lint-fault",
              "cooking-appliance-fault",
              "small-appliance-degradation",
              "other-appliance",
              "structural-sag-collapse",
              "water-damaged-structure",
              "foundation-crack-fault",
              "active-water-intrusion",
              "mold-growth",
              "interior-surface-damage",
              "door-window-hardware-fault",
              "other-structural",
              "roof-structure-rot",
              "roof-active-leak",
              "roof-membrane-degradation",
              "siding-envelope-damage",
              "other-roofing-exterior",
              "equipment-tool-fault",
              "site-grounds-fault",
              "soiling-maintenance-buildup",
              "cosmetic-surface-wear",
              "other-other"
            ]
          }
        },
        "severity": {
          "description": "P1 | P2 | P3 | P4. ASSIGNED BY THE SERVER from the rubric axes. Never taken from the model — whatever the model proposed is kept in provenance.model_proposed_severity instead.",
          "type": "string",
          "enum": [
            "P1",
            "P2",
            "P3",
            "P4"
          ]
        },
        "tier": {
          "description": "EMERGENCY | TODAY | THIS WEEK | WHENEVER. A pure function of severity, assigned in code. The only urgency wording a Triage Profile is allowed to use.",
          "type": "string",
          "enum": [
            "EMERGENCY",
            "TODAY",
            "THIS WEEK",
            "WHENEVER"
          ]
        },
        "criterion_1_fired": {
          "description": "True when the RUBRIC v1.1 EMERGENCY floor forced P1 — visible wiring, or water near anything electrical. This is the one legal way a diagnosis reaches P1 with fault_detected false.",
          "type": "boolean"
        },
        "tier_forced_by": {
          "description": "Which rule overrode the decision table, when one did: \"criterion-1:visible_wiring\", \"criterion-1:water_near_electrical\", \"not-a-building-asset\", or \"no-fault-detected-clamp\".",
          "type": [
            "string",
            "null"
          ]
        },
        "visible_wiring": {
          "description": "Photo-borne fact: a current-carrying conductor, splice or terminal a person could touch without a tool. Low-voltage wiring is excluded by definition. Uncertain reads as false, on purpose.",
          "type": "boolean"
        },
        "water_near_electrical": {
          "description": "Photo-borne fact: water, wetting or wetting-corrosion in contact with, above, or beneath an electrical device. Uncertain reads as false, on purpose.",
          "type": "boolean"
        },
        "safety_hazard": {
          "description": "A hazard EXISTS. Additive — it never replaces the explanation, and it is true whenever the hazard axis is at least H1. Every P1 carries a hazard; not every hazard is a P1.",
          "type": "boolean"
        },
        "hazard_detail": {
          "description": "The named hazard. Non-empty exactly when safety_hazard is true, null exactly when it is false.",
          "type": [
            "string",
            "null"
          ]
        },
        "trade_required": {
          "description": "The trade to dispatch. Null when there is no fault.",
          "type": [
            "string",
            "null"
          ]
        },
        "confidence": {
          "description": "Calibrated 0-1 probability that the fault_detected verdict is correct. Out-of-range values are REJECTED to null, never rescaled on a guess.",
          "type": [
            "number",
            "null"
          ],
          "minimum": 0,
          "maximum": 1
        },
        "resident_explanation": {
          "description": "Plain language, for the resident. Always filled, never \"contact maintenance\".",
          "type": "string"
        }
      }
    },
    "TriageCustomer": {
      "type": "object",
      "additionalProperties": true,
      "description": "What a partner shows its own customer, under its own brand. Every value is derived in code from `core` (never copied from model text), so none of these fields ever carries a brand name or an empty sentence.",
      "required": [
        "action",
        "emergency",
        "trade",
        "sentence",
        "language",
        "find_a_pro_url"
      ],
      "properties": {
        "action": {
          "type": "string",
          "enum": [
            "call_now",
            "book",
            "monitor",
            "no_fault"
          ],
          "description": "call_now = severity P1, a gas/CO fault class, a criterion-1 electrical fact, or a hazard the rubric scores H2 (life-safety now; a reported gas smell is H2); book = P2, or any other named hazard; monitor = P3, or a P4 with a named fault; no_fault = no fault, or not a building asset."
        },
        "emergency": {
          "type": "boolean",
          "description": "True exactly when action is call_now."
        },
        "trade": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "plumbing",
            "electrical",
            "hvac",
            "appliance",
            "roofing",
            "structural",
            "general",
            null
          ],
          "description": "Which trade this belongs to. Null only when the photo is not a building asset."
        },
        "sentence": {
          "type": "string",
          "minLength": 1,
          "description": "One or two plain sentences for the end customer, in `language`. No brand, no model hedging. A gas/CO fault class says leave the building; a criterion-1 electrical fact says keep away and switch off the power; any other named hazard says there is a safety concern and, IF you smell gas, leave the building — never \"not urgent\"."
        },
        "language": {
          "type": "string",
          "enum": [
            "en",
            "es"
          ],
          "description": "Language of `sentence`."
        },
        "find_a_pro_url": {
          "type": [
            "string",
            "null"
          ],
          "description": "OPTIONAL to use: a plain Google Maps \"<trade> near me\" search (no partner id, no affiliate tag) on call_now and book; null otherwise, and always null on a gas/CO emergency (the gas company comes first)."
        }
      }
    },
    "TriageDataUse": {
      "type": "object",
      "additionalProperties": true,
      "description": "Whether this call may ever be used to train or tune a model. Off by default; on only for a key whose holder authorised it in writing. A request can opt out (body do_not_train:true or header x-triage-do-not-train: 1); nothing in a request can opt in. No pipeline reads this flag yet, because nothing trains on these rows today; it is the one field any future export must honour.",
      "required": [
        "training_allowed",
        "basis",
        "request_opt_out",
        "photo_bytes_stored"
      ],
      "properties": {
        "training_allowed": {
          "type": "boolean"
        },
        "basis": {
          "type": "string"
        },
        "request_opt_out": {
          "type": "boolean"
        },
        "photo_bytes_stored": {
          "type": "boolean",
          "description": "True when the EXIF-stripped photo was kept with this Triage Profile (from 6 Oct 2026; a private bucket, shown on the profile's share page). False when it was not kept: the request sent keep_photo:false, or the keep failed. The SHA-256 of the stripped photo is always kept."
        }
      }
    },
    "TriageExtended": {
      "type": [
        "object",
        "null"
      ],
      "description": "Present only when extended=1 is requested; null otherwise. The rest of the contract output.",
      "properties": {
        "isHighRisk": {
          "description": "boolean — a projection of the axes (safety_hazard OR severity P1), not a fifth opinion."
        },
        "hazard_class": {
          "description": "H0 | H1 | H2 — the hazard axis as the rubric recorded it (criterion 1 records H2)."
        },
        "active_loss": {
          "description": "L0 | L1 | L2 — the active-loss axis."
        },
        "habitability": {
          "description": "C0 | C1 | C2 — the habitability axis."
        },
        "deferral_cost": {
          "description": "D0 | D1 | D2 — what one week of doing nothing does."
        },
        "severity_reason_row": {
          "description": "integer|null — which row of the RUBRIC v1 decision table decided it (1..7). Null when an override decided instead; tier_forced_by then says which."
        },
        "make_model": {
          "description": "string|null — only when legible on a label."
        },
        "identification_reasoning": {
          "description": "string|null — the distinguishing features behind asset_type."
        },
        "observations": {
          "description": "array — up to 4 {label, box, confidence}. box is [ymin, xmin, ymax, xmax] as INTEGERS 0-1000 (the Clarity convention, not the 0-1 floats used elsewhere in the consumer app)."
        },
        "loto_required": {
          "description": "boolean — lock-out/tag-out before work."
        },
        "skill_level": {
          "description": "integer|null — 1 (handy resident) .. 5 (licensed specialist)."
        },
        "diy_appropriate": {
          "description": "boolean"
        },
        "estimated_duration_minutes": {
          "description": "integer — technician time on site; 0 when no fault."
        },
        "parts_manifest": {
          "description": "array of {description, part_number, quantity}. part_number is ALWAYS null and

# --- truncated at 32 KB (37 KB total) ---
# Full source: https://artifacts.apis.io/fixragent-com/json-schema/fixragent-com-triage-response-schema.json

Work with this as data

Every JSON Schema here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for schemas

4 MCP tools reach this
  • find_json_schemasBrowse and filter every JSON Schema in the catalog.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This JSON Schema
curl "https://apis.io/api/v1/json-schemas/fixragent-com-triage-response"
All schemas
curl "https://apis.io/api/v1/json-schemas?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.