{"openapi":"3.1.0","info":{"title":"Customer-AI API","version":"1.0.0","description":"The Customer-AI API gives AI agents and backend integrations direct access to knowledge approved by operators — for example, utilities such as electricity networks — the same approach applies equally to operators in other regulated-service sectors. Every response is drawn exclusively from operator-reviewed knowledge objects — no hallucinated answers, full provenance.  **Authentication**: include either an `X-API-Key` header or an `Authorization: Bearer <token>` header to scope requests to your organisation's knowledge. Anonymous requests are also accepted and use the `organisation` parameter instead.  **Discovery**: see `/llms.txt` for a machine-readable summary of this service. The spec at `/api/v1/openapi.json` is cached in memory and refreshed automatically every 5 minutes so org codes and journey keys stay current without a server restart.","contact":{"name":"Customer-AI","url":"https://customer-ai.com"}},"servers":[{"url":"https://customer-ai.com/api/v1","description":"Customer-AI v1 API"}],"security":[],"components":{"securitySchemes":{"ApiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key issued per organisation. When present, scopes all results to that organisation's approved knowledge."},"BearerAuth":{"type":"http","scheme":"bearer","description":"Bearer token issued per organisation (used by ChatGPT / OpenAI-platform agents and other OAuth 2.0–style tooling). Supply as `Authorization: Bearer <token>`. Scopes all results to that organisation's approved knowledge, identical to X-API-Key."}},"schemas":{"Organisation":{"type":"object","properties":{"organisation":{"type":"string","description":"Canonical organisation code."},"jurisdictions":{"type":"array","items":{"type":"string"},"description":"ISO 3166-2 jurisdiction codes this organisation covers."},"objectCount":{"type":"integer","description":"Number of published knowledge objects."},"acceptedAliases":{"type":"array","items":{"type":"string"},"description":"Alternative names accepted by the `organisation` parameter of other endpoints."}},"required":["organisation","jurisdictions","objectCount","acceptedAliases"]},"Journey":{"type":"object","properties":{"key":{"type":"string","description":"Journey identifier to use in other endpoints."},"name":{"type":"string"},"description":{"type":"string"},"objectCount":{"type":"integer"}},"required":["key","name","description","objectCount"]},"SubmittedClassificationTag":{"type":"object","description":"One of the caller's submitted classificationTags that was valid and actually applied to this call's ranking.","properties":{"tag":{"type":"string","description":"dataName of the accepted classification tag."},"priority":{"type":"string","enum":["suppress","enhance","boost"],"description":"Priority as submitted by the caller."}},"required":["tag","priority"]},"ClassificationTagSummary":{"type":"object","description":"An approved classification tag, in the shape exposed to consuming agents.","properties":{"dataName":{"type":"string","description":"Stable identifier — pass this as `tag` in a classificationTags entry."},"displayName":{"type":"string"},"description":{"type":"string"}},"required":["dataName","displayName","description"]},"KnowledgeObject":{"type":"object","description":"A single approved knowledge proposition with provenance.","properties":{"id":{"type":["integer","null"]},"logicalId":{"type":"string"},"namespaceCode":{"type":"string"},"title":{"type":"string"},"objectType":{"type":"string"},"proposition":{"type":"string"},"jurisdictionCode":{"type":"string"},"utilityRole":{"type":"string"},"organisationCode":{"type":"string"},"journeyCodes":{"type":"array","items":{"type":"string"}},"conceptIds":{"type":"array","items":{"type":"string"}},"classificationTags":{"type":"array","items":{"type":"string"},"description":"Data names of the classification tags assigned to this object."},"approvalStatus":{"type":"string"},"effectiveFrom":{"type":["string","null"]},"effectiveTo":{"type":["string","null"]},"versionLabel":{"type":"string"},"certaintyClass":{"type":"string"},"consistencyStatus":{"type":"string"},"stepOrder":{"type":["integer","null"]},"primarySourceId":{"type":["string","null"]},"createdAt":{"type":["string","null"]}}},"PossibleOutcome":{"type":"object","properties":{"outcome":{"type":"string","description":"Plain-language description of the possible outcome."},"certaintyClass":{"type":"string","enum":["confirmed","may_be_eligible","may_be_considered","insufficient_information"],"description":"Certainty label from the approved knowledge object."},"basisLogicalId":{"type":"string","description":"Logical ID of the knowledge object this outcome was drawn from."}},"required":["outcome","certaintyClass","basisLogicalId"]},"ProvenanceEntry":{"type":"object","properties":{"logicalId":{"type":"string"},"objectType":{"type":"string"},"primarySourceId":{"type":["string","null"]},"versionLabel":{"type":"string"},"effectiveFrom":{"type":["string","null"]},"approvalStatus":{"type":"string"}}},"ScenarioClassification":{"type":"object","properties":{"journey":{"type":"string","description":"Classified journey key."},"journeyName":{"type":"string","description":"Human-readable journey name."},"jurisdiction":{"type":"string","description":"Applicable jurisdiction code (e.g. AU-SA)."},"organisation":{"type":"string","description":"Organisation code used for retrieval."},"responsibleParty":{"type":"string","description":"The organisation responsible for this journey, as stated in the approved knowledge base."},"confidence":{"type":"string","enum":["high","medium","low"],"description":"Classification confidence level."},"signals":{"type":"array","items":{"type":"string"},"description":"Keywords or phrases that drove the classification."}},"required":["journey","journeyName","jurisdiction","organisation","confidence","signals"]},"RetrievedObject":{"type":"object","description":"A knowledge object retrieved by the relevance engine, with its score, matched terms, and a diagnostic breakdown of how the score was composed.","properties":{"object":{"$ref":"#/components/schemas/KnowledgeObject"},"relevanceScore":{"type":"number","description":"Relevance score (higher = more relevant)."},"matchedTerms":{"type":"array","items":{"type":"string"},"description":"Terms in the query that matched this object."},"titleMatchedTerms":{"type":"array","items":{"type":"string"},"description":"Subset of matchedTerms found in the object's title (higher weight)."},"propositionMatchedTerms":{"type":"array","items":{"type":"string"},"description":"Subset of matchedTerms found in the object's proposition (lower weight)."},"scoreBreakdown":{"type":"object","description":"Diagnostic-only decomposition of relevanceScore into the scoring pipeline's actual stages (never recalculated or estimated for display). title + proposition + structural + classificationTags sums to relevanceScore, subject to independent rounding of each component.","properties":{"title":{"type":"number","description":"Contribution from query terms matching the object's title."},"proposition":{"type":"number","description":"Contribution from query terms matching the object's proposition."},"structural":{"type":"number","description":"Contribution from the flat structural/exclusion type bonus."},"classificationTags":{"type":"number","description":"Contribution from your submitted classificationTags that matched this object's own tags."}},"required":["title","proposition","structural","classificationTags"]},"matchedClassificationTags":{"type":"array","description":"Which of your submitted classificationTags (if any) matched this object's own classification tags, and how much each contributed to relevanceScore. Empty when you didn't submit classificationTags or none matched this object.","items":{"type":"object","properties":{"tag":{"type":"string"},"priority":{"type":"string","enum":["suppress","enhance","boost"]},"scoreContribution":{"type":"number"}},"required":["tag","priority","scoreContribution"]}},"classificationTagScore":{"type":"number","description":"Sum of matchedClassificationTags[].scoreContribution — same value as scoreBreakdown.classificationTags. 0 when none matched."}},"required":["object","relevanceScore","matchedTerms"]},"AgentFeedbackRequest":{"type":"object","required":["utility","resolutionStatus"],"description":"Utility feedback submitted by a consuming AI agent after it has used Customer-AI knowledge to attempt to resolve a customer problem. Call POST /api/v1/feedback (or the submit_agent_feedback MCP tool) once per interaction, after you have generated your response to the customer.","properties":{"interactionId":{"type":"string","description":"The evaluationId (REST) or interactionId (MCP) returned by the /ask or /scenario call this feedback relates to. Always pass this so operators can trace gaps back to their source evaluation."},"utility":{"type":"string","enum":["high","partial","none"],"description":"'high' = Customer-AI materially enabled resolution. 'partial' = useful but additional knowledge or data was also required. 'none' = response did not assist."},"resolutionStatus":{"type":"string","enum":["resolved","partial","unresolved"],"description":"Whether the customer problem was ultimately resolved."},"usedObjectIds":{"type":"array","items":{"type":"string"},"description":"logicalId values of the knowledge objects you actually used when constructing the customer response."},"gapCategory":{"type":"string","enum":["knowledge_gap","customer_information_gap","coverage_gap","retrieval_failure"],"description":"Category of the gap that prevented full resolution. Omit when utility=high and resolutionStatus=resolved. 'knowledge_gap' — a rule, eligibility condition, or process that Customer-AI should contain but does not. 'customer_information_gap' — missing customer data (bill, meter reading, account details) that is not in Customer-AI. 'coverage_gap' — the journey, organisation, or jurisdiction is not supported. 'retrieval_failure' — the relevant knowledge appears to exist but was not returned by the retrieval engine."},"gapType":{"type":"string","description":"Sub-type within gapCategory. knowledge_gap sub-types: rule, eligibility, exclusion, responsibility, evidence_requirement, process, escalation, historical_rule, safety. customer_information_gap: bill, meter_data, account_data, event_details, customer_status. coverage_gap: journey, organisation, jurisdiction."},"gapDescription":{"type":"string","description":"Precise description of the missing knowledge — what the agent needed, not a repeat of the customer question. Include product, date ranges, jurisdiction, and conditions to make this an actionable extraction brief."},"importance":{"type":"string","enum":["blocking","material","minor"],"description":"'blocking' = could not reliably resolve without it. 'material' = could answer but confidence or usefulness was significantly reduced. 'minor' = would have improved the answer but did not affect the core outcome."},"alternativeSourceUsed":{"type":"boolean","description":"Set to true if you used a source outside Customer-AI to fill the gap."},"agentComment":{"type":"string","description":"Optional free-text notes about the interaction or the gap."},"agentModel":{"type":"string","description":"Optional. The model name/version of the calling AI agent (e.g. 'claude-sonnet-4', 'gpt-4o'). Used only for aggregate usage analytics."},"agentPlatform":{"type":"string","description":"Optional. The platform or product hosting the agent (e.g. 'Claude Desktop', 'ChatGPT', 'custom'). Used only for analytics."},"endUserRegion":{"type":"string","description":"Optional. The end user's approximate region or state (e.g. 'SA, Australia'). Used only for aggregate analytics."}}},"AgentFeedbackResult":{"type":"object","description":"Confirmation that feedback was accepted.","properties":{"status":{"type":"string","enum":["accepted"],"description":"Always 'accepted' on success."},"feedbackId":{"type":"integer","description":"Database ID of the stored feedback record."},"createdAt":{"type":"string","format":"date-time"}},"required":["status","feedbackId","createdAt"]},"ForeclosedRoute":{"type":"object","description":"A named claims route that is foreclosed by an applicable exclusion knowledge object. Do NOT present any listed route as a straightforward option. A route listed here is explicitly suppressed by an approved exclusion from the knowledge base. It may remain conditionally relevant only if the customer's facts satisfy a separately qualifying condition not covered by the exclusion (e.g. a maintenance-failure allegation can open the Negligence route even when a weather exclusion is also present — but requires specific causation evidence). GSL entries never appear here; GSL payment eligibility is a separate automatic entitlement.","properties":{"route":{"type":"string","description":"Canonical name of the foreclosed claims route (e.g. 'Small Claims Scheme', 'Negligence and Bad Faith Claims')."},"excludedBy":{"type":"string","description":"The exclusion proposition that forecloses this route."},"relevanceScore":{"type":"number","description":"Keyword-relevance score of the underlying exclusion object to the scenario. A tangential foreclosure (retrieved only via the always-applied exclusion structural bonus) scores 0; a foreclosure backed by genuine keyword overlap with the scenario scores higher. Used only to order content within the assembled answer — it has no bearing on suppression, which is unconditional regardless of this score. Do not use this to decide whether the route is actually foreclosed."}},"required":["route","excludedBy","relevanceScore"]},"EvidenceGroups":{"type":"object","description":"Evidence requirements grouped by purpose and timing. incident: evidence establishing the event occurred (time, date, address, records). causation: evidence linking property damage to the incident (photographs, assessments). value: evidence supporting the amount claimed (repair/replacement quotes, like-for-like costs). conditional: evidence the operator may request only if it cannot independently confirm the incident — do NOT present these as mandatory upfront requirements. All four keys are always present; empty arrays mean no items in that group.","properties":{"incident":{"type":"array","items":{"type":"string"},"description":"Evidence to establish the incident occurred."},"causation":{"type":"array","items":{"type":"string"},"description":"Evidence linking the damage to the incident."},"value":{"type":"array","items":{"type":"string"},"description":"Evidence supporting the amount claimed."},"conditional":{"type":"array","items":{"type":"string"},"description":"Evidence the operator may request only if it cannot confirm the incident independently."}},"required":["incident","causation","value","conditional"]},"AskResult":{"type":"object","description":"Response from POST /ask. The answer field is a plain-language synthesis for direct customer display. After using this to respond to the customer, call POST /api/v1/feedback with the evaluationId to report utility and any knowledge gaps.","properties":{"evaluationId":{"type":"integer","description":"Database ID of the stored evaluation. Pass this as interactionId when submitting feedback via POST /api/v1/feedback."},"question":{"type":"string"},"customerQuery":{"type":["string","null"],"description":"Echoes the customerQuery from the request, or null when the caller did not send one."},"classification":{"$ref":"#/components/schemas/ScenarioClassification"},"emergencyFlag":{"type":"boolean","description":"True if safety/emergency signals were detected."},"emergencyRouting":{"type":["string","null"],"description":"Emergency routing instruction, or null."},"answer":{"type":"string","description":"Plain-language answer assembled from approved knowledge objects (mapped from guidanceSummary). Suitable for direct display to customers."},"processSteps":{"type":"array","items":{"type":"string"},"description":"Ordered steps the customer should follow."},"escalationRoutes":{"type":"array","items":{"type":"string"},"description":"Actionable escalation guidance from applicable escalation_route knowledge objects, ordered by step_order. Present only when relevant escalation routes were retrieved."},"possibleOutcomes":{"type":"array","items":{"$ref":"#/components/schemas/PossibleOutcome"}},"eligibilityRules":{"type":"array","items":{"type":"string"},"description":"Eligibility conditions from approved knowledge objects. Check these alongside exclusions before presenting a claims route as available."},"exclusions":{"type":"array","items":{"type":"string"},"description":"Applicable exclusions from approved knowledge objects. Evaluate these before considering any claims route — an applicable exclusion narrows or rules out most routing options. OMITTED ENTIRELY for journeys where the operator has turned off raw exclusion metadata in this response (see `includeExclusionsInResponse` in Journey Design → Retrieval settings). When omitted, `answer`/`customerAnswer` and `agentInstructions` still fully reflect any applicable exclusions or foreclosed routes in prose — route-foreclosure protection is never silently disabled by this setting, only the raw structured field is hidden."},"exclusionRelevance":{"type":"array","items":{"type":"number"},"description":"Keyword-relevance score for each entry in `exclusions`, at the same array index (parallel array). The always-applied exclusion structural bonus is subtracted out, so a tangential exclusion (retrieved via the bonus alone) scores 0 and a decisive one (genuine keyword overlap with the scenario) scores higher. For ranking/highlighting only — never bears on suppression, which is unconditional. Subject to the same `includeExclusionsInResponse` opt-out as `exclusions` (OMITTED ENTIRELY when off)."},"foreClosedRoutes":{"type":"array","items":{"$ref":"#/components/schemas/ForeclosedRoute"},"description":"Machine-readable list of claims routes foreclosed by retrieved exclusion KOs. Do NOT present any listed route as an available option. Empty array means no route is explicitly foreclosed (exclusions may still narrow eligibility — always check `exclusions`). OMITTED ENTIRELY (not even an empty array) for journeys where the operator has turned off raw exclusion metadata in this response — when omitted, rely on `answer`/`customerAnswer` and `agentInstructions`, which still carry the same foreclosure guidance in prose."},"guidanceRelevance":{"type":"number","description":"Highest relevanceScore among the customer_explanation objects backing `answer`/`guidanceSummary` (0 when no explanation was retrieved). Comparable to `exclusionRelevance` and `foreClosedRoutes[].relevanceScore` for ranking/highlighting. Subject to the same `includeExclusionsInResponse` opt-out (OMITTED ENTIRELY when off)."},"evidenceRequirements":{"$ref":"#/components/schemas/EvidenceGroups"},"gaps":{"type":"array","items":{"type":"string"},"description":"Known content gaps — areas where approved knowledge is absent."},"limitations":{"type":"array","items":{"type":"string"}},"humanReviewRequired":{"type":"boolean"},"humanReviewReasons":{"type":"array","items":{"type":"string"}},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/ProvenanceEntry"}},"createdAt":{"type":"string","format":"date-time"},"interaction":{"allOf":[{"$ref":"#/components/schemas/AgentInteraction"}],"description":"Present only when this journey has Interactions configured and no safety/exclusion/human-review constraint suppresses it. Additive — omitted for journeys with no Interactions configuration."},"display":{"type":"array","items":{"$ref":"#/components/schemas/DisplayComponent"},"description":"Structured presentation components for the fields above. Additive and optional; omitted when there is nothing to structure."},"presentationInstructions":{"allOf":[{"$ref":"#/components/schemas/PresentationInstructions"}],"description":"Present whenever `interaction` or `display` is present."},"ignoredClassificationTags":{"type":"array","items":{"type":"string"},"description":"Submitted classificationTags values that were dropped because they did not match an approved tag data name. Empty when every submitted tag was valid or none were submitted."},"submittedClassificationTags":{"type":"array","items":{"$ref":"#/components/schemas/SubmittedClassificationTag"},"description":"The submitted classificationTags that were valid and actually applied to this call's ranking — the counterpart to ignoredClassificationTags. Empty when none were submitted or none were valid. If this is non-empty but every retrievedObjects entry still shows an empty matchedClassificationTags, the submitted tags were accepted but didn't match any retrieved object's own tags."}},"required":["evaluationId","question","classification","emergencyFlag","answer","processSteps","escalationRoutes","possibleOutcomes","eligibilityRules","evidenceRequirements","gaps","limitations","humanReviewRequired","humanReviewReasons","provenance","createdAt"]},"ScenarioResult":{"type":"object","description":"Full evaluation result from POST /scenario. Includes raw retrieval trace and guidanceSummary.","properties":{"id":{"type":"integer","description":"Database ID of the stored evaluation."},"scenarioText":{"type":"string"},"customerQuery":{"type":["string","null"],"description":"Echoes the customerQuery from the request, or null when the caller did not send one."},"classification":{"$ref":"#/components/schemas/ScenarioClassification"},"emergencyFlag":{"type":"boolean"},"emergencyRouting":{"type":["string","null"]},"guidanceSummary":{"type":"string","description":"Plain-language guidance assembled from approved knowledge objects."},"processSteps":{"type":"array","items":{"type":"string"}},"escalationRoutes":{"type":"array","items":{"type":"string"},"description":"Actionable escalation guidance from applicable escalation_route knowledge objects, ordered by step_order."},"possibleOutcomes":{"type":"array","items":{"$ref":"#/components/schemas/PossibleOutcome"}},"eligibilityRules":{"type":"array","items":{"type":"string"},"description":"Eligibility conditions from approved knowledge objects. Check these alongside exclusions before presenting a claims route as available."},"exclusions":{"type":"array","items":{"type":"string"},"description":"Applicable exclusions from approved knowledge objects. Evaluate these before considering any claims route — an applicable exclusion narrows or rules out most routing options. OMITTED ENTIRELY for journeys where the operator has turned off raw exclusion metadata in this response (see `includeExclusionsInResponse` in Journey Design → Retrieval settings). When omitted, `guidanceSummary`/`customerAnswer` and `agentInstructions` still fully reflect any applicable exclusions or foreclosed routes in prose — route-foreclosure protection is never silently disabled by this setting, only the raw structured field is hidden."},"exclusionRelevance":{"type":"array","items":{"type":"number"},"description":"Keyword-relevance score for each entry in `exclusions`, at the same array index (parallel array). The always-applied exclusion structural bonus is subtracted out, so a tangential exclusion (retrieved via the bonus alone) scores 0 and a decisive one (genuine keyword overlap with the scenario) scores higher. For ranking/highlighting only — never bears on suppression, which is unconditional. Subject to the same `includeExclusionsInResponse` opt-out as `exclusions` (OMITTED ENTIRELY when off)."},"foreClosedRoutes":{"type":"array","items":{"$ref":"#/components/schemas/ForeclosedRoute"},"description":"Machine-readable list of claims routes foreclosed by retrieved exclusion KOs. Do NOT present any listed route as an available option. OMITTED ENTIRELY (not even an empty array) for journeys where the operator has turned off raw exclusion metadata in this response — when omitted, rely on `guidanceSummary`/`customerAnswer` and `agentInstructions`, which still carry the same foreclosure guidance in prose."},"guidanceRelevance":{"type":"number","description":"Highest relevanceScore among the customer_explanation objects backing `guidanceSummary` (0 when no explanation was retrieved). Comparable to `exclusionRelevance` and `foreClosedRoutes[].relevanceScore` for ranking/highlighting. Subject to the same `includeExclusionsInResponse` opt-out (OMITTED ENTIRELY when off)."},"evidenceRequirements":{"$ref":"#/components/schemas/EvidenceGroups"},"gaps":{"type":"array","items":{"type":"string"}},"limitations":{"type":"array","items":{"type":"string"}},"humanReviewRequired":{"type":"boolean"},"humanReviewReasons":{"type":"array","items":{"type":"string"}},"retrievedObjects":{"type":"array","items":{"$ref":"#/components/schemas/RetrievedObject"},"description":"Every knowledge object the engine retrieved, with relevance scores. Use for debugging the retrieval pipeline."},"provenance":{"type":"array","items":{"$ref":"#/components/schemas/ProvenanceEntry"}},"createdAt":{"type":"string","format":"date-time"},"interaction":{"allOf":[{"$ref":"#/components/schemas/AgentInteraction"}],"description":"Present only when this journey has Interactions configured and no safety/exclusion/human-review constraint suppresses it. Additive — omitted for journeys with no Interactions configuration."},"display":{"type":"array","items":{"$ref":"#/components/schemas/DisplayComponent"},"description":"Structured presentation components for the fields above. Additive and optional; omitted when there is nothing to structure."},"presentationInstructions":{"allOf":[{"$ref":"#/components/schemas/PresentationInstructions"}],"description":"Present whenever `interaction` or `display` is present."},"ignoredClassificationTags":{"type":"array","items":{"type":"string"},"description":"Submitted classificationTags values that were dropped because they did not match an approved tag data name. Empty when every submitted tag was valid or none were submitted."},"submittedClassificationTags":{"type":"array","items":{"$ref":"#/components/schemas/SubmittedClassificationTag"},"description":"The submitted classificationTags that were valid and actually applied to this call's ranking — the counterpart to ignoredClassificationTags. Empty when none were submitted or none were valid. If this is non-empty but every retrievedObjects entry still shows an empty matchedClassificationTags, the submitted tags were accepted but didn't match any retrieved object's own tags."}},"required":["id","scenarioText","classification","emergencyFlag","guidanceSummary","processSteps","escalationRoutes","possibleOutcomes","eligibilityRules","evidenceRequirements","gaps","limitations","humanReviewRequired","humanReviewReasons","retrievedObjects","provenance","createdAt"]},"Source":{"type":"object","description":"A knowledge source (regulatory document, policy, standard, etc.) with attribution metadata.","properties":{"sourceId":{"type":"string","description":"Unique source identifier."},"title":{"type":"string","description":"Human-readable title of the source document."},"sourceType":{"type":"string","description":"Type of source (e.g. 'regulation', 'policy', 'standard')."},"issuingAuthority":{"type":["string","null"],"description":"Organisation or body that issued this document."},"authorityClass":{"type":["string","null"],"description":"Authority classification (e.g. 'primary_legislation', 'regulatory_code')."},"jurisdictionCode":{"type":["string","null"],"description":"ISO 3166-2 jurisdiction code (e.g. 'AU-SA')."},"utilityRole":{"type":["string","null"],"description":"Utility role this source applies to (e.g. 'distributor', 'retailer')."},"organisationCode":{"type":["string","null"],"description":"Organisation this source is scoped to."},"authoritativeUrl":{"type":["string","null"],"description":"Canonical URL to the authoritative version of this document."},"effectiveFrom":{"type":["string","null"],"description":"Date from which this source version is effective."},"versionLabel":{"type":["string","null"],"description":"Version label (e.g. 'v2.1', '2024 edition')."},"approvalStatus":{"type":["string","null"],"description":"Approval status of this source in the platform."},"confidentiality":{"type":["string","null"],"description":"Confidentiality classification (e.g. 'public', 'internal')."}},"required":["sourceId","title","sourceType"]},"Error":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]},"InteractionOption":{"type":"object","properties":{"value":{"type":"string","description":"Machine value to send back if this option is chosen."},"label":{"type":"string","description":"Customer-facing label for this option."},"description":{"type":"string","description":"Optional supporting detail for this option."}},"required":["value","label"]},"AgentInteraction":{"type":"object","description":"The single next-best interactive control to offer the customer, derived from this journey's Interactions configuration (operator instructions plus linked 'interaction' knowledge objects). Prefer rendering this as native UI (buttons, a picker, a form field) over restating it as prose. Omitted entirely when the journey has no Interactions configured, or when a safety/exclusion/ human-review constraint takes precedence — those always come first in the response.","properties":{"type":{"type":"string","enum":["single_select","multi_select","date","number","text","upload","confirm","checklist","action","handoff"],"description":"How to render this interaction."},"purpose":{"type":"string","description":"Short machine label for why this is being offered, e.g. 'clarification', 'next_step', 'follow_up'."},"question":{"type":"string","description":"The prompt to present to the customer."},"options":{"type":"array","items":{"$ref":"#/components/schemas/InteractionOption"}},"agentNote":{"type":"string","description":"Operator-authored guidance for the calling agent's own use — not customer-facing prose."},"sourceKoIds":{"type":"array","items":{"type":"string"},"description":"Logical IDs of the Interaction knowledge objects that informed this suggestion."}},"required":["type","purpose","question"]},"DisplayComponent":{"type":"object","description":"A presentation-agnostic component naming how a piece of the already-assembled answer should render (e.g. as a warning banner vs. a checklist), instead of being flattened into a single paragraph.","properties":{"type":{"type":"string","enum":["outcome","eligibility","warning","process","evidence","source","journey_status"]},"title":{"type":"string"},"items":{"type":"array","items":{"type":"string"}},"severity":{"type":"string","enum":["info","warning","critical"]}},"required":["type","title","items"]},"PresentationInstructions":{"type":"object","properties":{"preferInteractive":{"type":"boolean","description":"True when `interaction` is present and should be preferred over an open-ended follow-up question."},"doNotFlattenInteractionToProse":{"type":"boolean","description":"True when `interaction`/`display` should render as native components, not be paraphrased into a paragraph."},"allowConversationalFallback":{"type":"boolean","description":"True when, if the surface cannot render structured content, falling back to plain conversational text is acceptable."}},"required":["preferInteractive","doNotFlattenInteractionToProse","allowConversationalFallback"]}}},"paths":{"/ask":{"get":{"operationId":"askQuestionGet","summary":"Ask a customer question (GET — no credentials required)","description":"Ask a natural-language question via a plain GET request. Designed for web-browsing AI assistants (Perplexity, ChatGPT browse mode, web-crawling agents) that can only fetch a URL via GET and cannot set custom request headers or POST a body. Returns the same JSON shape as `POST /ask`, including a top-level `answer` field. No API key is required. If an `X-API-Key` header is present it scopes results to that organisation; otherwise cross-org published knowledge is used.  **Coverage gaps**: when the scenario cannot be confidently classified, the response has `status: \"coverage_gap\"` instead of a normal answer. It also has `coverageGapCategory`: `missing_customer_context` means the topic is in scope but underspecified — use `missingInformation` to gather more context, then re-call. `no_knowledge_coverage` means there is no coverage for this topic/organisation at all — do not ask a follow-up question expecting it to help; tell the requester directly instead.","security":[{"ApiKeyHeader":[]},{}],"parameters":[{"name":"question","in":"query","required":true,"description":"The customer's question or situation, in plain language (5–4000 characters).","schema":{"type":"string","minLength":5,"maxLength":4000},"example":"My power went out during a storm. Can I claim compensation for the food I lost?"},{"name":"journey","in":"query","required":false,"description":"Journey key. When omitted the engine classifies the question automatically. Valid values: `general_complaint`, `compensation_property_damage_claim`, `unplanned_power_outage`, `planned_outage_notification`, `guaranteed_service_level_gsl_eligibility_and_automatic_payment`, `request_for_quotation_new_connection_or_alteration_to_supply`, `solar_connection_and_export_approval`, `meter_fault_or_replacement`, `vegetation_management_and_property_access`, `voltage_and_power_quality_issue`, `general_enquiry`, `high_bill_estimated_bill_or_billing_dispute`, `payment_difficulty_hardship_and_tailored_assistance`. See `GET /api/v1/journeys` for descriptions and object counts.","schema":{"type":"string"}},{"name":"jurisdiction","in":"query","required":false,"description":"Jurisdiction code. Accepts Australian state short codes (SA, QLD, VIC, NSW, WA, TAS, NT, ACT) or canonical ISO 3166-2 codes (AU-SA). Optional. When omitted (the default), the query searches across ALL jurisdictions and returns the most relevant published knowledge objects regardless of which jurisdiction they apply to. Only specify this when you need to limit results to one jurisdiction's content.","schema":{"type":"string"}},{"name":"organisation","in":"query","required":false,"description":"Organisation code. Valid canonical codes: `customer-ai`, `melbourne-energy`, `sapn`, `escosa`, `sa-gov-dem`, `ewosa`. Also accepts slug aliases: `sa-power-networks`, `sa power networks`. When an `X-API-Key` or `Authorization: Bearer` header is present the credential's organisation is used instead.","schema":{"type":"string","enum":["customer-ai","melbourne-energy","sapn","escosa","sa-gov-dem","ewosa","sa-power-networks","sa power networks"]}}],"responses":{"200":{"description":"Evaluation completed successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskResult"},"examples":{"foodSpoilage":{"summary":"Food spoilage compensation claim","value":{"question":"My power went out during a storm. Can I claim compensation for the food I lost?","answer":"Storm-related power outages are generally excluded from compensation claims under the applicable regulatory framework. However, you may be eligible for a Guaranteed Service Level (GSL) payment if your outage exceeded the threshold duration."}}}}}},"400":{"description":"Missing or invalid question parameter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Evaluation engine temporarily unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"askQuestion","summary":"Ask a customer question","description":"Ask a natural-language question on behalf of a utility customer. Returns an assembled, provenance-backed answer drawn exclusively from approved knowledge objects. Applicability filtering (journey / jurisdiction / organisation) runs before relevance ranking so only content approved for the caller's context is returned.  **Coverage gaps**: when the scenario cannot be confidently classified, the response has `status: \"coverage_gap\"` instead of a normal answer. It also has `coverageGapCategory`: `missing_customer_context` means the topic is in scope but underspecified — use `missingInformation` to gather more context from the customer, then re-call. `no_knowledge_coverage` means there is no coverage for this topic/organisation at all — do not ask a follow-up question expecting it to help; tell the customer directly instead.  **Feedback required**: after using this response to answer the customer, call `POST /api/v1/feedback` with the returned `evaluationId` as `interactionId`. Report `utility`, `resolutionStatus`, `usedObjectIds`, and any `gapCategory`/`gapDescription` to surface unmet knowledge demand.","security":[{"ApiKeyHeader":[]},{"BearerAuth":[]},{}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["question"],"properties":{"question":{"type":"string","minLength":5,"maxLength":4000,"description":"The customer's question or situation, in plain language. If you are a consuming AI agent, prefer sending a concise, retrieval-oriented rewrite of the customer's situation here (preserving material facts such as dates, amounts, durations, counts, organisations, and causes) and put the customer's own wording in `customerQuery` instead."},"customerQuery":{"type":"string","maxLength":4000,"description":"Optional. The customer's original, unedited wording, kept for provenance, human review, and feedback context — NOT used for retrieval or classification (the primary text field above drives that). Provide this whenever you have rewritten the customer's wording into a concise, retrieval-oriented query; omit it only when the primary field already IS the customer's own words."},"journey":{"type":"string","description":"Journey key. When omitted the engine classifies the question automatically. Valid values: `general_complaint`, `compensation_property_damage_claim`, `unplanned_power_outage`, `planned_outage_notification`, `guaranteed_service_level_gsl_eligibility_and_automatic_payment`, `request_for_quotation_new_connection_or_alteration_to_supply`, `solar_connection_and_export_approval`, `meter_fault_or_replacement`, `vegetation_management_and_property_access`, `voltage_and_power_quality_issue`, `general_enquiry`, `high_bill_estimated_bill_or_billing_dispute`, `payment_difficulty_hardship_and_tailored_assistance`. See `GET /api/v1/journeys` for descriptions and object counts."},"jurisdiction":{"type":"string","description":"Jurisdiction code. Accepts Australian state short codes (SA, QLD, VIC, NSW, WA, TAS, NT, ACT) or canonical ISO 3166-2 codes (AU-SA). Optional. When omitted (the default), the query searches across ALL jurisdictions and returns the most relevant published knowledge objects regardless of which jurisdiction they apply to. Only specify this when you need to limit results to one jurisdiction's content."},"organisation":{"type":"string","description":"Organisation code. Valid canonical codes: `customer-ai`, `melbourne-energy`, `sapn`, `escosa`, `sa-gov-dem`, `ewosa`. Also accepts slug aliases: `sa-power-networks`, `sa power networks`. When an `X-API-Key` or `Authorization: Bearer` header is present the credential's organisation is used instead."},"classificationTags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","description":"dataName of an approved classification tag — see GET /classification-tags."},"priority":{"type":"string","enum":["suppress","enhance","boost"],"description":"How this tag affects ranking of matching knowledge objects, as a score multiplier (defaults, operator-configurable per journey): 'boost' (default ×3) — use for the 1-2 tags most central to this customer's situation. 'enhance' (default ×2) — use for tags that are clearly relevant but not the primary focus. 'suppress' (default ×0.5) — use to push down objects you know are noise for this query, e.g. suppressing property_damage_claim when the customer is asking about life support or billing. Combining boost with suppress is where the most value lies — boosting the central tags while suppressing known noise reshapes the result set far more than boosting alone."}},"required":["tag","priority"]},"description":"Optional. Signals to re-rank retrieved knowledge objects before they reach the caller, each with a priority that multiplies the score of any retrieved object carrying that tag in its own classificationTags (see `priority` above for the default multipliers, which operators can tune per journey, and guidance on when to use each). None of them can restrict which objects are eligible at all. A tag that doesn't match any retrieved object's own classificationTags simply has no effect — this is normal when coverage is sparse, not an error. Look up valid tag values with GET /classification-tags first — unknown tags are silently dropped and reported back in `ignoredClassificationTags`."},"agentModel":{"type":"string","description":"Optional. The model name/version of the calling AI agent (e.g. 'claude-sonnet-4', 'gpt-4o'). Used only for aggregate usage analytics."},"agentPlatform":{"type":"string","description":"Optional. The platform or product hosting the agent (e.g. 'Claude Desktop', 'ChatGPT', 'custom'). Used only for analytics."},"endUserRegion":{"type":"string","description":"Optional. The end user's approximate region or state (e.g. 'SA, Australia'). Used only for aggregate analytics."}}},"examples":{"foodSpoilage":{"summary":"Food spoilage compensation claim","value":{"question":"My fridge stopped working during a power outage yesterday. Can I claim compensation for the food I lost?","organisation":"example-org","jurisdiction":"SA"}}}}}},"responses":{"201":{"description":"Evaluation completed successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskResult"}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/scenario":{"post":{"operationId":"evaluateScenario","summary":"Run a full scenario evaluation","description":"Submit a customer scenario and receive the complete structured evaluation: classification with signals, emergency routing check, every retrieved knowledge object with relevance scores and matched terms, certainty-labelled outcomes, gaps, human-review flags and full provenance. Use this instead of `/ask` when you need the raw retrieval trace.  **Feedback required**: after using this evaluation to respond to the customer, call `POST /api/v1/feedback` with the returned `id` as `interactionId`.","security":[{"ApiKeyHeader":[]},{"BearerAuth":[]},{}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["scenarioText"],"properties":{"scenarioText":{"type":"string","minLength":5,"maxLength":4000,"description":"The customer scenario to evaluate. If you are a consuming AI agent, prefer sending a concise, retrieval-oriented rewrite of the customer's situation here (preserving material facts such as dates, amounts, durations, counts, organisations, and causes) and put the customer's own wording in `customerQuery` instead."},"customerQuery":{"type":"string","maxLength":4000,"description":"Optional. The customer's original, unedited wording, kept for provenance, human review, and feedback context — NOT used for retrieval or classification (the primary text field above drives that). Provide this whenever you have rewritten the customer's wording into a concise, retrieval-oriented query; omit it only when the primary field already IS the customer's own words."},"journey":{"type":"string","description":"Journey key. When omitted the engine classifies the question automatically. Valid values: `general_complaint`, `compensation_property_damage_claim`, `unplanned_power_outage`, `planned_outage_notification`, `guaranteed_service_level_gsl_eligibility_and_automatic_payment`, `request_for_quotation_new_connection_or_alteration_to_supply`, `solar_connection_and_export_approval`, `meter_fault_or_replacement`, `vegetation_management_and_property_access`, `voltage_and_power_quality_issue`, `general_enquiry`, `high_bill_estimated_bill_or_billing_dispute`, `payment_difficulty_hardship_and_tailored_assistance`. See `GET /api/v1/journeys` for descriptions and object counts."},"jurisdiction":{"type":"string","description":"Jurisdiction code. Accepts Australian state short codes (SA, QLD, VIC, NSW, WA, TAS, NT, ACT) or canonical ISO 3166-2 codes (AU-SA). Optional. When omitted (the default), the query searches across ALL jurisdictions and returns the most relevant published knowledge objects regardless of which jurisdiction they apply to. Only specify this when you need to limit results to one jurisdiction's content."},"organisation":{"type":"string","description":"Organisation code. Valid canonical codes: `customer-ai`, `melbourne-energy`, `sapn`, `escosa`, `sa-gov-dem`, `ewosa`. Also accepts slug aliases: `sa-power-networks`, `sa power networks`. When an `X-API-Key` or `Authorization: Bearer` header is present the credential's organisation is used instead."},"classificationTags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","description":"dataName of an approved classification tag — see GET /classification-tags."},"priority":{"type":"string","enum":["suppress","enhance","boost"],"description":"How this tag affects ranking of matching knowledge objects, as a score multiplier (defaults, operator-configurable per journey): 'boost' (default ×3) — use for the 1-2 tags most central to this customer's situation. 'enhance' (default ×2) — use for tags that are clearly relevant but not the primary focus. 'suppress' (default ×0.5) — use to push down objects you know are noise for this query, e.g. suppressing property_damage_claim when the customer is asking about life support or billing. Combining boost with suppress is where the most value lies — boosting the central tags while suppressing known noise reshapes the result set far more than boosting alone."}},"required":["tag","priority"]},"description":"Optional. Signals to re-rank retrieved knowledge objects before they reach the caller, each with a priority that multiplies the score of any retrieved object carrying that tag in its own classificationTags (see `priority` above for the default multipliers, which operators can tune per journey, and guidance on when to use each). None of them can restrict which objects are eligible at all. A tag that doesn't match any retrieved object's own classificationTags simply has no effect — this is normal when coverage is sparse, not an error. Look up valid tag values with GET /classification-tags first — unknown tags are silently dropped and reported back in `ignoredClassificationTags`."},"agentModel":{"type":"string","description":"Optional. The model name/version of the calling AI agent (e.g. 'claude-sonnet-4', 'gpt-4o'). Used only for aggregate usage analytics."},"agentPlatform":{"type":"string","description":"Optional. The platform or product hosting the agent (e.g. 'Claude Desktop', 'ChatGPT', 'custom'). Used only for analytics."},"endUserRegion":{"type":"string","description":"Optional. The end user's approximate region or state (e.g. 'SA, Australia'). Used only for aggregate analytics."}}}}}},"responses":{"201":{"description":"Scenario evaluated successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScenarioResult"}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/feedback":{"post":{"operationId":"submitAgentFeedback","summary":"Submit utility feedback","description":"Submit feedback on how useful a Customer-AI response was, AFTER the consuming agent has attempted to resolve the customer's problem.  This is the REST equivalent of the `submit_agent_feedback` MCP tool. Call it once per interaction, after generating your customer response.  **Why this matters**: Customer-AI does not determine all knowledge gaps from retrieval alone. The consuming agent is better placed to assess utility because it knows the customer's original problem, what Customer-AI returned, what other sources were needed, and whether the problem was resolved. Feedback from this endpoint drives the extraction-priority queue that determines what Customer-AI learns next.  Pass the `evaluationId` returned by `POST /ask` (or the `id` from `POST /scenario`) as `interactionId` to link the feedback to the original retrieval for operator traceability.","security":[{"ApiKeyHeader":[]},{"BearerAuth":[]},{}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentFeedbackRequest"},"examples":{"highUtility":{"summary":"High utility — resolved","value":{"interactionId":"1234","utility":"high","resolutionStatus":"resolved","usedObjectIds":["example-org-cpd-responsibility-distributor-v1","eligibility-rule-1"],"agentComment":"Eligibility and evidence requirements were sufficient to resolve the question."}},"knowledgeGap":{"summary":"Knowledge gap — blocking","value":{"interactionId":"1235","utility":"none","resolutionStatus":"unresolved","usedObjectIds":[],"gapCategory":"knowledge_gap","gapType":"historical_rule","gapDescription":"Historical South Australian solar feed-in tariff eligibility rules applying to systems installed in 2018, including grandfathering conditions and events that terminate or modify entitlement.","importance":"blocking","alternativeSourceUsed":true}},"customerDataGap":{"summary":"Customer information gap","value":{"interactionId":"1236","utility":"partial","resolutionStatus":"partial","gapCategory":"customer_information_gap","gapType":"bill","gapDescription":"Current and previous electricity bills are required to determine why charges increased.","importance":"blocking"}}}}}},"responses":{"201":{"description":"Feedback accepted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentFeedbackResult"}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/knowledge":{"get":{"operationId":"searchKnowledge","summary":"Search approved knowledge objects","description":"Search the repository of approved knowledge objects with metadata filters. Returns structured objects each expressing a single proposition with certainty class, provenance and approval status.","security":[{"ApiKeyHeader":[]},{"BearerAuth":[]},{}],"parameters":[{"name":"search","in":"query","description":"Free-text search over titles, propositions and logical IDs.","schema":{"type":"string"}},{"name":"journey","in":"query","description":"Filter by journey key.","schema":{"type":"string","enum":["general_complaint","compensation_property_damage_claim","unplanned_power_outage","planned_outage_notification","guaranteed_service_level_gsl_eligibility_and_automatic_payment","request_for_quotation_new_connection_or_alteration_to_supply","solar_connection_and_export_approval","meter_fault_or_replacement","vegetation_management_and_property_access","voltage_and_power_quality_issue","general_enquiry","high_bill_estimated_bill_or_billing_dispute","payment_difficulty_hardship_and_tailored_assistance"]}},{"name":"jurisdiction","in":"query","description":"Jurisdiction code. Accepts Australian state short codes (SA, QLD, VIC, NSW, WA, TAS, NT, ACT) or canonical ISO 3166-2 codes (AU-SA). Optional. When omitted (the default), the query searches across ALL jurisdictions and returns the most relevant published knowledge objects regardless of which jurisdiction they apply to. Only specify this when you need to limit results to one jurisdiction's content.","schema":{"type":"string"}},{"name":"organisation","in":"query","description":"Organisation code. Valid canonical codes: `customer-ai`, `melbourne-energy`, `sapn`, `escosa`, `sa-gov-dem`, `ewosa`. Also accepts slug aliases: `sa-power-networks`, `sa power networks`. When an `X-API-Key` or `Authorization: Bearer` header is present the credential's organisation is used instead.","schema":{"type":"string","enum":["customer-ai","melbourne-energy","sapn","escosa","sa-gov-dem","ewosa","sa-power-networks","sa power networks"]}},{"name":"objectType","in":"query","description":"Filter by object type.","schema":{"type":"string","enum":["responsibility_rule","eligibility_rule","exclusion","evidence_requirement","process_step","timeframe","outcome","escalation_route","human_review_trigger","safety_routing_rule","customer_explanation","content_gap","interaction"]}}],"responses":{"200":{"description":"List of matching knowledge objects.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeObject"}}}}}}}},"/sources/{sourceId}":{"get":{"operationId":"getSource","summary":"Get a source by ID","description":"Retrieve attribution metadata for a single knowledge source — title, source type, issuing authority, authority class, authoritative URL, effective date and confidentiality. When an `X-API-Key` header is present, only sources belonging to that key's organisation are returned.","security":[{"ApiKeyHeader":[]},{"BearerAuth":[]},{}],"parameters":[{"name":"sourceId","in":"path","required":true,"description":"Source identifier (e.g. `example-org-cpd-v3`).","schema":{"type":"string"}}],"responses":{"200":{"description":"Source found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Source"}}}},"404":{"description":"Source not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/knowledge/{logicalId}":{"get":{"operationId":"getKnowledgeObject","summary":"Get one knowledge object","description":"Retrieve a single published knowledge object by its logical ID. Use `GET /api/v1/knowledge` to discover logical IDs.","security":[{"ApiKeyHeader":[]},{"BearerAuth":[]},{}],"parameters":[{"name":"logicalId","in":"path","required":true,"description":"Logical knowledge object identifier (e.g. `example-org-cpd-responsibility-distributor-v1`).","schema":{"type":"string"}}],"responses":{"200":{"description":"Knowledge object found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeObject"}}}},"404":{"description":"Knowledge object not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/journeys":{"get":{"operationId":"listJourneys","summary":"List supported journeys","description":"List the customer journeys the platform supports, with descriptions and approved object counts. Use the returned `key` values for the `journey` parameter of other endpoints.","responses":{"200":{"description":"List of journeys.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Journey"}}}}}}}},"/organisations":{"get":{"operationId":"listOrganisations","summary":"List organisations","description":"List organisations with published content, including jurisdictions, object counts, and accepted name aliases. Use the returned `organisation` codes or any `acceptedAliases` entry for the `organisation` parameter of other endpoints.","responses":{"200":{"description":"List of organisations.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Organisation"}}}}}}}},"/classification-tags":{"get":{"operationId":"listClassificationTags","summary":"List classification tags","description":"List the operator-approved classification tags you can submit as `classificationTags` on `POST /ask` or `POST /scenario` to enhance/boost — or actively suppress — ranking of matching knowledge objects. Look up valid tag values here first — any tag not in this list is silently ignored.","responses":{"200":{"description":"List of classification tags.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClassificationTagSummary"}}}}}}}},"/scenarios/evaluate":{"post":{"operationId":"evaluateScenarioLegacy","summary":"Run a full scenario evaluation (deprecated)","description":"**Deprecated** — use `POST /api/v1/scenario` instead. This route is kept for backwards compatibility with v1 integrations. It accepts the same request body and returns the same ScenarioResult response as `/scenario`.","deprecated":true,"security":[{"ApiKeyHeader":[]},{"BearerAuth":[]},{}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["scenarioText"],"properties":{"scenarioText":{"type":"string","minLength":5,"maxLength":4000,"description":"The customer scenario to evaluate. If you are a consuming AI agent, prefer sending a concise, retrieval-oriented rewrite of the customer's situation here (preserving material facts such as dates, amounts, durations, counts, organisations, and causes) and put the customer's own wording in `customerQuery` instead."},"customerQuery":{"type":"string","maxLength":4000,"description":"Optional. The customer's original, unedited wording, kept for provenance, human review, and feedback context — NOT used for retrieval or classification (the primary text field above drives that). Provide this whenever you have rewritten the customer's wording into a concise, retrieval-oriented query; omit it only when the primary field already IS the customer's own words."},"journey":{"type":"string","description":"Journey key. When omitted the engine classifies the question automatically. Valid values: `general_complaint`, `compensation_property_damage_claim`, `unplanned_power_outage`, `planned_outage_notification`, `guaranteed_service_level_gsl_eligibility_and_automatic_payment`, `request_for_quotation_new_connection_or_alteration_to_supply`, `solar_connection_and_export_approval`, `meter_fault_or_replacement`, `vegetation_management_and_property_access`, `voltage_and_power_quality_issue`, `general_enquiry`, `high_bill_estimated_bill_or_billing_dispute`, `payment_difficulty_hardship_and_tailored_assistance`. See `GET /api/v1/journeys` for descriptions and object counts."},"jurisdiction":{"type":"string","description":"Jurisdiction code. Accepts Australian state short codes (SA, QLD, VIC, NSW, WA, TAS, NT, ACT) or canonical ISO 3166-2 codes (AU-SA). Optional. When omitted (the default), the query searches across ALL jurisdictions and returns the most relevant published knowledge objects regardless of which jurisdiction they apply to. Only specify this when you need to limit results to one jurisdiction's content."},"organisation":{"type":"string","description":"Organisation code. Valid canonical codes: `customer-ai`, `melbourne-energy`, `sapn`, `escosa`, `sa-gov-dem`, `ewosa`. Also accepts slug aliases: `sa-power-networks`, `sa power networks`. When an `X-API-Key` or `Authorization: Bearer` header is present the credential's organisation is used instead."},"agentModel":{"type":"string","description":"Optional. The model name/version of the calling AI agent (e.g. 'claude-sonnet-4', 'gpt-4o'). Used only for aggregate usage analytics."},"agentPlatform":{"type":"string","description":"Optional. The platform or product hosting the agent (e.g. 'Claude Desktop', 'ChatGPT', 'custom'). Used only for analytics."},"endUserRegion":{"type":"string","description":"Optional. The end user's approximate region or state (e.g. 'SA, Australia'). Used only for aggregate analytics."}}}}}},"responses":{"201":{"description":"Scenario evaluated successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScenarioResult"}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/knowledge-objects":{"get":{"operationId":"searchKnowledgeObjectsLegacy","summary":"Search approved knowledge objects (deprecated)","description":"**Deprecated** — use `GET /api/v1/knowledge` instead. This route is kept for backwards compatibility with v1 integrations and accepts the same query parameters.","deprecated":true,"security":[{"ApiKeyHeader":[]},{"BearerAuth":[]},{}],"parameters":[{"name":"search","in":"query","description":"Free-text search over titles, propositions and logical IDs.","schema":{"type":"string"}},{"name":"journey","in":"query","description":"Filter by journey key.","schema":{"type":"string","enum":["general_complaint","compensation_property_damage_claim","unplanned_power_outage","planned_outage_notification","guaranteed_service_level_gsl_eligibility_and_automatic_payment","request_for_quotation_new_connection_or_alteration_to_supply","solar_connection_and_export_approval","meter_fault_or_replacement","vegetation_management_and_property_access","voltage_and_power_quality_issue","general_enquiry","high_bill_estimated_bill_or_billing_dispute","payment_difficulty_hardship_and_tailored_assistance"]}},{"name":"jurisdiction","in":"query","description":"Jurisdiction code. Accepts Australian state short codes (SA, QLD, VIC, NSW, WA, TAS, NT, ACT) or canonical ISO 3166-2 codes (AU-SA). Optional. When omitted (the default), the query searches across ALL jurisdictions and returns the most relevant published knowledge objects regardless of which jurisdiction they apply to. Only specify this when you need to limit results to one jurisdiction's content.","schema":{"type":"string"}},{"name":"organisation","in":"query","description":"Organisation code. Valid canonical codes: `customer-ai`, `melbourne-energy`, `sapn`, `escosa`, `sa-gov-dem`, `ewosa`. Also accepts slug aliases: `sa-power-networks`, `sa power networks`. When an `X-API-Key` or `Authorization: Bearer` header is present the credential's organisation is used instead.","schema":{"type":"string","enum":["customer-ai","melbourne-energy","sapn","escosa","sa-gov-dem","ewosa","sa-power-networks","sa power networks"]}},{"name":"objectType","in":"query","description":"Filter by object type.","schema":{"type":"string","enum":["responsibility_rule","eligibility_rule","exclusion","evidence_requirement","process_step","timeframe","outcome","escalation_route","human_review_trigger","safety_routing_rule","customer_explanation","content_gap","interaction"]}}],"responses":{"200":{"description":"List of matching knowledge objects.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeObject"}}}}}}}},"/knowledge-objects/{logicalId}":{"get":{"operationId":"getKnowledgeObjectLegacy","summary":"Get one knowledge object (deprecated)","description":"**Deprecated** — use `GET /api/v1/knowledge/{logicalId}` instead. This route is kept for backwards compatibility with v1 integrations.","deprecated":true,"security":[{"ApiKeyHeader":[]},{"BearerAuth":[]},{}],"parameters":[{"name":"logicalId","in":"path","required":true,"description":"Logical knowledge object identifier (e.g. `example-org-cpd-responsibility-distributor-v1`).","schema":{"type":"string"}}],"responses":{"200":{"description":"Knowledge object found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeObject"}}}},"404":{"description":"Knowledge object not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/openapi.json":{"get":{"operationId":"getOpenApiSpec","summary":"OpenAPI 3.1 specification","description":"This OpenAPI 3.1 specification document. Cached in memory and refreshed automatically every 5 minutes (configurable via OPENAPI_CACHE_TTL_MS env var).","responses":{"200":{"description":"OpenAPI specification.","content":{"application/json":{"schema":{"type":"object"}}}}}}}}}