openapi: 3.0.3 info: title: IAM Notification Center API version: 1.0.0 description: First release supports IN_APP only and does not support deleting or clearing read messages. servers: [{url: /api}] tags: - {name: Notification inbox} - {name: Platform notification administration} paths: /{app}/v1/notifications: get: tags: [Notification inbox] summary: List current user's inbox parameters: - {$ref: '#/components/parameters/AppPath'} - {$ref: '#/components/parameters/Page'} - {$ref: '#/components/parameters/Size'} - {name: status, in: query, schema: {type: string, enum: [UNREAD, READ]}} - {name: category, in: query, schema: {type: string, maxLength: 64}} responses: {'200': {$ref: '#/components/responses/InboxPage'}} /{app}/v1/notifications/unread-count: get: tags: [Notification inbox] summary: Count current user's unread messages parameters: [{$ref: '#/components/parameters/AppPath'}] responses: {'200': {$ref: '#/components/responses/UnreadCount'}} /{app}/v1/notifications/{id}: get: tags: [Notification inbox] summary: Get one inbox message parameters: [{$ref: '#/components/parameters/AppPath'}, {$ref: '#/components/parameters/IdPath'}] responses: {'200': {$ref: '#/components/responses/InboxDetail'}} /{app}/v1/notifications/{id}/read: put: tags: [Notification inbox] summary: Mark one message as read (idempotent) description: Requires {app}:notification:read. parameters: [{$ref: '#/components/parameters/AppPath'}, {$ref: '#/components/parameters/IdPath'}] responses: {'200': {$ref: '#/components/responses/InboxDetail'}} /{app}/v1/notifications/read-all: put: tags: [Notification inbox] summary: Mark all current-user messages as read (idempotent) description: Requires {app}:notification:read-all. parameters: [{$ref: '#/components/parameters/AppPath'}] responses: {'200': {$ref: '#/components/responses/ReadAll'}} /platform/v1/notifications: post: tags: [Platform notification administration] summary: Create an IN_APP notification task requestBody: {$ref: '#/components/requestBodies/CreateNotification'} responses: {'200': {$ref: '#/components/responses/TaskItem'}} /platform/v1/notification-templates: get: tags: [Platform notification administration] summary: List templates parameters: - {$ref: '#/components/parameters/Page'} - {$ref: '#/components/parameters/Size'} - {name: templateCode, in: query, schema: {type: string, maxLength: 64}} - {name: category, in: query, schema: {type: string, maxLength: 64}} - {name: title, in: query, description: 'Fuzzy match template name or title template', schema: {type: string, maxLength: 128}} - {name: status, in: query, schema: {type: string, enum: [DRAFT, ACTIVE, DISABLED]}} responses: {'200': {$ref: '#/components/responses/TemplatePage'}} post: tags: [Platform notification administration] summary: Create a DRAFT template requestBody: {$ref: '#/components/requestBodies/CreateTemplate'} responses: {'200': {$ref: '#/components/responses/TemplateDetail'}} /platform/v1/notification-templates/{id}: put: tags: [Platform notification administration] summary: Edit a DRAFT template only parameters: [{$ref: '#/components/parameters/IdPath'}] requestBody: {$ref: '#/components/requestBodies/UpdateTemplate'} responses: {'200': {$ref: '#/components/responses/TemplateDetail'}} /platform/v1/notification-templates/{id}/status: put: tags: [Platform notification administration] summary: Change template status description: Only DRAFT to ACTIVE and ACTIVE to DISABLED are valid. DISABLED cannot return to ACTIVE; copy it to a new DRAFT instead. parameters: [{$ref: '#/components/parameters/IdPath'}] requestBody: {$ref: '#/components/requestBodies/ChangeTemplateStatus'} responses: {'200': {$ref: '#/components/responses/TemplateDetail'}} /platform/v1/notification-tasks: get: tags: [Platform notification administration] summary: List notification tasks parameters: - {$ref: '#/components/parameters/Page'} - {$ref: '#/components/parameters/Size'} - {name: taskNo, in: query, schema: {type: string, maxLength: 64}} - {name: category, in: query, schema: {type: string, maxLength: 64}} - {name: title, in: query, description: 'Fuzzy match rendered title', schema: {type: string, maxLength: 128}} - {name: status, in: query, schema: {type: string, enum: [CREATED, SCHEDULED, DISPATCHING, RETRYING, SENT, DEAD, CANCELLED]}} responses: {'200': {$ref: '#/components/responses/TaskPage'}} /platform/v1/notification-tasks/{id}: get: tags: [Platform notification administration] summary: Get notification task delivery details description: Uses the task list permission and exposes bounded operational evidence without message body content. parameters: [{$ref: '#/components/parameters/IdPath'}] responses: {'200': {$ref: '#/components/responses/TaskDetail'}} /platform/v1/notification-tasks/{id}/cancel: put: tags: [Platform notification administration] summary: Cancel a CREATED or SCHEDULED task parameters: [{$ref: '#/components/parameters/IdPath'}] responses: {'200': {$ref: '#/components/responses/TaskItem'}} /platform/v1/notification-tasks/{id}/retry: put: tags: [Platform notification administration] summary: Requeue a RETRYING or DEAD task description: Resets the current delivery-attempt counter and returns the task in RETRYING; scheduler delivery starts after the retry delay. parameters: [{$ref: '#/components/parameters/IdPath'}] responses: {'200': {$ref: '#/components/responses/TaskItem'}} components: parameters: AppPath: {name: app, in: path, required: true, schema: {type: string, enum: [platform, merchant]}} IdPath: {name: id, in: path, required: true, schema: {type: integer, format: int64}} Page: {name: page, in: query, schema: {type: integer, minimum: 1, default: 1}} Size: {name: size, in: query, schema: {type: integer, minimum: 1, maximum: 200, default: 20}} requestBodies: CreateNotification: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/CreateNotificationRequest'}}}} CreateTemplate: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/CreateTemplateRequest'}}}} UpdateTemplate: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/UpdateTemplateRequest'}}}} ChangeTemplateStatus: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ChangeTemplateStatusRequest'}}}} responses: InboxPage: {description: OK, content: {application/json: {schema: {$ref: '#/components/schemas/InboxPageResponse'}}}} InboxDetail: {description: OK, content: {application/json: {schema: {$ref: '#/components/schemas/InboxDetailResponse'}}}} UnreadCount: {description: OK, content: {application/json: {schema: {$ref: '#/components/schemas/UnreadCountResponse'}}}} ReadAll: {description: OK, content: {application/json: {schema: {$ref: '#/components/schemas/ReadAllResponse'}}}} TemplatePage: {description: OK, content: {application/json: {schema: {$ref: '#/components/schemas/TemplatePageResponse'}}}} TemplateDetail: {description: OK, content: {application/json: {schema: {$ref: '#/components/schemas/TemplateDetailResponse'}}}} TaskPage: {description: OK, content: {application/json: {schema: {$ref: '#/components/schemas/TaskPageResponse'}}}} TaskItem: {description: OK, content: {application/json: {schema: {$ref: '#/components/schemas/TaskItemResponse'}}}} TaskDetail: {description: OK, content: {application/json: {schema: {$ref: '#/components/schemas/TaskDetailResponse'}}}} schemas: CreateNotificationRequest: type: object required: [tenantId, templateCode, recipientType, variables, idempotencyKey] description: tenantId is a backend compatibility field. 0 means platform scope and a positive value is the customer (enterprise) ID; UI copy must use customer or enterprise, never tenant. properties: tenantId: {type: integer, format: int64, minimum: 0} templateCode: {type: string, maxLength: 64} recipientType: {type: string, enum: [USER, MEMBERSHIP, TENANT_ROLE, TENANT_ALL, PLATFORM_ROLE]} recipientIds: {type: array, minItems: 1, maxItems: 1000, items: {type: integer, format: int64}, description: "Required for USER or MEMBERSHIP; forbidden for role/all recipient types. For a specified enterprise member, pass the user's ID and use USER; membership IDs are reserved for explicit membership-level calls."} roleCode: {type: string, minLength: 1, maxLength: 64, description: Required for TENANT_ROLE or PLATFORM_ROLE; forbidden otherwise.} variables: {type: object, maxProperties: 32, additionalProperties: {type: string, maxLength: 2000}} scheduledAt: {type: string, format: date-time, nullable: true} idempotencyKey: {type: string, minLength: 8, maxLength: 128} sourceEventId: {type: string, maxLength: 128, nullable: true} oneOf: - required: [recipientIds] properties: recipientType: {type: string, enum: [USER, MEMBERSHIP]} not: {required: [roleCode]} - required: [roleCode] properties: recipientType: {type: string, enum: [TENANT_ROLE, PLATFORM_ROLE]} not: {required: [recipientIds]} - properties: recipientType: {type: string, enum: [TENANT_ALL]} not: anyOf: - required: [recipientIds] - required: [roleCode] CreateTemplateRequest: type: object required: [templateCode, templateName, category, titleTemplate, bodyTemplate, variableWhitelist] properties: {templateCode: {type: string, pattern: '^[A-Z][A-Z0-9_]{0,63}$'}, templateName: {type: string, maxLength: 128}, category: {type: string, maxLength: 64}, titleTemplate: {type: string, maxLength: 256}, bodyTemplate: {type: string, maxLength: 10000}, variableWhitelist: {type: array, maxItems: 32, uniqueItems: true, items: {type: string, pattern: '^[a-z][a-z0-9_]{0,31}$'}}} UpdateTemplateRequest: type: object required: [templateName, category, titleTemplate, bodyTemplate, variableWhitelist, version] properties: {templateName: {type: string, maxLength: 128}, category: {type: string, maxLength: 64}, titleTemplate: {type: string, maxLength: 256}, bodyTemplate: {type: string, maxLength: 10000}, variableWhitelist: {type: array, maxItems: 32, uniqueItems: true, items: {type: string, pattern: '^[a-z][a-z0-9_]{0,31}$'}}, version: {type: integer, minimum: 1}} ChangeTemplateStatusRequest: type: object required: [targetStatus, version] properties: {targetStatus: {type: string, enum: [ACTIVE, DISABLED], description: ACTIVE is accepted only from DRAFT; DISABLED only from ACTIVE.}, version: {type: integer, minimum: 1}} InboxItem: {type: object, required: [id, category, title, summary, status, sentAt], properties: {id: {type: integer, format: int64}, category: {type: string}, title: {type: string}, summary: {type: string, maxLength: 120, description: Whitespace-compacted body preview limited to 120 Unicode code points; never contains the full body unless the body itself fits.}, status: {type: string, enum: [UNREAD, READ]}, sentAt: {type: string, format: date-time}, readAt: {type: string, format: date-time, nullable: true}}} InboxDetail: {allOf: [{$ref: '#/components/schemas/InboxItem'}, {type: object, required: [body], properties: {body: {type: string}, sourceType: {type: string, nullable: true}, sourceId: {type: string, nullable: true}}}]} TemplateItem: {type: object, required: [id, templateCode, templateName, category, channel, titleTemplate, bodyTemplate, variableWhitelist, status, version], properties: {id: {type: integer, format: int64}, templateCode: {type: string}, templateName: {type: string}, category: {type: string}, channel: {type: string, enum: [IN_APP]}, titleTemplate: {type: string}, bodyTemplate: {type: string}, variableWhitelist: {type: array, items: {type: string}}, status: {type: string, enum: [DRAFT, ACTIVE, DISABLED]}, version: {type: integer}}} TaskItem: {type: object, required: [id, taskNo, category, title, status, recipientCount, duplicated], properties: {id: {type: integer, format: int64}, taskNo: {type: string}, category: {type: string}, title: {type: string}, status: {type: string, enum: [CREATED, SCHEDULED, DISPATCHING, RETRYING, SENT, DEAD, CANCELLED]}, recipientCount: {type: integer}, scheduledAt: {type: string, format: date-time, nullable: true}, sentAt: {type: string, format: date-time, nullable: true}, duplicated: {type: boolean}}} TaskRecipientItem: {type: object, required: [id, displayName, appCode, status], properties: {id: {type: integer, format: int64}, displayName: {type: string}, appCode: {type: string}, status: {type: string}, failureReason: {type: string, nullable: true}, deliveredAt: {type: string, format: date-time, nullable: true}}} TaskDetail: {type: object, required: [id, taskNo, category, title, status, recipientCount, attemptCount, recipients], properties: {id: {type: integer, format: int64}, taskNo: {type: string}, category: {type: string}, title: {type: string}, status: {type: string, enum: [CREATED, SCHEDULED, DISPATCHING, RETRYING, SENT, DEAD, CANCELLED]}, recipientCount: {type: integer}, attemptCount: {type: integer, minimum: 0, maximum: 3, description: Number of started delivery attempts in the current automatic retry cycle.}, scheduledAt: {type: string, format: date-time, nullable: true}, sentAt: {type: string, format: date-time, nullable: true}, lastError: {type: string, nullable: true}, recipients: {type: array, items: {$ref: '#/components/schemas/TaskRecipientItem'}}}} InboxPageData: {type: object, required: [records, page, size, total, pages], properties: {records: {type: array, items: {$ref: '#/components/schemas/InboxItem'}}, page: {type: integer}, size: {type: integer}, total: {type: integer}, pages: {type: integer}}} TemplatePageData: {type: object, required: [records, page, size, total, pages], properties: {records: {type: array, items: {$ref: '#/components/schemas/TemplateItem'}}, page: {type: integer}, size: {type: integer}, total: {type: integer}, pages: {type: integer}}} TaskPageData: {type: object, required: [records, page, size, total, pages], properties: {records: {type: array, items: {$ref: '#/components/schemas/TaskItem'}}, page: {type: integer}, size: {type: integer}, total: {type: integer}, pages: {type: integer}}} InboxPageResponse: {type: object, required: [code, data], properties: {code: {type: string, example: '200'}, data: {$ref: '#/components/schemas/InboxPageData'}}} InboxDetailResponse: {type: object, required: [code, data], properties: {code: {type: string, example: '200'}, data: {$ref: '#/components/schemas/InboxDetail'}}} UnreadCountResponse: {type: object, required: [code, data], properties: {code: {type: string, example: '200'}, data: {type: object, required: [unreadCount], properties: {unreadCount: {type: integer, format: int64}}}}} ReadAllResponse: {type: object, required: [code, data], properties: {code: {type: string, example: '200'}, data: {type: object, required: [updatedCount], properties: {updatedCount: {type: integer, format: int64}}}}} TemplatePageResponse: {type: object, required: [code, data], properties: {code: {type: string, example: '200'}, data: {$ref: '#/components/schemas/TemplatePageData'}}} TemplateDetailResponse: {type: object, required: [code, data], properties: {code: {type: string, example: '200'}, data: {$ref: '#/components/schemas/TemplateItem'}}} TaskPageResponse: {type: object, required: [code, data], properties: {code: {type: string, example: '200'}, data: {$ref: '#/components/schemas/TaskPageData'}}} TaskItemResponse: {type: object, required: [code, data], properties: {code: {type: string, example: '200'}, data: {$ref: '#/components/schemas/TaskItem'}}} TaskDetailResponse: {type: object, required: [code, data], properties: {code: {type: string, example: '200'}, data: {$ref: '#/components/schemas/TaskDetail'}}}