Admin Service Public API Standard
Goal
admin_service public APIs must match the old admin_server v2 responsibility
boundary:
- Supabase owns normal list, detail, report, statistics, and log reads.
admin_serviceowns writes, business actions, approvals, state transitions, orchestration, and narrowly-scoped PII reveal actions.- External docs must expose only the canonical public surface, not historical compatibility aliases.
Route Style
Use a hybrid style:
- Resource writes use RESTful methods.
- Business commands use explicit
POST .../actions/.... - Sensitive PII reveal uses explicit
POST .../pii/reveal. - Broad reads do not exist on the public router.
HTTP Method Rules
| Case | Method | Pattern | Notes |
|---|---|---|---|
| Create resource | POST | /api/v1/{resources} | Do not use /create suffix. |
| Partial field update | PATCH | /api/v1/{resources}/{id} | For ordinary editable fields. |
| Replace subresource/config | PUT | /api/v1/{resources}/{id}/{subresource} | Idempotent replacement only. |
| Delete resource | DELETE | /api/v1/{resources}/{id} | Real delete only. |
| Business action | POST | /api/v1/{resources}/{id}/actions/{action} | Status transitions, audits, money, side effects. |
| Collection action | POST | /api/v1/{resources}/actions/{action} | Batch commands or action without one URL id. |
| PII reveal | POST | /api/v1/{resources}/pii/reveal | Body is exactly { "guids": [...] }; guids are positive integers; batch decrypts phone and bank fields, requires audit, and must not be a GET. |
| Health | GET | /health, /api/v1/health | The only public GETs. |
Naming Rules
- Resource names are plural and kebab-case:
players,player-deposits,bank-card-groups,message-templates. - Path parameters are explicit:
{player_id},{deposit_id},{brand_id}. - Action names are kebab-case verbs or verb phrases:
lock,unlock,reset-password,approve,decline,recycle,activate,import. - Do not use singular legacy prefixes such as
/player/depositor/coupon. - Do not use RPC-style suffixes directly under resources:
/edit-phone,/update-commission,/create,/send. - Do not expose broad query terms in public routes:
list,search,stats,logs,details,condition,history,meta.
Auth And Audit Rules
Every public non-health route must require:
- Admin JWT.
- Brand context when the target is brand-scoped. PII reveal is the exception:
the external system owns brand/permission filtering and sends only the
authorized
player.guidoragent.guidvalues to decrypt. - JWT-derived immutable admin account for writes/actions. Do not accept caller-supplied operator identity in request bodies for business attribution.
- Audit for money, approvals, state transitions, PII reveal, and security sensitive changes.
PII reveal responses must return only guid, phone_num, and bank_num, and
must not include full entity rows.
Because the reveal endpoints have a fixed meaning, they always return
phone_num and bank_num; callers must not send a fields selector or
encrypted PII values in the request body.
Current Public Surface Status
Current public_router has:
139public HTTP operations.2public GETs, both health checks.0broad query/list/log/stat routes.0internal operational routes.0legacy-style public aliases.0public WebSocket routes.- PII reveal actions:
POST /api/v1/players/pii/revealPOST /api/v1/agents/pii/reveal
Published Cleanup Status
The public surface is normalized before external publication. Historical route
handlers are still retained on the internal router for compatibility tests and
controlled transition work, but public_router hides them from OpenAPI and from
the externally mounted app.
Player Routes
| Current | Canonical | Action |
|---|---|---|
POST /api/v1/players/{player_id}/lock | POST /api/v1/players/{player_id}/actions/lock | Add canonical, deprecate current. |
POST /api/v1/players/{player_id}/unlock | POST /api/v1/players/{player_id}/actions/unlock | Add canonical, deprecate current. |
POST /api/v1/players/{player_id}/unlock-freeze | POST /api/v1/players/{player_id}/actions/unlock-freeze | Add canonical, deprecate current. |
POST /api/v1/players/{player_id}/edit-password | POST /api/v1/players/{player_id}/actions/reset-password | Rename action. |
POST /api/v1/players/{player_id}/edit-phone | PUT /api/v1/players/{player_id}/phone | Use explicit audited PII subresource replace. |
POST /api/v1/players/{player_id}/edit-level | PATCH /api/v1/players/{player_id}/level | Use explicit field subresource update. |
POST /api/v1/players/{player_id}/edit-group | PATCH /api/v1/players/{player_id}/group | Use explicit field subresource update. |
POST /api/v1/players/{player_id}/edit-agent | PATCH /api/v1/players/{player_id}/agent | Use explicit assignment update. |
POST /api/v1/players/{player_id}/edit-bank | PUT /api/v1/players/{player_id}/bank-account | Treat as sensitive subresource replace. |
POST /api/v1/players/edit-level-multi | PATCH /api/v1/players/bulk | Body contains ids and patch. |
POST /api/v1/players/edit-group-multi | PATCH /api/v1/players/bulk | Body contains ids and patch. |
POST /api/v1/players/{player_id}/tags | PUT /api/v1/players/{player_id}/tags | Remove duplicate tag update. |
POST /api/v1/players/tags/create | POST /api/v1/player-tags | Use resource collection. |
POST /api/v1/players/groups | POST /api/v1/player-groups | Use resource collection. |
DELETE /api/v1/players/groups/{group_id} | DELETE /api/v1/player-groups/{group_id} | Use resource collection. |
Finance And Approval Routes
| Current | Canonical | Action |
|---|---|---|
POST /api/v1/players/deposits/{deposit_id}/agree | POST /api/v1/player-deposits/{deposit_id}/actions/approve | Rename. |
POST /api/v1/players/deposits/{deposit_id}/decline | POST /api/v1/player-deposits/{deposit_id}/actions/decline | Rename. |
POST /api/v1/players/deposits/{deposit_id}/recycle | POST /api/v1/player-deposits/{deposit_id}/actions/recycle | Rename. |
POST /api/v1/players/deposits/{deposit_id}/coin-agree | POST /api/v1/player-deposits/{deposit_id}/actions/approve-coin | Rename. |
POST /api/v1/player/deposit/coin/agree | Remove from external docs | Legacy singular alias. |
POST /api/v1/player/deposit/decline | Remove from external docs | Legacy singular alias. |
POST /api/v1/player/deposit/recycle | Remove from external docs | Legacy singular alias. |
POST /api/v1/players/deposits/shooter-agree | POST /api/v1/player-deposits/actions/approve-shooter | Collection action. |
POST /api/v1/players/withdrawals/{withdraw_id}/review-agree | POST /api/v1/player-withdrawals/{withdraw_id}/actions/review-approve | Rename. |
POST /api/v1/players/withdrawals/{withdraw_id}/review-decline | POST /api/v1/player-withdrawals/{withdraw_id}/actions/review-decline | Rename. |
POST /api/v1/players/withdrawals/{withdraw_id}/pay-agree | POST /api/v1/player-withdrawals/{withdraw_id}/actions/pay-approve | Rename. |
POST /api/v1/players/withdrawals/{withdraw_id}/pay-decline | POST /api/v1/player-withdrawals/{withdraw_id}/actions/pay-decline | Rename. |
POST /api/v1/players/rolling/{rolling_id}/force-complete | POST /api/v1/player-rollings/{rolling_id}/actions/force-complete | Rename resource. |
Agent Routes
| Current | Canonical | Action |
|---|---|---|
POST /api/v1/agents/create | POST /api/v1/agents | Remove /create. |
POST /api/v1/agents/{agent_id}/reset-password | POST /api/v1/agents/{agent_id}/actions/reset-password | Add actions namespace. |
POST /api/v1/agents/{agent_id}/lock | POST /api/v1/agents/{agent_id}/actions/lock | Add actions namespace. |
POST /api/v1/agents/{agent_id}/unlock | POST /api/v1/agents/{agent_id}/actions/unlock | Add actions namespace. |
POST /api/v1/agents/{agent_id}/update-commission | PATCH /api/v1/agents/{agent_id}/commission | Treat as subresource update. |
POST /api/v1/agents/{agent_id}/adjust-balance | POST /api/v1/agents/{agent_id}/actions/adjust-balance | Money action. |
POST /api/v1/agents/{agent_id}/add-coupon | POST /api/v1/agents/{agent_id}/actions/add-coupon | Action. |
POST /api/v1/agents/{agent_id}/domains/create | POST /api/v1/agents/{agent_id}/domains | Remove /create. |
Promotion Routes
| Current | Canonical | Action |
|---|---|---|
POST /api/v1/coupons/create | POST /api/v1/coupons | Remove /create. |
POST /api/v1/coupons/grant | POST /api/v1/coupons/actions/grant | Collection action. |
POST /api/v1/coupons/recycle | POST /api/v1/coupons/{coupon_id}/actions/recycle | Prefer URL id. |
POST /api/v1/coupon/recycle | Remove from external docs | Legacy singular alias. |
POST /api/v1/coupons/send | POST /api/v1/coupons/actions/send | Collection action. |
POST /api/v1/coupon/send | Remove from external docs | Legacy singular alias. |
POST /api/v1/promotions/create | POST /api/v1/promotions | Remove /create. |
POST /api/v1/events/deposit/create | Not public | deposit_event is not present in the v2 schema; the legacy handler remains an internal not-implemented placeholder only. |
PUT /api/v1/events/deposit/{event_id} | Not public | Same blocker: no production deposit_event owner/table/route contract exists yet. |
POST /api/v1/events/register/create | POST /api/v1/register-events | Resource collection. |
POST /api/v1/events/register/update | PUT /api/v1/register-events/{event_id} | URL id required. |
POST /api/v1/events/attendance/create | POST /api/v1/attendance-events | Resource collection. |
PUT /api/v1/events/attendance/{event_id} | PUT /api/v1/attendance-events/{event_id} | Resource collection. |
POST /api/v1/message-templates/create | POST /api/v1/message-templates | Remove /create. |
POST /api/v1/message-templates/update | PUT /api/v1/message-templates/{template_id} | URL id required. |
System, Brand, Wallet, Upload Routes
| Current | Canonical | Action |
|---|---|---|
POST /api/v1/ip-blacklist | POST /api/v1/ip-blacklists | Pluralize. |
PUT /api/v1/ip-blacklist/{entry_id} | PUT /api/v1/ip-blacklists/{entry_id} | Pluralize. |
DELETE /api/v1/ip-blacklist/{entry_id} | DELETE /api/v1/ip-blacklists/{entry_id} | Pluralize. |
POST /api/v1/upload/image | POST /api/v1/uploads/images | Resource collection. |
POST /api/v1/brands/{brand_id}/enable | POST /api/v1/brands/{brand_id}/actions/enable | State action. |
POST /api/v1/brands/{brand_id}/disable | POST /api/v1/brands/{brand_id}/actions/disable | State action. |
POST /api/v1/brands/{brand_id}/config/import | POST /api/v1/brands/{brand_id}/config/actions/import | Action. |
POST /api/v1/agent-brand | POST /api/v1/agent-brands | Pluralize or model as /agents/{agent_id}/brands. |
PATCH /api/v1/agent-brand/{agent_id}/{brand_id} | PATCH /api/v1/agent-brands/{agent_id}/{brand_id} | Pluralize. |
DELETE /api/v1/agent-brand/{agent_id}/{brand_id} | DELETE /api/v1/agent-brands/{agent_id}/{brand_id} | Pluralize. |
PUT /api/v1/wallet/topologies/{code}/activate | POST /api/v1/wallet/topologies/{code}/actions/activate | State action. |
PUT /api/v1/wallet/policies/{policy_key}/activate | POST /api/v1/wallet/policies/{policy_key}/actions/activate | State action. |
Implemented Policy
- Internal
routerkeeps legacy handlers for compatibility tests and controlled migration. - External
public_routerexposes canonical aliases only. public_routerprunes:- broad reads,
- internal operational paths,
- singular legacy aliases,
/create,/edit-*,- historical action verbs outside
/actions.
- Public surface tests fail when a canonical alias is missing or a legacy alias leaks.
- External API docs must be generated from
public_router, not the internalrouter. - Placeholder routes whose backing table/domain contract does not exist, such
as
deposit_event, must not be registered as canonical aliases or published in OpenAPI. A future deposit-event migration must add the schema, owner, route tests, and OpenAPI contract in the same change.
Test Standard
Every public non-health operation must have either:
- a direct route test, or
- a documented exception in the public API contract test.
Minimum direct test coverage per operation:
- auth required,
- brand required when brand-scoped,
- happy-path envelope,
- audit/side-effect assertion for money, approval, state transition, and PII.