A custom POST action should not inherit the permission that guards create. In our open-source role-scopes it doesn't, and we built that refusal in on purpose; it is not a gap.
The permission class decides which Permission gates a request before it asks whether the actor holds it. That lookup runs in a fixed order.
First the viewset action name, checked against the class's own actions mapping. Then the HTTP method. An explicit entry under either one wins.
Only when neither is mapped does a fallback apply, and what the view claims to be decides which fallback you get. A viewset action consults VIEWSET_ACTIONS, which maps list, retrieve and create, and nothing else. A plain APIView consults METHOD_ACTIONS, where GET, HEAD and OPTIONS are view and POST is create.
update, destroy and custom actions are missing from the viewset table on purpose. So a custom POST route has no verb to fall back on. It stays unmapped, and unmapped means refused.
The denial is loud rather than silent. A name the matrix lacks resolves to None, and the gate turns None into a failure under a rule that says no permission is mapped for this action, naming the actor or ANONYMOUS. Only after that does it reject a request carrying no actor at all. The ordering matters: you learn the route was never wired before you learn you were not logged in. That message travels into the 403 body, so you read the real reason instead of a generic forbidden.
One more line of defence sits earlier than any request. Building the permission class refuses a resource outside the declared set with ValueError, and it resolves every mapped value through Permission() as the class is constructed. An undeclared capability fails at import, not on the first production call.
The trade-off is real and we chose it knowingly. Every custom action now costs one line in a mapping, and a forgotten line shows up as a 403 in review. The alternative, inheriting create from the verb, costs a quiet grant that nobody reviews at all.
When an endpoint in your system is mapped to nothing, it either denies or falls back to the method. We would rather you hit the deny.