fix: deliver validated AI insight responses

This commit is contained in:
2026-08-22 15:47:38 +08:00
parent ca55c2fb3d
commit 3fba4261ee
68 changed files with 6870 additions and 493 deletions

View File

@@ -1,7 +1,7 @@
openapi: 3.1.0
info:
title: ZarinPal Merchant Analytics API
version: 1.1.0
version: 1.2.0
summary: Contract-first API for authenticated merchant analytics and growth reports.
description: |
Approved analytics and AI growth-report interface. This document is the API source of truth; generated
@@ -20,6 +20,7 @@ tags:
- name: Reporting periods
- name: Analytics runs
- name: Analytics details
- name: Growth coach
security:
- merchantSession: []
paths:
@@ -646,6 +647,7 @@ paths:
parameters:
- {name: module, in: path, required: true, schema: {$ref: '#/components/schemas/AIInsightModule'}}
- {name: terminalId, in: query, required: false, schema: {type: string, maxLength: 128}}
- {name: periodId, in: query, required: false, description: Used only by the transactions module to match the selected reporting period., schema: {type: string, maxLength: 128}}
- $ref: '#/components/parameters/RequestIdHeader'
responses:
'200': {description: Latest ready or deterministic-partial snapshot., content: {application/json: {schema: {$ref: '#/components/schemas/AIInsight'}}}}
@@ -676,6 +678,30 @@ paths:
'422': {$ref: '#/components/responses/ValidationFailed'}
'429': {$ref: '#/components/responses/RateLimited'}
'503': {$ref: '#/components/responses/AnalyticsUnavailable'}
/analytics/ai-insights/{module}/sections/{sectionId}/refreshes:
post:
tags: [AI page insights]
operationId: refreshAIInsightSection
summary: Refresh one independently persisted AI narrative section
parameters:
- {name: module, in: path, required: true, schema: {$ref: '#/components/schemas/AIInsightModule'}}
- {name: sectionId, in: path, required: true, schema: {$ref: '#/components/schemas/AIInsightSection'}}
- $ref: '#/components/parameters/IdempotencyKeyHeader'
- $ref: '#/components/parameters/CsrfTokenHeader'
- $ref: '#/components/parameters/OriginHeader'
requestBody:
required: true
content: {application/json: {schema: {$ref: '#/components/schemas/AIInsightRefreshRequest'}}}
responses:
'200': {description: Idempotent job reused., content: {application/json: {schema: {$ref: '#/components/schemas/AIInsightJob'}}}}
'202': {description: Section refresh accepted., content: {application/json: {schema: {$ref: '#/components/schemas/AIInsightJob'}}}}
'401': {$ref: '#/components/responses/Unauthenticated'}
'403': {$ref: '#/components/responses/Forbidden'}
'404': {$ref: '#/components/responses/ResourceNotFound'}
'409': {$ref: '#/components/responses/Conflict'}
'422': {$ref: '#/components/responses/ValidationFailed'}
'429': {$ref: '#/components/responses/RateLimited'}
'503': {$ref: '#/components/responses/AnalyticsUnavailable'}
/analytics/ai-insight-refreshes/{jobId}:
get:
tags: [AI page insights]
@@ -699,6 +725,159 @@ paths:
'401': {$ref: '#/components/responses/Unauthenticated'}
'404': {$ref: '#/components/responses/ResourceNotFound'}
/growth-coach:
get:
tags: [Growth coach]
operationId: getGrowthCoachDashboard
summary: Return a terminal coach workspace or an all-terminal rollup
parameters:
- {name: terminalId, in: query, required: false, schema: {type: string, maxLength: 128}}
responses:
'200': {description: Persisted coach state and deterministic progress., content: {application/json: {schema: {$ref: '#/components/schemas/GrowthCoachDashboard'}}}}
'401': {$ref: '#/components/responses/Unauthenticated'}
'404': {$ref: '#/components/responses/ResourceNotFound'}
'503': {$ref: '#/components/responses/AnalyticsUnavailable'}
/growth-coach/terminals/{terminalId}/profile:
get:
tags: [Growth coach]
operationId: getGrowthCoachProfile
parameters:
- {name: terminalId, in: path, required: true, schema: {type: string, maxLength: 128}}
responses:
'200': {description: Current profile version., content: {application/json: {schema: {$ref: '#/components/schemas/GrowthCoachProfile'}}}}
'404': {$ref: '#/components/responses/ResourceNotFound'}
put:
tags: [Growth coach]
operationId: putGrowthCoachProfile
parameters:
- {name: terminalId, in: path, required: true, schema: {type: string, maxLength: 128}}
- $ref: '#/components/parameters/IdempotencyKeyHeader'
- $ref: '#/components/parameters/CsrfTokenHeader'
requestBody:
required: true
content: {application/json: {schema: {$ref: '#/components/schemas/GrowthCoachProfileInput'}}}
responses:
'201': {description: New profile version and generation jobs., content: {application/json: {schema: {$ref: '#/components/schemas/GrowthCoachProfileResult'}}}}
'409': {$ref: '#/components/responses/Conflict'}
'422': {$ref: '#/components/responses/ValidationFailed'}
/growth-coach/terminals/{terminalId}/refreshes:
post:
tags: [Growth coach]
operationId: refreshGrowthCoach
parameters:
- {name: terminalId, in: path, required: true, schema: {type: string, maxLength: 128}}
- $ref: '#/components/parameters/IdempotencyKeyHeader'
- $ref: '#/components/parameters/CsrfTokenHeader'
requestBody:
required: true
content: {application/json: {schema: {$ref: '#/components/schemas/GrowthCoachRefreshInput'}}}
responses:
'202': {description: Cadence-scoped coach refresh queued., content: {application/json: {schema: {$ref: '#/components/schemas/GrowthCoachJob'}}}}
'409': {$ref: '#/components/responses/Conflict'}
'422': {$ref: '#/components/responses/ValidationFailed'}
'429': {$ref: '#/components/responses/RateLimited'}
'404': {$ref: '#/components/responses/ResourceNotFound'}
/growth-coach/plans:
get:
tags: [Growth coach]
operationId: listGrowthCoachPlans
parameters:
- {name: terminalId, in: query, required: false, schema: {type: string, maxLength: 128}}
responses:
'200': {description: Coach plans., content: {application/json: {schema: {type: object, required: [plans], properties: {plans: {type: array, items: {$ref: '#/components/schemas/GrowthCoachPlan'}}}}}}}
/growth-coach/actions:
get:
tags: [Growth coach]
operationId: listGoldenSteps
parameters:
- {name: terminalId, in: query, required: false, schema: {type: string, maxLength: 128}}
responses:
'200': {description: Shared Golden Steps workspace., content: {application/json: {schema: {type: object, required: [actions], properties: {actions: {type: array, items: {$ref: '#/components/schemas/GoldenStep'}}}}}}}
/growth-coach/goals:
get:
tags: [Growth coach]
operationId: listMonthlyGoals
parameters:
- {name: terminalId, in: query, required: false, schema: {type: string, maxLength: 128}}
responses:
'200': {description: Automatically tracked monthly goals with manual action subtasks., content: {application/json: {schema: {type: object, required: [goals], properties: {goals: {type: array, items: {$ref: '#/components/schemas/MonthlyGoal'}}}}}}}
/growth-coach/goals/{goalId}/subtasks/{subtaskId}/transitions:
post:
tags: [Growth coach]
operationId: transitionGoalSubtask
parameters:
- {name: goalId, in: path, required: true, schema: {type: string}}
- {name: subtaskId, in: path, required: true, schema: {type: string}}
- $ref: '#/components/parameters/IdempotencyKeyHeader'
- $ref: '#/components/parameters/CsrfTokenHeader'
requestBody: {required: true, content: {application/json: {schema: {type: object, additionalProperties: false, required: [transition], properties: {transition: {type: string, enum: [COMPLETE, REOPEN]}}}}}}
responses:
'200': {description: Updated user-completed subtask., content: {application/json: {schema: {$ref: '#/components/schemas/GoalSubtask'}}}}
'409': {$ref: '#/components/responses/Conflict'}
/growth-coach/deadline-proposals/{proposalId}/decisions:
post:
tags: [Growth coach]
operationId: decideGoalDeadlineProposal
parameters:
- {name: proposalId, in: path, required: true, schema: {type: string}}
- $ref: '#/components/parameters/IdempotencyKeyHeader'
- $ref: '#/components/parameters/CsrfTokenHeader'
requestBody: {required: true, content: {application/json: {schema: {type: object, additionalProperties: false, required: [decision], properties: {decision: {type: string, enum: [ACCEPT, REJECT]}}}}}}
responses:
'200': {description: Deadline proposal decision., content: {application/json: {schema: {$ref: '#/components/schemas/GoalDeadlineProposal'}}}}
'409': {$ref: '#/components/responses/Conflict'}
/growth-coach/recommendations/{id}/decisions:
post:
tags: [Growth coach]
operationId: decideGrowthCoachRecommendation
parameters:
- {name: id, in: path, required: true, schema: {type: string}}
- $ref: '#/components/parameters/IdempotencyKeyHeader'
- $ref: '#/components/parameters/CsrfTokenHeader'
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/CoachDecision'}}}}
responses:
'200': {description: Decision recorded and accepted action created when applicable.}
'409': {$ref: '#/components/responses/Conflict'}
/growth-coach/actions/{id}/transitions:
post:
tags: [Growth coach]
operationId: transitionGoldenStep
parameters:
- {name: id, in: path, required: true, schema: {type: string}}
- $ref: '#/components/parameters/IdempotencyKeyHeader'
- $ref: '#/components/parameters/CsrfTokenHeader'
requestBody: {required: true, content: {application/json: {schema: {type: object, additionalProperties: false, required: [transition], properties: {transition: {type: string, enum: [START, COMPLETE, ABANDON]}}}}}}
responses:
'200': {description: Updated action., content: {application/json: {schema: {$ref: '#/components/schemas/GoldenStep'}}}}
'409': {$ref: '#/components/responses/Conflict'}
/growth-coach/difficulty-proposals/{id}/decisions:
post:
tags: [Growth coach]
operationId: decideGrowthCoachDifficulty
parameters:
- {name: id, in: path, required: true, schema: {type: string}}
- $ref: '#/components/parameters/IdempotencyKeyHeader'
- $ref: '#/components/parameters/CsrfTokenHeader'
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/CoachDecision'}}}}
responses:
'200': {description: Difficulty decision recorded.}
'409': {$ref: '#/components/responses/Conflict'}
/growth-coach/refreshes/{jobId}:
get:
tags: [Growth coach]
operationId: getGrowthCoachJob
parameters: [{name: jobId, in: path, required: true, schema: {type: string}}]
responses:
'200': {description: Generation job., content: {application/json: {schema: {$ref: '#/components/schemas/GrowthCoachJob'}}}}
'404': {$ref: '#/components/responses/ResourceNotFound'}
/growth-coach/refreshes/{jobId}/events:
get:
tags: [Growth coach]
operationId: streamGrowthCoachJob
parameters: [{name: jobId, in: path, required: true, schema: {type: string}}]
responses:
'200': {description: Job snapshots., content: {text/event-stream: {schema: {type: string}}}}
components:
securitySchemes:
merchantSession:
@@ -1064,6 +1243,208 @@ components:
$ref: '#/components/schemas/Problem'
schemas:
GrowthCoachProfileInput:
type: object
additionalProperties: false
required: [businessDescription, niches, salesChannels, primaryGoal, constraints]
properties:
businessDescription: {type: string, minLength: 20, maxLength: 2000}
niches: {type: array, minItems: 1, maxItems: 10, items: {type: string, maxLength: 80}}
salesChannels: {type: array, minItems: 1, maxItems: 10, items: {type: string, maxLength: 80}}
primaryGoal: {type: string, minLength: 3, maxLength: 500}
constraints: {type: string, maxLength: 1000}
GrowthCoachProfile:
allOf:
- $ref: '#/components/schemas/GrowthCoachProfileInput'
- type: object
required: [profileId, terminalId, version, difficulty, createdAt]
properties:
profileId: {type: string}
terminalId: {type: string}
version: {type: integer, minimum: 1}
difficulty: {type: integer, minimum: 1, maximum: 5}
createdAt: {type: string, format: date-time}
GrowthCoachJob:
type: object
additionalProperties: false
required: [jobId, state, eventsUrl, createdAt, updatedAt]
properties:
jobId: {type: string}
state: {type: string, enum: [QUEUED, RUNNING, COMPLETED, FAILED]}
eventsUrl: {type: string}
createdAt: {type: string, format: date-time}
updatedAt: {type: string, format: date-time}
GrowthCoachRefreshInput:
type: object
additionalProperties: false
required: [cadence]
properties:
cadence: {type: string, enum: [WEEKLY, MONTHLY]}
GrowthCoachProfileResult:
type: object
additionalProperties: false
required: [profile, generationJobs, pageInsightJobs]
properties:
profile: {$ref: '#/components/schemas/GrowthCoachProfile'}
generationJobs: {type: array, items: {$ref: '#/components/schemas/GrowthCoachJob'}}
pageInsightJobs: {type: array, items: {$ref: '#/components/schemas/AIInsightJob'}}
GrowthCoachRecommendation:
type: object
additionalProperties: false
required: [recommendationId, planId, rank, status, actionType, title, rationale, expectedImpact, effort, evidenceIds, sampleSize, deadline, subtasks, eventIds]
properties:
recommendationId: {type: string}
planId: {type: string}
rank: {type: integer, minimum: 1, maximum: 3}
status: {type: string, enum: [OFFERED, ACCEPTED, REJECTED, EXPIRED]}
actionType: {type: string, enum: [MANUAL, METRIC]}
title: {type: string}
rationale: {type: string}
expectedImpact: {type: string}
effort: {type: string, enum: [LOW, MEDIUM, HIGH]}
evidenceIds: {type: array, items: {type: string}}
metricId: {type: [string, 'null']}
metricDirection: {type: [string, 'null']}
actionLevel: {type: [string, 'null']}
baseline: {type: [number, 'null']}
target: {type: [number, 'null']}
sampleSize: {type: integer, format: int64}
deadline: {type: string, format: date}
subtasks: {type: array, minItems: 3, maxItems: 7, items: {$ref: '#/components/schemas/GeneratedGoalSubtask'}}
eventIds: {type: array, items: {type: string}}
GeneratedGoalSubtask:
type: object
additionalProperties: false
required: [title, deadline, eventId]
properties:
title: {type: string}
deadline: {type: [string, 'null'], format: date}
eventId: {type: [string, 'null']}
GoalSubtask:
type: object
additionalProperties: false
required: [subtaskId, position, title, status, deadline]
properties:
subtaskId: {type: string}
position: {type: integer, minimum: 1, maximum: 7}
title: {type: string}
status: {type: string, enum: [TODO, DONE]}
deadline: {type: string, format: date}
eventId: {type: [string, 'null']}
completedAt: {type: [string, 'null'], format: date-time}
GoalDeadlineProposal:
type: object
additionalProperties: false
required: [proposalId, goalId, subtaskId, eventId, currentDeadline, proposedDeadline, reason, status]
properties:
proposalId: {type: string}
goalId: {type: string}
subtaskId: {type: string}
eventId: {type: string}
currentDeadline: {type: string, format: date}
proposedDeadline: {type: string, format: date}
reason: {type: string}
status: {type: string, enum: [OFFERED, ACCEPTED, REJECTED, EXPIRED]}
decidedAt: {type: [string, 'null'], format: date-time}
MonthlyGoal:
type: object
additionalProperties: false
required: [goalId, recommendationId, terminalId, title, expectedImpact, status, metricId, metricDirection, baseline, target, currentValue, progressPercent, sampleSize, deadline, eventIds, subtasks, deadlineProposals, updatedAt]
properties:
goalId: {type: string}
recommendationId: {type: string}
terminalId: {type: string}
title: {type: string}
expectedImpact: {type: string}
status: {type: string, enum: [ACTIVE, COMPLETED, MISSED, ABANDONED]}
metricId: {type: string, enum: [paid_count, paid_amount, fee_amount, avg_latency, success_rate]}
metricDirection: {type: string, enum: [INCREASE, DECREASE]}
baseline: {type: number}
target: {type: number}
currentValue: {type: number}
progressPercent: {type: number, minimum: 0, maximum: 100}
sampleSize: {type: integer, format: int64}
deadline: {type: string, format: date}
eventIds: {type: array, items: {type: string}}
subtasks: {type: array, items: {$ref: '#/components/schemas/GoalSubtask'}}
deadlineProposals: {type: array, items: {$ref: '#/components/schemas/GoalDeadlineProposal'}}
updatedAt: {type: string, format: date-time}
GrowthCoachPlan:
type: object
additionalProperties: false
required: [planId, cadence, status, periodStart, periodEnd, difficulty, suggestions, generatedAt]
properties:
planId: {type: string}
parentId: {type: [string, 'null']}
cadence: {type: string, enum: [DAILY, WEEKLY, MONTHLY]}
status: {type: string}
periodStart: {type: string, format: date}
periodEnd: {type: string, format: date}
dailyBrief: {type: [string, 'null']}
difficulty: {type: integer, minimum: 1, maximum: 5}
suggestions: {type: array, items: {$ref: '#/components/schemas/GrowthCoachRecommendation'}}
generatedAt: {type: string, format: date-time}
GoldenStep:
type: object
additionalProperties: false
required: [actionId, recommendationId, terminalId, actionType, title, status, sampleSize, deadline, updatedAt]
properties:
actionId: {type: string}
recommendationId: {type: string}
terminalId: {type: string}
actionType: {type: string, enum: [MANUAL, METRIC]}
title: {type: string}
status: {type: string, enum: [NOT_STARTED, IN_PROGRESS, COMPLETED, ABANDONED, MISSED]}
metricId: {type: [string, 'null']}
metricDirection: {type: [string, 'null']}
baseline: {type: [number, 'null']}
target: {type: [number, 'null']}
currentValue: {type: [number, 'null']}
sampleSize: {type: integer, format: int64}
deadline: {type: string, format: date}
updatedAt: {type: string, format: date-time}
GrowthCoachTerminalRollup:
type: object
additionalProperties: false
required: [terminalId, onboardingStatus, activeActions, overdueActions, completionRate]
properties:
terminalId: {type: string}
onboardingStatus: {type: string, enum: [REQUIRED, COMPLETE]}
activeActions: {type: integer}
overdueActions: {type: integer}
completionRate: {type: number, minimum: 0, maximum: 1}
GrowthCoachDashboard:
type: object
additionalProperties: false
required: [scope, generationState, plans, actions, terminals, difficultyProposals]
properties:
scope: {type: string, enum: [TERMINAL, ALL_TERMINALS]}
terminalId: {type: [string, 'null']}
dataThrough: {type: [string, 'null'], format: date}
generationState: {type: string, enum: [ONBOARDING_REQUIRED, GENERATING, IDLE, UNAVAILABLE]}
profile: {anyOf: [{$ref: '#/components/schemas/GrowthCoachProfile'}, {type: 'null'}]}
plans: {type: array, items: {$ref: '#/components/schemas/GrowthCoachPlan'}}
actions: {type: array, items: {$ref: '#/components/schemas/GoldenStep'}}
terminals: {type: array, items: {$ref: '#/components/schemas/GrowthCoachTerminalRollup'}}
difficultyProposals: {type: array, items: {$ref: '#/components/schemas/GrowthCoachDifficultyProposal'}}
GrowthCoachDifficultyProposal:
type: object
additionalProperties: false
required: [proposalId, fromLevel, toLevel, reason, status]
properties:
proposalId: {type: string}
fromLevel: {type: integer, minimum: 1, maximum: 5}
toLevel: {type: integer, minimum: 1, maximum: 5}
reason: {type: string}
status: {type: string, enum: [OFFERED, ACCEPTED, REJECTED, EXPIRED]}
CoachDecision:
type: object
additionalProperties: false
required: [decision, reason, note]
properties:
decision: {type: string, enum: [ACCEPTED, REJECTED]}
reason: {type: string, maxLength: 100}
note: {type: string, maxLength: 500}
CreateGrowthReportRequest:
type: object
additionalProperties: false
@@ -1147,6 +1528,26 @@ components:
expectedSessions: {$ref: '#/components/schemas/GrowthRange'}
expectedAmountRial: {$ref: '#/components/schemas/GrowthRange'}
historicalEvidenceId: {type: string}
GrowthUpcomingEvent:
type: object
additionalProperties: false
required: [evidenceId, eventId, eventTitleFa, date, eventType, officialHoliday, merchantCategories, behavioralChannels]
properties:
evidenceId: {type: string}
eventId: {type: string}
eventTitleFa: {type: string}
date: {type: string, format: date-time}
eventType: {type: string}
officialHoliday: {type: boolean}
merchantCategories: {type: array, items: {type: string}}
behavioralChannels: {type: array, items: {type: string}}
GrowthEventRecommendation:
type: object
additionalProperties: false
required: [evidenceId, relevanceReason]
properties:
evidenceId: {type: string}
relevanceReason: {type: string, minLength: 1, maxLength: 320}
GrowthRisk:
type: object
additionalProperties: false
@@ -1197,14 +1598,17 @@ components:
GrowthEvidence:
type: object
additionalProperties: false
required: [forecast, historicalWins, opportunities, risks, fallbackBriefs]
required: [events, merchantContext]
properties:
forecast: {$ref: '#/components/schemas/GrowthForecast'}
historicalWins: {type: array, items: {$ref: '#/components/schemas/GrowthHistoricalWin'}}
opportunities: {type: array, maxItems: 5, items: {$ref: '#/components/schemas/GrowthOpportunity'}}
risks: {type: array, maxItems: 3, items: {$ref: '#/components/schemas/GrowthRisk'}}
peer: {$ref: '#/components/schemas/GrowthPeer'}
fallbackBriefs: {type: array, items: {$ref: '#/components/schemas/GrowthFallbackBrief'}}
events: {type: array, maxItems: 5, items: {$ref: '#/components/schemas/GrowthUpcomingEvent'}}
merchantContext:
type: object
additionalProperties: false
required: [category, niches, salesChannels]
properties:
category: {type: string}
niches: {type: array, items: {type: string}}
salesChannels: {type: array, items: {type: string}}
GrowthReport:
type: object
additionalProperties: false
@@ -1218,13 +1622,13 @@ components:
additionalProperties: false
properties: {terminalId: {type: [string, 'null']}}
horizonDays: {type: integer, enum: [90]}
forecastStartDate: {type: string, format: date-time}
deterministicEvidence: {$ref: '#/components/schemas/GrowthEvidence'}
aiCopy:
type: object
additionalProperties: false
required: [briefs]
properties: {briefs: {type: array, maxItems: 5, items: {$ref: '#/components/schemas/GrowthCampaignBrief'}}}
required: [eventRecommendations]
properties:
eventRecommendations: {type: array, minItems: 3, maxItems: 3, items: {$ref: '#/components/schemas/GrowthEventRecommendation'}}
failureCode: {type: string}
cached: {type: boolean}
stale: {type: boolean}
@@ -1279,13 +1683,13 @@ components:
AIInsight:
type: object
additionalProperties: false
required: [insightId, module, revision, status, scope, asOfDate, dataThroughDate, forecastStartDate, freshness, dataLagDays, confidence, copy, evidence, calendarContext, refreshState, generatedAt]
required: [insightId, module, revision, status, scope, asOfDate, dataThroughDate, forecastStartDate, freshness, dataLagDays, confidence, copy, evidence, calendarContext, refreshState, sectionRefreshState, generatedAt]
properties:
insightId: {type: string}
module: {$ref: '#/components/schemas/AIInsightModule'}
revision: {type: integer, minimum: 1}
status: {type: string, enum: [READY, PARTIAL]}
scope: {type: object, properties: {terminalId: {type: [string, 'null']}}}
scope: {type: object, properties: {terminalId: {type: [string, 'null']}, periodId: {type: [string, 'null']}}}
asOfDate: {type: string, format: date}
dataThroughDate: {type: string, format: date}
forecastStartDate: {type: string, format: date}
@@ -1296,22 +1700,41 @@ components:
evidence: {type: object, additionalProperties: true}
calendarContext: {type: array, items: {type: object, additionalProperties: true}}
refreshState: {type: object, additionalProperties: true}
sectionRefreshState:
type: object
additionalProperties: {$ref: '#/components/schemas/AIInsightRefreshState'}
generatedAt: {type: string, format: date-time}
AIInsightRefreshRequest:
type: object
additionalProperties: false
properties: {terminalId: {type: string, maxLength: 128}}
properties:
terminalId: {type: string, maxLength: 128}
periodId: {type: string, maxLength: 128, description: Used only by the transactions module.}
AIInsightJob:
type: object
additionalProperties: false
required: [jobId, snapshotId, state, eventsUrl, createdAt, updatedAt]
required: [jobId, snapshotId, module, sectionId, state, eventsUrl, createdAt, updatedAt]
properties:
jobId: {type: string}
snapshotId: {type: string}
module: {$ref: '#/components/schemas/AIInsightModule'}
sectionId: {type: string, enum: [full, past, latest-observed, future-7-days, future-30-days, future-90-days, findings, options]}
state: {type: string, enum: [QUEUED, RUNNING, COMPLETED, FAILED]}
eventsUrl: {type: string, format: uri-reference}
createdAt: {type: string, format: date-time}
updatedAt: {type: string, format: date-time}
AIInsightSection:
type: string
enum: [past, latest-observed, future-7-days, future-30-days, future-90-days, findings, options]
AIInsightRefreshState:
type: object
additionalProperties: false
required: [state]
properties:
state: {type: string, enum: [IDLE, QUEUED, RUNNING, FAILED, LIMITED]}
jobId: {type: string}
eventsUrl: {type: string, format: uri-reference}
nextRefreshAt: {type: string, format: date-time}
LoginRequest:
type: object
additionalProperties: false
@@ -1529,7 +1952,7 @@ components:
SectionId:
type: string
enum: [overview, payment_performance, fees, peer_comparison]
enum: [overview, payment_performance, fees, peer_comparison, transactions]
AnalyticsRunState:
type: string
@@ -1566,7 +1989,7 @@ components:
description: Opaque identifier for the immutable completed-import snapshot.
metricDefinitionVersion:
type: string
const: 0.4.0
const: 0.5.0
createdAt:
type: string
format: date-time
@@ -1581,7 +2004,7 @@ components:
sections:
type: object
additionalProperties: false
required: [overview, paymentPerformance, fees, peerComparison]
required: [overview, paymentPerformance, fees, peerComparison, transactions]
properties:
overview:
$ref: '#/components/schemas/OverviewSection'
@@ -1591,6 +2014,8 @@ components:
$ref: '#/components/schemas/FeesSection'
peerComparison:
$ref: '#/components/schemas/PeerComparisonSection'
transactions:
$ref: '#/components/schemas/TransactionsSection'
QueuedSection:
type: object
@@ -1649,7 +2074,7 @@ components:
pattern: '^ds_[A-Za-z0-9_-]{8,60}$'
metricDefinitionVersion:
type: string
const: 0.4.0
const: 0.5.0
IntegerMetric:
type: object
@@ -1823,6 +2248,58 @@ components:
initTiming: {$ref: '#/components/schemas/TimingDistribution'}
verifyTiming: {$ref: '#/components/schemas/TimingDistribution'}
TransactionStatusDistribution:
type: object
additionalProperties: false
required: [status, count, sharePercent]
properties:
status: {$ref: '#/components/schemas/SessionStatus'}
count:
type: integer
format: int64
minimum: 0
sharePercent:
type: [string, 'null']
pattern: '^(100|[0-9]{1,2})(\.[0-9]{1,6})?$'
TransactionStatusTrendPoint:
type: object
additionalProperties: false
required: [localDate, paidSessions, verifiedSessions, failedSessions, reversedSessions, totalSessions]
properties:
localDate:
type: string
format: date
localHour:
type: integer
minimum: 0
maximum: 23
paidSessions: {type: integer, format: int64, minimum: 0}
verifiedSessions: {type: integer, format: int64, minimum: 0}
failedSessions: {type: integer, format: int64, minimum: 0}
reversedSessions: {type: integer, format: int64, minimum: 0}
totalSessions: {type: integer, format: int64, minimum: 0}
TransactionsData:
type: object
additionalProperties: false
required: [totalSessions, bankSuccessRate, verificationCompletionRate, failureRate, statusDistribution, statusTrend]
properties:
totalSessions: {$ref: '#/components/schemas/IntegerMetric'}
bankSuccessRate: {$ref: '#/components/schemas/DecimalMetric'}
verificationCompletionRate: {$ref: '#/components/schemas/DecimalMetric'}
failureRate: {$ref: '#/components/schemas/DecimalMetric'}
statusDistribution:
type: array
minItems: 4
maxItems: 4
description: Canonical order is Paid, Verified, Failed, Reversed.
items: {$ref: '#/components/schemas/TransactionStatusDistribution'}
statusTrend:
type: array
maxItems: 31
items: {$ref: '#/components/schemas/TransactionStatusTrendPoint'}
FeesData:
type: object
additionalProperties: false
@@ -1898,6 +2375,16 @@ components:
metadata: {$ref: '#/components/schemas/ReadySectionMetadata'}
data: {$ref: '#/components/schemas/PaymentPerformanceData'}
TransactionsReadySection:
type: object
additionalProperties: false
required: [sectionId, state, metadata, data]
properties:
sectionId: {type: string, const: transactions}
state: {type: string, const: READY}
metadata: {$ref: '#/components/schemas/ReadySectionMetadata'}
data: {$ref: '#/components/schemas/TransactionsData'}
FeesReadySection:
type: object
additionalProperties: false
@@ -1966,6 +2453,12 @@ components:
- $ref: '#/components/schemas/CalculatingSection'
- $ref: '#/components/schemas/FailedSection'
- $ref: '#/components/schemas/PaymentPerformanceReadySection'
TransactionsSection:
oneOf:
- $ref: '#/components/schemas/QueuedSection'
- $ref: '#/components/schemas/CalculatingSection'
- $ref: '#/components/schemas/FailedSection'
- $ref: '#/components/schemas/TransactionsReadySection'
FeesSection:
oneOf:
- $ref: '#/components/schemas/QueuedSection'
@@ -2172,6 +2665,7 @@ components:
oneOf:
- $ref: '#/components/schemas/OverviewReadySection'
- $ref: '#/components/schemas/PaymentPerformanceReadySection'
- $ref: '#/components/schemas/TransactionsReadySection'
- $ref: '#/components/schemas/FeesReadySection'
- $ref: '#/components/schemas/PeerComparisonReadySection'
SectionFailedEvent: