Entitlement errors are designed for UIs that upsell. A locked feature should show a lock and say which plan unlocks it, instead of disappearing. Every denial therefore carries enough information to render that: the privilege, the limit, the usage, and requiredPlan.
EntitlementLimitExceeded MUST carry privilege, feature, limit (the resolved value), current (as passed), requested, the full resolution details, and requiredPlan when Requirement 3.4.1 yields one.
EntitlementLimitExceeded and FeatureNotEntitled MUST share a common base type, EntitlementDenied, so an application can map every denial to one HTTP response.
SDKs MUST also define TypeMismatch, InvalidArgument, NotSupported, NotFound, Conflict, and InvalidValue. These signal programming or administrative errors and MUST NOT extend EntitlementDenied.
Every error defined in this section MUST expose a stable machine-readable code: entitlement_limit_exceeded, feature_not_entitled, TYPE_MISMATCH, INVALID_ARGUMENT, NOT_SUPPORTED, NOT_FOUND, CONFLICT, INVALID_VALUE.
The body follows RFC 9457 problem details. error MUST be entitlement_required, so clients can tell an entitlement 403 from an RBAC 403. limit MUST use the UNLIMITED encoding of Requirement 1.2.3. Fields that don’t apply (limit, current, and requested for FeatureNotEntitled, or requiredPlan when unknown) MUST be omitted.
An entitlement denial MUST NOT map to 401 or 404(not mechanically testable for applications, testable for toHttpProblem).
404 means the code isn’t released (feature flag), and 401 means “who are you?”. Using either for an entitlement leaks one layer into another, and the UI can no longer show the lock.
When a denial or isEnabled details carry requiredPlan, show the feature as locked, with an upgrade path to that plan.
When reason is ERROR, don’t show an upsell: the customer may well be entitled, and the system just doesn’t know right now.
When the current usage is above the limit (after a downgrade), show the data, explain the limit, and offer to upgrade or clean up. Don’t hide anything.