diff --git a/contracts/path/jobs.yaml b/contracts/path/jobs.yaml index 2ac042f..1ea0858 100644 --- a/contracts/path/jobs.yaml +++ b/contracts/path/jobs.yaml @@ -6,7 +6,7 @@ job_by_id: operationId: getJobStatus summary: Get async job status description: | - Universal job status endpoint for any asynchronous operation in FireForm — + Universal job status endpoint for any asynchronous operation in FireForm transcription, LLM extraction, form generation, and report generation. Returns the current status, progress percentage, and a result URL when the job completes. Poll this endpoint for long-running operations. diff --git a/contracts/path/system.yaml b/contracts/path/system.yaml index cd00901..2a2bb95 100644 --- a/contracts/path/system.yaml +++ b/contracts/path/system.yaml @@ -2,13 +2,14 @@ # GET /api/v1/health # GET /api/v1/schema/incident # GET /api/v1/schema/incident/versions +# GET /api/v1/schema/fields health: get: operationId: getHealth summary: System health check description: | - Returns the health status of all FireForm components — database, Ollama LLM + Returns the health status of all FireForm components - database, Ollama LLM (including loaded models, GPU status, and current load), Whisper transcription, and file storage. Returns 200 even when degraded (so load balancers don't kill it), 503 only if the system is truly unhealthy and cannot serve any requests. @@ -99,9 +100,9 @@ health: schema_incident: get: operationId: getIncidentSchema - summary: Get the canonical FireForm JSON Schema + summary: Get the incident contract as a JSON Schema description: | - Returns the full canonical FireForm incident JSON Schema (JSON Schema + Returns the full incident contract as a JSON Schema document (JSON Schema draft-07). Clients can use this to validate incident data locally before submitting. This is a schema-as-API pattern for interoperability. tags: @@ -113,21 +114,99 @@ schema_incident: application/json: schema: type: object - description: JSON Schema draft-07 document for the canonical incident model + description: JSON Schema draft-07 document for the incident contract example: $schema: "http://json-schema.org/draft-07/schema#" - title: "FireForm Canonical Incident" + title: "FireForm Incident Contract" type: "object" properties: schema_version: type: "string" +schema_fields: + get: + operationId: searchSchemaFields + summary: Search or list the incident-contract field catalog + description: | + Returns the flattened, searchable catalog of every leaf field in the + incident contract: dotted path, type, section, description, enum values, + aliases and PII flag. The catalog is generated from the contract itself, + so it always matches the running schema version. + + The catalog (paths, types, descriptions, aliases, PII flags) is built + by flattening incident-contract.yaml at startup; aliases come from each + field's x-aliases entry in that file. The contract is the single source + of truth, nothing is duplicated in code, and a schema upgrade updates + search and suggestions automatically. + + Without `q` the full catalog is returned; the editor can cache it for + the whole session since it only changes with a schema upgrade and + filter locally as the user types. With `q` the server ranks matches + with plain fuzzy scoring. No LLM is involved, results are deterministic + and instant. Ranking order, best first: exact field-name match, exact + alias match, field-name prefix, alias prefix, token-based fuzzy match + on name and aliases, word match in the description. Ties break toward + the shorter path. Short queries stay usable because name and alias hits + always outrank description-only hits. + + This powers two things: the type-ahead mapping picker in the template + editor, and the automatic mapping suggestions produced after commonforms + field detection (same index, same scorer). + tags: + - system + parameters: + - name: q + in: query + required: false + description: Search text. Matches field names, aliases and descriptions. + schema: + type: string + - name: section + in: query + required: false + description: Restrict results to one top-level contract section + schema: + type: string + - name: limit + in: query + required: false + schema: + type: integer + default: 20 + maximum: 100 + responses: + "200": + description: Ranked matches, or the full catalog when q is omitted + content: + application/json: + schema: + $ref: "../schemas/template-record.yaml#/SchemaFieldSearchResponse" + example: + query: "zip" + total: 2 + schema_version: "1.1.0" + fields: + - path: "location.postal_code" + label: "Postal code" + field_type: "string" + section: "location" + aliases: ["zip", "zipcode", "pincode"] + pii: false + score: 0.97 + - path: "persons_involved[].address" + label: "Person address" + field_type: "string" + section: "persons_involved" + description: "Owner, occupant or other party's address" + pii: true + score: 0.41 + schema_versions: get: operationId: getSchemaVersions summary: Get schema version history description: | - Returns the version history of the canonical FireForm incident schema, + Returns the version history of the incident contract, including changelogs and whether each version introduced breaking changes. Useful for clients tracking schema evolution across FireForm updates. tags: @@ -148,5 +227,5 @@ schema_versions: breaking_changes: true - version: "1.0.0" released_at: "2024-01-01T00:00:00Z" - changelog: "Initial canonical schema with NFIRS support." + changelog: "Initial incident contract with NFIRS support." breaking_changes: false diff --git a/contracts/path/templates.yaml b/contracts/path/templates.yaml index c2621ca..da433d6 100644 --- a/contracts/path/templates.yaml +++ b/contracts/path/templates.yaml @@ -4,7 +4,9 @@ # POST /api/v1/templates # PUT /api/v1/templates/{template_id} # GET /api/v1/templates/{template_id}/fields +# GET /api/v1/templates/{template_id}/pdf # POST /api/v1/templates/pdf +# GET /api/v1/templates/pdf/{upload_id} templates: get: @@ -25,7 +27,7 @@ templates: schema: type: array items: - $ref: "../schemas/template.yaml#/TemplateSummary" + $ref: "../schemas/template-record.yaml#/TemplateSummary" example: - template_id: "550e8400-e29b-41d4-a716-446655440070" form_type: "neris" @@ -61,8 +63,11 @@ templates: description: | Registers a new form template for a jurisdiction or agency not yet supported. This is how FireForm extends to new states, countries, or custom agency forms - without code changes. The template defines all fields, their types, validation - rules, and how each maps from the FireForm incident schema. + without code changes. The template defines all fields, their placement on the + uploaded PDF, and where each value comes from (see FieldSource): a contract + lookup, a constant, a per-incident manual entry, or an open field the LLM + extracts. Typically the fields array starts from the detected_fields draft + returned by the PDF upload and carries the user's edits. tags: - templates requestBody: @@ -70,16 +75,17 @@ templates: content: application/json: schema: - $ref: "../schemas/template.yaml#/CreateTemplateRequest" + $ref: "../schemas/template-record.yaml#/CreateTemplateRequest" example: form_type: "state_texas" display_name: "Texas State Fire Marshal Incident Report" jurisdiction: "US-TX" agency_type: "fire_department" - pdf_template_ref: "templates/state_texas.pdf" + pdf_template_ref: "templates/uploads/550e8400-e29b-41d4-a716-446655440080.pdf" fields: - field_name: "incident_number" field_type: "string" + source: "schema" required: true max_length: 20 description: "State-assigned incident number" @@ -96,6 +102,7 @@ templates: align: "left" - field_name: "fire_cause" field_type: "enum" + source: "schema" required: true allowed_values: - "accidental" @@ -109,11 +116,33 @@ templates: y: 560.0 width: 200.0 height: 18.0 + - field_name: "insurance_company" + field_type: "string" + source: "open" + required: false + description: "Name of the insurance company covering the property" + layout: + page: 0 + x: 188.33 + y: 520.0 + width: 200.0 + height: 18.0 + - field_name: "marshal_signature_name" + field_type: "string" + source: "manual" + required: true + description: "Reviewing marshal's printed name, entered per incident" + layout: + page: 1 + x: 100.0 + y: 120.0 + width: 180.0 + height: 18.0 - field_name: "report_footer" field_type: "string" + source: "static" required: false static_text: "Generated by FireForm" - incident_mapping: null layout: page: 0 x: 72.0 @@ -128,7 +157,7 @@ templates: content: application/json: schema: - $ref: "../schemas/template.yaml#/Template" + $ref: "../schemas/template-record.yaml#/Template" "409": description: Template with this form_type already exists content: @@ -166,7 +195,7 @@ template_by_id: content: application/json: schema: - $ref: "../schemas/template.yaml#/Template" + $ref: "../schemas/template-record.yaml#/Template" "404": description: Template not found content: @@ -196,14 +225,14 @@ template_by_id: content: application/json: schema: - $ref: "../schemas/template.yaml#/CreateTemplateRequest" + $ref: "../schemas/template-record.yaml#/CreateTemplateRequest" responses: "200": description: Template updated content: application/json: schema: - $ref: "../schemas/template.yaml#/Template" + $ref: "../schemas/template-record.yaml#/Template" "404": description: Template not found content: @@ -269,7 +298,7 @@ template_fields: fields: type: array items: - $ref: "../schemas/template.yaml#/TemplateField" + $ref: "../schemas/template-record.yaml#/TemplateField" example: template_id: "550e8400-e29b-41d4-a716-446655440070" form_type: "neris" @@ -309,20 +338,27 @@ template_fields: templates_pdf: post: operationId: uploadTemplatePdf - summary: Upload a blank PDF for a template before defining its fields + summary: Upload a blank PDF and auto-detect its fields description: | - Uploads the blank agency PDF that a template's field coordinates will be - written onto, and returns a `pdf_template_ref` plus the page geometry. + First step of the template-authoring flow. Stores the blank agency PDF, + reads its page geometry, and kicks off asynchronous field detection with + commonforms. Returns 202 immediately with the stored `pdf_template_ref`, + the page dimensions, and an `upload_id` to poll. + + Poll `GET /api/v1/templates/pdf/{upload_id}` until detection completes. + The completed draft carries one TemplateField per detected box, each with + its layout already set and, where the fuzzy matcher scored a confident + match, a suggested `incident_mapping` plus ranked alternatives. Detection + is best effort: boxes with no interpretable label come back as plain + coordinate boxes with no mapping, and a detection failure still leaves + the upload fully usable for manual box drawing. - This is the first step of the template-authoring flow. The visual editor - renders the returned pages, the user draws a box per field, and the box - coordinates become each field's `layout`. The resulting `pdf_template_ref` - is then sent in the `POST /api/v1/templates` body. Upload precedes create - because the layout coordinates only have meaning relative to this PDF. + The editor renders the pages, the user adjusts boxes, sources and + mappings, and the resulting fields array goes into `POST /api/v1/templates` + together with the returned `pdf_template_ref`. - Page dimensions are returned in PDF points (1/72 inch, origin bottom-left) - so the editor can map canvas positions to the same coordinate system the - `layout` fields use. + Page dimensions are in PDF points (1/72 inch, origin bottom-left), the + same coordinate system the `layout` fields use. tags: - templates requestBody: @@ -338,44 +374,29 @@ templates_pdf: type: string format: binary description: Blank fillable or flat PDF form. Max 50MB. + detect_fields: + type: boolean + default: true + description: Set false to skip field detection and only store + the PDF (draft completes immediately with no detected_fields) responses: - "201": - description: PDF stored and ready to be referenced by a template + "202": + description: PDF stored, field detection queued content: application/json: schema: - type: object - required: - - pdf_template_ref - - original_filename - - page_count - - pages - properties: - pdf_template_ref: - type: string - description: Opaque reference to the stored PDF, passed back in - the template body as pdf_template_ref - original_filename: - type: string - page_count: - type: integer - pages: - type: array - description: Per-page geometry in PDF points, index 0 = first page - items: - type: object - required: - - page - - width - - height - properties: - page: - type: integer - width: - type: number - height: - type: number + allOf: + - $ref: "../schemas/template-record.yaml#/TemplateDraft" + - type: object + properties: + job_id: + type: string + format: uuid + poll_url: + type: string example: + upload_id: "550e8400-e29b-41d4-a716-446655440080" + status: "processing" pdf_template_ref: "templates/uploads/550e8400-e29b-41d4-a716-446655440080.pdf" original_filename: "texas_sfm_incident.pdf" page_count: 2 @@ -386,6 +407,9 @@ templates_pdf: - page: 1 width: 612.0 height: 792.0 + retry_after_seconds: 5 + job_id: "550e8400-e29b-41d4-a716-446655440095" + poll_url: "/api/v1/templates/pdf/550e8400-e29b-41d4-a716-446655440080" "400": description: Missing file or malformed multipart request content: @@ -404,3 +428,141 @@ templates_pdf: application/json: schema: $ref: "../schemas/common.yaml#/ErrorResponse" + +template_pdf_draft: + get: + operationId: getTemplateDraft + summary: Get the template draft for an uploaded PDF + description: | + Returns the TemplateDraft for a previous PDF upload: page geometry plus, + once detection finishes, the auto-detected fields with their mapping + suggestions. Poll while status is "processing". A "failed" status means + detection failed but the PDF is stored and usable; the editor falls back + to manual box drawing. + tags: + - templates + parameters: + - name: upload_id + in: path + required: true + description: Upload identifier returned by POST /api/v1/templates/pdf + schema: + type: string + format: uuid + responses: + "200": + description: Current draft state + content: + application/json: + schema: + $ref: "../schemas/template-record.yaml#/TemplateDraft" + examples: + completed: + summary: Detection finished with suggestions + value: + upload_id: "550e8400-e29b-41d4-a716-446655440080" + status: "completed" + pdf_template_ref: "templates/uploads/550e8400-e29b-41d4-a716-446655440080.pdf" + original_filename: "texas_sfm_incident.pdf" + page_count: 2 + pages: + - page: 0 + width: 612.0 + height: 792.0 + - page: 1 + width: 612.0 + height: 792.0 + detected_fields: + - field: + field_name: "incident_number" + field_type: "string" + source: "schema" + required: false + incident_mapping: "report_metadata.incident_number" + layout: + page: 0 + x: 188.33 + y: 621.33 + width: 127.33 + height: 28.67 + detected_label: "Incident No." + suggestions: + - path: "report_metadata.incident_number" + label: "Incident number" + field_type: "string" + section: "report_metadata" + description: "Agency-assigned incident number" + score: 0.93 + - path: "dispatch.dispatch_number" + label: "Dispatch number" + field_type: "string" + section: "dispatch" + score: 0.61 + - field: + field_name: "field_7" + field_type: "string" + source: "manual" + required: false + layout: + page: 1 + x: 100.0 + y: 120.0 + width: 180.0 + height: 18.0 + detected_label: null + suggestions: [] + processing: + summary: Detection still running + value: + upload_id: "550e8400-e29b-41d4-a716-446655440080" + status: "processing" + pdf_template_ref: "templates/uploads/550e8400-e29b-41d4-a716-446655440080.pdf" + original_filename: "texas_sfm_incident.pdf" + page_count: 2 + pages: + - page: 0 + width: 612.0 + height: 792.0 + - page: 1 + width: 612.0 + height: 792.0 + retry_after_seconds: 5 + "404": + description: Upload not found + content: + application/json: + schema: + $ref: "../schemas/common.yaml#/ErrorResponse" + +template_source_pdf: + get: + operationId: downloadTemplatePdf + summary: Download a template's source PDF + description: | + Returns the blank source PDF a template's layout coordinates refer to. + The editor calls this when reopening a saved template so the user sees + the exact same pages and boxes they saved, ready to edit again. + tags: + - templates + parameters: + - name: template_id + in: path + required: true + description: Unique identifier of the template + schema: + type: string + format: uuid + responses: + "200": + description: The template's source PDF + content: + application/pdf: + schema: + type: string + format: binary + "404": + description: Template not found or has no source PDF + content: + application/json: + schema: + $ref: "../schemas/common.yaml#/ErrorResponse" diff --git a/contracts/schemas/common.yaml b/contracts/schemas/common.yaml index 0798f85..4fa2f05 100644 --- a/contracts/schemas/common.yaml +++ b/contracts/schemas/common.yaml @@ -82,6 +82,7 @@ AsyncJobResponse: - form_generation - batch_form_generation - report_generation + - template_field_detection status: $ref: "enums.yaml#/JobStatus" estimated_seconds: @@ -109,6 +110,7 @@ Job: - form_generation - batch_form_generation - report_generation + - template_field_detection status: $ref: "enums.yaml#/JobStatus" progress_percent: diff --git a/contracts/schemas/template-record.yaml b/contracts/schemas/template-record.yaml new file mode 100644 index 0000000..0ce8111 --- /dev/null +++ b/contracts/schemas/template-record.yaml @@ -0,0 +1,429 @@ +# Template & Configuration schemas + +TemplateSummary: + type: object + properties: + template_id: + type: string + format: uuid + form_type: + type: string + description: Unique form type identifier (built-in or custom jurisdiction) + display_name: + type: string + jurisdiction: + type: string + nullable: true + description: Jurisdiction code (e.g. "US-Federal", "US-CA", "US-GA") + agency_type: + type: string + nullable: true + version: + type: string + last_updated: + type: string + format: date + field_count: + type: integer + status: + type: string + enum: + - active + - legacy + - draft + +CreateTemplateRequest: + type: object + required: + - form_type + - display_name + - fields + properties: + form_type: + type: string + description: Unique form type identifier + display_name: + type: string + jurisdiction: + type: string + nullable: true + description: Jurisdiction code. Optional - the visual editor may register a + template before jurisdiction is assigned. + agency_type: + type: string + nullable: true + fields: + type: array + items: + $ref: "#/TemplateField" + source_standard: + type: string + nullable: true + description: Reference to the source standard (e.g. "NERIS v2.0", "NFIRS 5.0") + pdf_template_ref: + type: string + nullable: true + description: Reference to the PDF template file the layout coordinates apply to + +Template: + allOf: + - type: object + properties: + template_id: + type: string + format: uuid + version: + type: string + last_updated: + type: string + format: date + field_count: + type: integer + status: + type: string + enum: [active, legacy, draft] + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + - $ref: "#/CreateTemplateRequest" + +TemplateField: + type: object + description: | + One box on the form. The `source` decides how it gets its value: + - source=schema: `incident_mapping` is required, value is a contract lookup. + - source=static: `static_text` is required, stamped as-is on every form. + - source=manual: no mapping. User types the value on the review screen; + it is stored under custom_fields."{form_type}.{field_name}" in the + contract and filled from there. + - source=open: no mapping. `description` is required and is the + instruction the LLM uses to extract the value during the extraction + layer. The value is stored under custom_fields."{form_type}.{field_name}" + and reviewed like any other extracted value. + required: + - field_name + - field_type + - source + - required + properties: + field_name: + type: string + description: Field identifier within this template + field_type: + type: string + enum: + - string + - integer + - number + - boolean + - date + - datetime + - time + - enum + - text + - array + description: Data type of the field + source: + $ref: "../schemas/enums.yaml#/FieldSource" + required: + type: boolean + description: + type: string + nullable: true + description: | + Help text for the field. For source=open this is required and doubles + as the extraction instruction sent to the LLM, so write it as a plain + statement of what to pull from the narrative (e.g. "Name of the + insurance company covering the property"). + max_length: + type: integer + nullable: true + min_value: + type: number + nullable: true + max_value: + type: number + nullable: true + allowed_values: + type: array + items: + type: string + nullable: true + description: Valid values for enum fields + incident_mapping: + type: string + nullable: true + description: | + JSON path in the FireForm incident contract this field pulls its value + from (e.g. "fire.cause_category"). Required when source=schema, null + for every other source. + static_text: + type: string + nullable: true + description: | + Fixed text drawn into the field. Required when source=static, null for + every other source. + default_value: + nullable: true + description: Default value if the mapped incident field is null + unit: + type: string + nullable: true + description: | + Target unit for the stamped value when the form wants something other + than the incident contract's SI unit (e.g. "acres" for a field mapped + to wildland.area_burned_ha, "fahrenheit" for temperature_c). The + mapper converts during generation. Null means stamp the value as + stored. + layout: + nullable: true + description: Visual placement of the field on the PDF. Null for fields with + no fixed position. + allOf: + - $ref: "#/TemplateFieldLayout" + +TemplateFieldLayout: + type: object + description: | + Visual placement of a field on the PDF page. Coordinates are in PDF points + with the origin at the bottom-left of the page, so the field box runs from + start = (x, y) to end = (x + width, y + height). The editor is responsible + for converting rendered-canvas pixels to PDF points before saving. + required: + - page + - x + - y + - width + - height + properties: + page: + type: integer + description: Zero-based page index + x: + type: number + description: Lower-left X of the box, in PDF points + y: + type: number + description: Lower-left Y of the box, in PDF points (origin bottom-left) + width: + type: number + height: + type: number + font: + type: string + default: Helvetica + font_size: + type: number + default: 10 + color: + type: string + default: "#000000" + description: Hex color, e.g. "#000000" + align: + type: string + enum: + - left + - center + - right + default: left + +TemplateDraft: + type: object + description: | + Result of uploading a PDF for template authoring. Carries everything the + visual editor needs: the stored PDF reference, page geometry, and the + fields auto-detected by commonforms with mapping suggestions. Each + detected field is already a full TemplateField draft, so the editor can + let the user adjust boxes, change sources, accept or override suggested + mappings, add or delete fields, and then send the exact same array in + POST /api/v1/templates. Fields the detector could not interpret still + come back with a layout box and empty mapping; coordinates alone are a + valid starting point. + required: + - upload_id + - status + - pdf_template_ref + - page_count + - pages + properties: + upload_id: + type: string + format: uuid + status: + type: string + enum: + - processing + - completed + - failed + description: Field detection status. The PDF itself is stored as soon + as the upload returns; only detection is asynchronous. + pdf_template_ref: + type: string + description: Opaque reference to the stored PDF, passed back in the + template body as pdf_template_ref + original_filename: + type: string + page_count: + type: integer + pages: + type: array + description: Per-page geometry in PDF points, index 0 = first page + items: + type: object + required: + - page + - width + - height + properties: + page: + type: integer + width: + type: number + height: + type: number + detected_fields: + type: array + description: Present when status is completed. Empty when the detector + found nothing; the user then draws boxes from scratch. + items: + $ref: "#/DraftField" + detection_error: + type: string + nullable: true + description: Present when status is failed. The upload is still usable; + the editor falls back to manual box drawing. + retry_after_seconds: + type: integer + description: Polling hint, present while status is processing + +DraftField: + type: object + description: | + One auto-detected field plus ranked mapping suggestions. + + Detected labels are messy ("Incident No.:", stray colons, no text at + all), so the suggester normalizes the label before scoring: lowercase, + strip punctuation, collapse whitespace, expand common form abbreviations + ("no" → "number", "dt" → "date", "addr" → "address", "tel" → "phone", + "dob" → "date of birth"). Scoring then runs against the schema catalog + (names, x-aliases and descriptions, same index and scorer as + GET /api/v1/schema/fields). + + Two thresholds apply. Below the confidence floor, `suggestions` is empty: + an empty list with a good search box beats a wrong guess, because users + trust pre-filled values too much. Above the higher auto-apply threshold, + the top suggestion is pre-applied as the field's incident_mapping and + source is set to schema; in between, suggestions are listed but nothing + is pre-applied. + required: + - field + properties: + field: + $ref: "#/TemplateField" + detected_label: + type: string + nullable: true + description: Raw label text found near the box on the PDF, kept verbatim + (not normalized) so the editor can show what the suggestion was based on + suggestions: + type: array + description: Ranked incident-contract mapping suggestions for this box, + best first. Empty when nothing scored above the confidence floor. + items: + $ref: "#/MappingSuggestion" + +MappingSuggestion: + type: object + required: + - path + - score + properties: + path: + type: string + description: Incident-contract JSON path (e.g. "location.postal_code") + label: + type: string + description: Human-friendly name of the schema field + field_type: + type: string + description: Data type of the schema field + section: + type: string + description: Top-level contract section the path belongs to + description: + type: string + description: The schema field's own description from the contract + score: + type: number + description: Match confidence 0-1 from the fuzzy matcher (no LLM) + +SchemaFieldEntry: + type: object + description: One searchable entry in the incident-contract field catalog. + required: + - path + - field_type + - section + properties: + path: + type: string + description: Dotted JSON path into the incident contract + label: + type: string + description: Human-friendly name derived from the field name + field_type: + type: string + section: + type: string + description: Top-level contract section (fire, location, casualties, ...) + description: + type: string + nullable: true + enum_values: + type: array + items: + type: string + nullable: true + pii: + type: boolean + description: True when the contract marks this field x-pii + aliases: + type: array + items: + type: string + description: Alternate names the search index also matches (zip for + postal_code, victim for casualty, and similar). Sourced from the + field's x-aliases entry in incident-contract.yaml - the contract is + the single place aliases are defined; the catalog builder reads them + from the schema file at startup, nothing re-declares them in code. + +SchemaFieldSearchResponse: + type: object + required: + - total + - fields + properties: + query: + type: string + nullable: true + description: Echo of the q parameter, null when listing the full catalog + total: + type: integer + schema_version: + type: string + description: Incident-contract version the catalog was built from + fields: + type: array + description: Ranked matches when q is given, full catalog otherwise + items: + allOf: + - $ref: "#/SchemaFieldEntry" + - type: object + properties: + score: + type: number + nullable: true + description: Present only for search results diff --git a/contracts/schemas/template.yaml b/contracts/schemas/template.yaml deleted file mode 100644 index f9ce921..0000000 --- a/contracts/schemas/template.yaml +++ /dev/null @@ -1,203 +0,0 @@ -# Template & Configuration schemas - -TemplateSummary: - type: object - properties: - template_id: - type: string - format: uuid - form_type: - type: string - description: Unique form type identifier (built-in or custom jurisdiction) - display_name: - type: string - jurisdiction: - type: string - nullable: true - description: Jurisdiction code (e.g. "US-Federal", "US-CA", "US-GA") - agency_type: - type: string - nullable: true - version: - type: string - last_updated: - type: string - format: date - field_count: - type: integer - status: - type: string - enum: - - active - - legacy - - draft - -CreateTemplateRequest: - type: object - required: - - form_type - - display_name - - fields - properties: - form_type: - type: string - description: Unique form type identifier - display_name: - type: string - jurisdiction: - type: string - nullable: true - description: Jurisdiction code. Optional — the visual editor may register a - template before jurisdiction is assigned. - agency_type: - type: string - nullable: true - fields: - type: array - items: - $ref: "#/TemplateField" - source_standard: - type: string - nullable: true - description: Reference to the source standard (e.g. "NERIS v2.0", "NFIRS 5.0") - pdf_template_ref: - type: string - nullable: true - description: Reference to the PDF template file the layout coordinates apply to - -Template: - allOf: - - type: object - properties: - template_id: - type: string - format: uuid - version: - type: string - last_updated: - type: string - format: date - field_count: - type: integer - status: - type: string - enum: [active, legacy, draft] - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - - $ref: "#/CreateTemplateRequest" - -TemplateField: - type: object - required: - - field_name - - field_type - - required - properties: - field_name: - type: string - description: Field identifier within this template - field_type: - type: string - enum: - - string - - integer - - number - - boolean - - date - - datetime - - time - - enum - - text - - array - description: Data type of the field - required: - type: boolean - description: - type: string - nullable: true - max_length: - type: integer - nullable: true - min_value: - type: number - nullable: true - max_value: - type: number - nullable: true - allowed_values: - type: array - items: - type: string - nullable: true - description: Valid values for enum fields - incident_mapping: - type: string - nullable: true - description: | - JSON path in the FireForm incident schema this field pulls its value from - (e.g. "fire.cause_category"). Null for a static field. Exactly one of - incident_mapping or static_text is expected. - static_text: - type: string - nullable: true - description: | - Fixed text drawn into the field instead of a mapped value. Null for a - data-mapped field. Mutually exclusive with incident_mapping. - default_value: - nullable: true - description: Default value if the mapped incident field is null - layout: - nullable: true - description: Visual placement of the field on the PDF. Null for fields with - no fixed position. - allOf: - - $ref: "#/TemplateFieldLayout" - -TemplateFieldLayout: - type: object - description: | - Visual placement of a field on the PDF page. Coordinates are in PDF points - with the origin at the bottom-left of the page, so the field box runs from - start = (x, y) to end = (x + width, y + height). The editor is responsible - for converting rendered-canvas pixels to PDF points before saving. - required: - - page - - x - - y - - width - - height - properties: - page: - type: integer - description: Zero-based page index - x: - type: number - description: Lower-left X of the box, in PDF points - y: - type: number - description: Lower-left Y of the box, in PDF points (origin bottom-left) - width: - type: number - height: - type: number - font: - type: string - default: Helvetica - font_size: - type: number - default: 10 - color: - type: string - default: "#000000" - description: Hex color, e.g. "#000000" - align: - type: string - enum: - - left - - center - - right - default: left