diff --git a/backend/secretapi/app.go b/backend/secretapi/app.go index 7788b2b..6fa98b9 100644 --- a/backend/secretapi/app.go +++ b/backend/secretapi/app.go @@ -1827,9 +1827,11 @@ func writeErrorCode(w http.ResponseWriter, status int, code string, message stri correlationID = "req_" + rid } normalizedCode := strings.TrimSpace(strings.ToLower(code)) + canonicalCode := canonicalErrorCode(normalizedCode) writeJSON(w, status, map[string]string{ "error": message, "code": normalizedCode, + "canonical_code": canonicalCode, "next_step": errorNextStepForCode(normalizedCode), "correlation_id": correlationID, }) @@ -1867,6 +1869,20 @@ func errorNextStepForCode(code string) string { } } +func canonicalErrorCode(code string) string { + code = strings.TrimSpace(strings.ToLower(code)) + switch code { + case "membership_inactive": + return "edut_id_inactive" + case "membership_required": + return "edut_id_required" + case "membership_verification_failed": + return "edut_id_verification_failed" + default: + return code + } +} + func appendUniqueString(existing []string, value string) []string { value = strings.TrimSpace(value) if value == "" { diff --git a/docs/api/examples/governance-installer.examples.md b/docs/api/examples/governance-installer.examples.md index c6ac43b..9f6ea52 100644 --- a/docs/api/examples/governance-installer.examples.md +++ b/docs/api/examples/governance-installer.examples.md @@ -110,6 +110,7 @@ Authorization: Bearer { "error": "entitlement_inactive", "code": "entitlement_inactive", + "canonical_code": "entitlement_inactive", "correlation_id": "req_01J9B6BJBVKY3N6WQ7TQM2R2P5" } ``` diff --git a/docs/api/examples/member-channel.examples.md b/docs/api/examples/member-channel.examples.md index fa4e304..025dc8a 100644 --- a/docs/api/examples/member-channel.examples.md +++ b/docs/api/examples/member-channel.examples.md @@ -107,6 +107,7 @@ Content-Type: application/json { "error": "membership_inactive", "code": "membership_inactive", + "canonical_code": "edut_id_inactive", "correlation_id": "req_01J9A4Q9GPDXDNZEWJ2FJS6F5R" } ``` @@ -145,6 +146,7 @@ Content-Type: application/json { "error": "contact_your_org_admin", "code": "owner_role_required", + "canonical_code": "owner_role_required", "correlation_id": "req_01J9A6ZC0SYF5A0M8AP8BNX7B2" } ``` @@ -155,6 +157,7 @@ Content-Type: application/json { "error": "owner support actions require onramp_attested identity assurance", "code": "identity_assurance_insufficient", + "canonical_code": "identity_assurance_insufficient", "correlation_id": "req_01J9A72Q2Q0VQ8MCR7D2N95D3R" } ``` diff --git a/docs/api/governance-installer.openapi.yaml b/docs/api/governance-installer.openapi.yaml index 6c46ebc..a84a6f6 100644 --- a/docs/api/governance-installer.openapi.yaml +++ b/docs/api/governance-installer.openapi.yaml @@ -389,6 +389,10 @@ components: type: string code: type: string + description: Emitted compatibility code (may be legacy). + canonical_code: + type: string + description: Canonical EDUT ID era code alias for deterministic client routing. next_step: type: string description: Deterministic operator guidance for remediation/retry. diff --git a/docs/api/member-channel.openapi.yaml b/docs/api/member-channel.openapi.yaml index e25321a..e08ff62 100644 --- a/docs/api/member-channel.openapi.yaml +++ b/docs/api/member-channel.openapi.yaml @@ -413,6 +413,10 @@ components: type: string code: type: string + description: Emitted compatibility code (may be legacy). + canonical_code: + type: string + description: Canonical EDUT ID era code alias for deterministic client routing. next_step: type: string description: Deterministic operator guidance for remediation/retry.