CWP version: 1.9
OS: AlmaLinux release 8.10 (Cerulean Leopard)
API: external API on port 2304, JSON
SummaryAn API key with
Account / UPD granted is refused on
/v1/account with
action=udp. Every other action on the same key and the same
Account function
works. CWP's own debug log shows it resolving the request to
accout_upd — the grant
that is switched on — and refusing it anyway, with
Unauthorized2 action rather than
the usual
Unauthorized action.
Steps to reproduce1. Create an API key in API Manager. Grant
Account ADD, UPD, DEL, LIST, SUSP, UNSP.
2. Call
/v1/account with
action=list — succeeds.
3. Call
/v1/account with
action=udp for an existing account — fails.
curl -sk https://SERVER:2304/v1/account \
-d "key=KEY" -d "action=udp" -d "user=USERNAME" \
-d "email=user@example.com" -d "package=@12" \
-d "inode=0" -d "openfiles=150" -d "processes=40"
Expected{"status":"OK"}, with the account's inode / open-file / process limits updated.
Actual{"status":"Error","msj":"Unauthorized action KEY"}
From /var/log/cwp/cwp_api.log (debug=1), key maskedA successful call in the same session — note it ends with a
return: clause:
2026-08-23 08:09:25 authorized
2026-08-23 08:09:25 Key:<KEY>, action:changepack_udp, ip:<CLIENT> return:{status:OK,format:JSON}
2026-08-23 08:09:25 {status:OK} -> {key:<KEY>,action:udp,user:USERNAME,adds:null,package:8}
The failing call, one second later, same key, same source address:
2026-08-23 08:09:26 Unauthorized2 action
2026-08-23 08:09:26 Key:<KEY>, action:accout_upd, ip:<CLIENT>
2026-08-23 08:09:26 Error
Two things stand out:
- The action it checked is accout_upd — exactly the permission API Manager saves
for the Account / UPD checkbox (value="accout_upd" in
mod_reseller_apimanager.html), and exactly the one that is granted. - It logs Unauthorized2 action, not Unauthorized action, and the line
carries no return: clause where every authorised call carries one. That looks like a
second authorisation check which the grant alone does not satisfy.
Ruled out- The grant. Account ADD, UPD, DEL, LIST, SUSP and UNSP are all on in the grid.
- The key. The same key succeeds in the same second on packages/list
(pack_list), changepack/udp (changepack_udp) and
accountdetail/list (detailaccount_list). - The request. It matches the Interactive Documentation for account/udp
field for field: user, email, package with the documented
@ prefix, inode, openfiles, processes. Omitting
email produces a different, correct error, so the request is being parsed. - A stale saved key. Toggling Account / UPD off, saving, on, saving again changes
nothing. - A missing companion grant. Adding Account Quota / LIST, Quota limit / LIST and
Metadata Account / LIST changes nothing. - The source address. The refused call comes from the same IP as the calls that
succeed, in the same second.
Impactaccount/udp is the only endpoint that sets a per-account inode, open-file or process
limit — Account Quota and Quota limit have no update action, and those three are not package
properties, so
packages/add and
packages/udp cannot carry them. With this
call refused there is no way to change those limits on an existing account through the API.
This affects my WHMCS module using the endpoint.
QuestionIs
accout_upd the permission this endpoint actually requires, and what is the second
check that logs
Unauthorized2? If the endpoint wants a grant that API Manager does
not expose, that would explain the behaviour.