docs: define AI growth report API contract
This commit is contained in:
@@ -578,6 +578,66 @@ paths:
|
|||||||
'504':
|
'504':
|
||||||
$ref: '#/components/responses/ExportTimeout'
|
$ref: '#/components/responses/ExportTimeout'
|
||||||
|
|
||||||
|
/analytics/growth-reports:
|
||||||
|
post:
|
||||||
|
tags: [AI growth analyst]
|
||||||
|
operationId: createGrowthReport
|
||||||
|
summary: Create or reuse a growth report
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/IdempotencyKeyHeader'
|
||||||
|
- $ref: '#/components/parameters/CsrfTokenHeader'
|
||||||
|
- $ref: '#/components/parameters/OriginHeader'
|
||||||
|
- $ref: '#/components/parameters/RequestIdHeader'
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema: {$ref: '#/components/schemas/CreateGrowthReportRequest'}
|
||||||
|
responses:
|
||||||
|
'200': {description: Matching durable report reused., content: {application/json: {schema: {$ref: '#/components/schemas/GrowthReport'}}}}
|
||||||
|
'202': {description: Report accepted for generation., content: {application/json: {schema: {$ref: '#/components/schemas/GrowthReport'}}}}
|
||||||
|
'401': {$ref: '#/components/responses/Unauthenticated'}
|
||||||
|
'403': {$ref: '#/components/responses/Forbidden'}
|
||||||
|
'409': {$ref: '#/components/responses/Conflict'}
|
||||||
|
'422': {$ref: '#/components/responses/ValidationFailed'}
|
||||||
|
'429': {$ref: '#/components/responses/RateLimited'}
|
||||||
|
'503': {$ref: '#/components/responses/AnalyticsUnavailable'}
|
||||||
|
/analytics/growth-reports/latest:
|
||||||
|
get:
|
||||||
|
tags: [AI growth analyst]
|
||||||
|
operationId: getLatestGrowthReport
|
||||||
|
parameters:
|
||||||
|
- {name: terminalId, in: query, required: false, schema: {type: string}}
|
||||||
|
- $ref: '#/components/parameters/RequestIdHeader'
|
||||||
|
responses:
|
||||||
|
'200': {description: Latest successful report for the authorized scope., content: {application/json: {schema: {$ref: '#/components/schemas/GrowthReport'}}}}
|
||||||
|
'401': {$ref: '#/components/responses/Unauthenticated'}
|
||||||
|
'404': {$ref: '#/components/responses/ResourceNotFound'}
|
||||||
|
'503': {$ref: '#/components/responses/AnalyticsUnavailable'}
|
||||||
|
/analytics/growth-reports/{reportId}:
|
||||||
|
get:
|
||||||
|
tags: [AI growth analyst]
|
||||||
|
operationId: getGrowthReport
|
||||||
|
parameters:
|
||||||
|
- {name: reportId, in: path, required: true, schema: {type: string, pattern: '^growth_[a-f0-9]{24}$'}}
|
||||||
|
- $ref: '#/components/parameters/RequestIdHeader'
|
||||||
|
responses:
|
||||||
|
'200': {description: Authorized report snapshot., content: {application/json: {schema: {$ref: '#/components/schemas/GrowthReport'}}}}
|
||||||
|
'401': {$ref: '#/components/responses/Unauthenticated'}
|
||||||
|
'404': {$ref: '#/components/responses/ResourceNotFound'}
|
||||||
|
'503': {$ref: '#/components/responses/AnalyticsUnavailable'}
|
||||||
|
/analytics/growth-reports/{reportId}/events:
|
||||||
|
get:
|
||||||
|
tags: [AI growth analyst]
|
||||||
|
operationId: streamGrowthReportEvents
|
||||||
|
parameters:
|
||||||
|
- {name: reportId, in: path, required: true, schema: {type: string}}
|
||||||
|
- {name: Last-Event-ID, in: header, required: false, schema: {type: string}}
|
||||||
|
responses:
|
||||||
|
'200': {description: Stable-id report snapshots and heartbeats., content: {text/event-stream: {schema: {type: string}}}}
|
||||||
|
'401': {$ref: '#/components/responses/Unauthenticated'}
|
||||||
|
'404': {$ref: '#/components/responses/ResourceNotFound'}
|
||||||
|
|
||||||
components:
|
components:
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
merchantSession:
|
merchantSession:
|
||||||
@@ -943,6 +1003,159 @@ components:
|
|||||||
$ref: '#/components/schemas/Problem'
|
$ref: '#/components/schemas/Problem'
|
||||||
|
|
||||||
schemas:
|
schemas:
|
||||||
|
CreateGrowthReportRequest:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
properties:
|
||||||
|
terminalId: {type: string, minLength: 1, maxLength: 128}
|
||||||
|
refresh: {type: boolean, default: false}
|
||||||
|
GrowthRange:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [p10, p50, p90]
|
||||||
|
properties:
|
||||||
|
p10: {type: integer, format: int64, minimum: 0}
|
||||||
|
p50: {type: integer, format: int64, minimum: 0}
|
||||||
|
p90: {type: integer, format: int64, minimum: 0}
|
||||||
|
GrowthForecastPoint:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [date, baselineSessions, baselineAmountRial, paidSessions, paidAmountRial]
|
||||||
|
properties:
|
||||||
|
date: {type: string, format: date-time}
|
||||||
|
baselineSessions: {type: integer, format: int64, minimum: 0}
|
||||||
|
baselineAmountRial: {type: integer, format: int64, minimum: 0}
|
||||||
|
paidSessions: {$ref: '#/components/schemas/GrowthRange'}
|
||||||
|
paidAmountRial: {$ref: '#/components/schemas/GrowthRange'}
|
||||||
|
eventIds: {type: array, items: {type: string}}
|
||||||
|
GrowthForecast:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [startDate, horizonDays, confidence, points, backtest]
|
||||||
|
properties:
|
||||||
|
startDate: {type: string, format: date-time}
|
||||||
|
horizonDays: {type: integer, enum: [90]}
|
||||||
|
confidence: {type: string, enum: [HIGH, MEDIUM, INSUFFICIENT_DATA]}
|
||||||
|
suppressionReason: {type: string}
|
||||||
|
points: {type: array, maxItems: 90, items: {$ref: '#/components/schemas/GrowthForecastPoint'}}
|
||||||
|
backtest:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [origins, sessionMapeBps, amountMapeBps, intervalCoverageBps]
|
||||||
|
properties:
|
||||||
|
origins: {type: integer, minimum: 0}
|
||||||
|
sessionMapeBps: {type: integer, format: int64, minimum: 0}
|
||||||
|
amountMapeBps: {type: integer, format: int64, minimum: 0}
|
||||||
|
intervalCoverageBps: {type: integer, format: int64, minimum: 0, maximum: 10000}
|
||||||
|
GrowthHistoricalWin:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
required: [evidenceId, eventId, eventTitleFa, startDate, endDate, materialWin]
|
||||||
|
properties:
|
||||||
|
evidenceId: {type: string}
|
||||||
|
eventId: {type: string}
|
||||||
|
eventTitleFa: {type: string}
|
||||||
|
startDate: {type: string, format: date-time}
|
||||||
|
endDate: {type: string, format: date-time}
|
||||||
|
eligible: {type: boolean}
|
||||||
|
suppressionReason: {type: string}
|
||||||
|
observationCount: {type: integer}
|
||||||
|
baselineSessionCount: {type: integer, format: int64}
|
||||||
|
baselinePaidSessions: {type: integer, format: int64}
|
||||||
|
baselineAmountRial: {type: integer, format: int64}
|
||||||
|
actualSessionCount: {type: integer, format: int64}
|
||||||
|
actualPaidSessions: {type: integer, format: int64}
|
||||||
|
actualAmountRial: {type: integer, format: int64}
|
||||||
|
sessionUpliftBps: {type: integer, format: int64}
|
||||||
|
paidSessionUpliftBps: {type: integer, format: int64}
|
||||||
|
amountUpliftBps: {type: integer, format: int64}
|
||||||
|
materialWin: {type: boolean}
|
||||||
|
materialDecline: {type: boolean}
|
||||||
|
GrowthOpportunity:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [evidenceId, eventId, eventTitleFa, date, similarityBps, expectedSessions, expectedAmountRial, historicalEvidenceId]
|
||||||
|
properties:
|
||||||
|
evidenceId: {type: string}
|
||||||
|
eventId: {type: string}
|
||||||
|
eventTitleFa: {type: string}
|
||||||
|
date: {type: string, format: date-time}
|
||||||
|
similarityBps: {type: integer, format: int64, minimum: 6000, maximum: 10000}
|
||||||
|
expectedSessions: {$ref: '#/components/schemas/GrowthRange'}
|
||||||
|
expectedAmountRial: {$ref: '#/components/schemas/GrowthRange'}
|
||||||
|
historicalEvidenceId: {type: string}
|
||||||
|
GrowthRisk:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [evidenceId, date, sessionDeclineBps, amountDeclineBps, expectedSessions, expectedAmountRial]
|
||||||
|
properties:
|
||||||
|
evidenceId: {type: string}
|
||||||
|
date: {type: string, format: date-time}
|
||||||
|
endDate: {type: string, format: date-time}
|
||||||
|
sessionDeclineBps: {type: integer, format: int64, minimum: 2000}
|
||||||
|
amountDeclineBps: {type: integer, format: int64, minimum: 2000}
|
||||||
|
expectedSessions: {$ref: '#/components/schemas/GrowthRange'}
|
||||||
|
expectedAmountRial: {$ref: '#/components/schemas/GrowthRange'}
|
||||||
|
GrowthPeer:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [state, eligiblePeerCount, combinedPeerSessionCount, medianPaidSessions, percentileBps]
|
||||||
|
properties:
|
||||||
|
state: {type: string, enum: [READY, SUPPRESSED]}
|
||||||
|
reason: {type: string}
|
||||||
|
eligiblePeerCount: {type: integer, minimum: 0}
|
||||||
|
combinedPeerSessionCount: {type: integer, format: int64, minimum: 0}
|
||||||
|
medianPaidSessions: {type: integer, format: int64, minimum: 0}
|
||||||
|
percentileBps: {type: integer, format: int64, minimum: 0, maximum: 10000}
|
||||||
|
GrowthCampaignBrief:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [headline, interpretation, offerSuggestion, messageSuggestion, preparationAction, limitation, evidenceIds]
|
||||||
|
properties:
|
||||||
|
headline: {type: string}
|
||||||
|
interpretation: {type: string}
|
||||||
|
offerSuggestion: {type: string}
|
||||||
|
messageSuggestion: {type: string}
|
||||||
|
preparationAction: {type: string}
|
||||||
|
limitation: {type: string}
|
||||||
|
evidenceIds: {type: array, minItems: 1, items: {type: string}}
|
||||||
|
GrowthEvidence:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [forecast, historicalWins, opportunities, risks, fallbackBriefs]
|
||||||
|
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/GrowthCampaignBrief'}}
|
||||||
|
GrowthReport:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [reportId, status, scope, horizonDays, deterministicEvidence, cached, stale, nextRefreshAt, createdAt, updatedAt, eventsUrl]
|
||||||
|
properties:
|
||||||
|
reportId: {type: string}
|
||||||
|
status: {type: string, enum: [QUEUED, ANALYZING, GENERATING, READY, PARTIAL, FAILED]}
|
||||||
|
scope:
|
||||||
|
type: object
|
||||||
|
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'}}}
|
||||||
|
failureCode: {type: string}
|
||||||
|
cached: {type: boolean}
|
||||||
|
stale: {type: boolean}
|
||||||
|
nextRefreshAt: {type: string, format: date-time}
|
||||||
|
createdAt: {type: string, format: date-time}
|
||||||
|
updatedAt: {type: string, format: date-time}
|
||||||
|
eventsUrl: {type: string, format: uri-reference}
|
||||||
LoginRequest:
|
LoginRequest:
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
|
|||||||
Reference in New Issue
Block a user