7.4 KiB
Contract — Config, Reference & Platform Signals (backend phase b1)
Admin config/holiday/audit/support-alert endpoints + the current-user notification endpoints. Assumes
../conventions/api-conventions.md+../conventions/money-and-types.md. Machine schema (authoritative for exact field/param casing):../openapi/swagger.v1.json.
Status: live as of backend-phase-1 · Frontend consumer: frontend-phase-f14 (notification center) / frontend-phase-f15 (admin config/holidays/audit/alerts)
All responses are the standard OperationResult→ApiResult envelope (camelCase body, snake_case URLs).
Lists carry { items, total, page, pageSize }. Pagination inputs are page (1-based) + pageSize
(default 50, max 100) — bound from the query string; derive exact casing from swagger.v1.json.
Enums used
- config
data_type:decimal|int|bool|string|json— how to parse a configvalue. - holiday
type:official|religious|national. - support-alert
type:low_rating|evv_no_show|evv_location_mismatch|verification_expired|payment_anomaly|fraud_signal. - support-alert
severity:low|medium|high. - support-alert
status:open|assigned|resolved(forward-only). - notification
type: open string code the front-end renders/deep-links on (e.g.booking_confirmed); its shape is thedata_jsoncontract (below), versioned per type.
Admin — Platform config (platform_config controller, [Authorize(DynamicPermission)])
GET api/v1/platform_config/get_platform_configs
- Purpose: list config rows. Auth: admin (DynamicPermission). Rate-limited: no.
- Query:
page,pageSize. - 200
data:PagedResult<PlatformConfigDto>—{ items:[{ key, value, dataType, description }], total, page, pageSize }.
POST api/v1/platform_config/update_platform_config
- Purpose: update one existing config row; writes an
audit_logsentry in the same transaction and evicts the cache. Auth: admin. - Body:
{ "key": "platform_fee_rate", "value": "0.18" }. - 200
data:true(empty-body success). - Failures:
400validation (empty key);404key does not exist. Notes: value is the raw string parsed per the row'sdata_type; changing a rate never retroactively re-prices already-computed rows.
GET api/v1/platform_config/get_config_change_history
- Purpose: the audited change history for one key (from the append-only trail). Auth: admin.
- Query:
key(required),page,pageSize. - 200
data:PagedResult<ConfigChangeDto>—{ items:[{ id, action, changedFieldsJson, actorUserId, occurredAt }], … }, newest first.
Admin — Holidays (holidays controller, [Authorize(DynamicPermission)])
GET api/v1/holidays/get_holidays
- Query:
from(date, optional),to(date, optional),page,pageSize. - 200
data:PagedResult<HolidayDto>—{ items:[{ id, holidayDate, nameFa, type, isBankClosed }], … }, by date.
POST api/v1/holidays/upsert_holiday
- Body:
{ "holidayDate": "2026-03-21", "nameFa": "نوروز", "type": "national", "isBankClosed": true }. - 200
data:true. Failures:400(badtype, emptynameFa, default date). Notes: upsert keyed onholidayDate.
POST api/v1/holidays/delete_holiday
- Body:
{ "holidayDate": "2026-03-21" }. 200:true; 404 if no holiday on that date.
Admin — Audit (audit controller, [Authorize(DynamicPermission)])
GET api/v1/audit/get_audit_trail
- Purpose: the immutable trail for one entity. Auth: admin.
- Query:
entity_type(e.g.PlatformConfig),entity_id(string),page,pageSize. - 200
data:PagedResult<AuditLogDto>—{ items:[{ id, entityType, entityId, action, changedFieldsJson, actorUserId, occurredAt }], … }, newest first. Notes: read-only; there is no write/update/delete endpoint for audit rows.
Admin — Support alerts (support_alerts controller, [Authorize(DynamicPermission)], never user-facing)
GET api/v1/support_alerts/get_support_alerts
- Query:
type?,status?,owner_user_id?,page,pageSize. - 200
data:PagedResult<SupportAlertDto>—{ items:[{ id, type, severity, status, entityType, entityId, bookingId, reviewId, ownerUserId, resolutionNote, resolvedAt, createdAt }], … }.
POST api/v1/support_alerts/assign_support_alert
- Body:
{ "alertId": 42, "ownerUserId": 7 }. 200:true(open → assigned); 404 if missing or already resolved.
POST api/v1/support_alerts/resolve_support_alert
- Body:
{ "alertId": 42, "note": "handled" }. 200:true(→ resolved); 404 if missing or already resolved.
Current user — Notifications (notifications controller, [Authorize], tenant-scoped)
Every endpoint is scoped to the signed-in caller (ICurrentUser) — never a body-supplied user id.
GET api/v1/notifications/get_notifications
- Query:
page,pageSize. - 200
data:PagedResult<NotificationDto>—{ items:[{ id, type, title, body, dataJson, isRead, readAt, createdAt }], … }, unread-first then newest-first.
GET api/v1/notifications/get_unread_count
- 200
data:{ count }— cheap index-backed count for the polling bell.
POST api/v1/notifications/mark_notification_read
- Body:
{ "notificationId": 100 }. 200:true; 404 if it isn't the caller's or doesn't exist.
POST api/v1/notifications/mark_all_read
- No body. 200:
true.
Not exposed via REST (internal contracts other backend domains call):
CreateNotification(viaINotificationDispatcher.DispatchAsync),RaiseSupportAlert(ISupportAlertService.RaiseAsync),EmitSystemEvent(IAnalyticsSink.EmitAsync),WriteAuditLog(IAuditLogger.WriteAsync). The notification retention purge runs on a background hosted service, not an endpoint.
Shared shapes
PlatformConfigDto:key(string),value(string, raw — parse perdataType),dataType(enum),description(string, nullable).ConfigChangeDto:id(long),action(created/updated/deleted),changedFieldsJson(string, nullable —{ "Field": { "old": …, "new": … } }; encrypted/PII fields redacted as"<redacted>"),actorUserId(int, nullable),occurredAt(UTC ISO-8601).HolidayDto:id(long),holidayDate(date),nameFa(string),type(enum),isBankClosed(bool).AuditLogDto:id(long),entityType(string),entityId(string),action,changedFieldsJson(nullable),actorUserId(nullable),occurredAt.NotificationDto:id(long),type(string code),title(string),body(string, nullable),dataJson(string, nullable — a typed, versioned deep-link payload; shape depends ontype, e.g.{"booking_id": 1}),isRead(bool),readAt(UTC, nullable),createdAt(UTC).SupportAlertDto:id,type,severity,status,entityType(string),entityId(string),bookingId(long, nullable),reviewId(long, nullable),ownerUserId(int, nullable),resolutionNote(string, nullable),resolvedAt(UTC, nullable),createdAt(UTC).
Changelog
- b1 — initial contract (config, holidays, audit, support alerts, notifications).