본문으로 건너뛰기

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_service owns 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

CaseMethodPatternNotes
Create resourcePOST/api/v1/{resources}Do not use /create suffix.
Partial field updatePATCH/api/v1/{resources}/{id}For ordinary editable fields.
Replace subresource/configPUT/api/v1/{resources}/{id}/{subresource}Idempotent replacement only.
Delete resourceDELETE/api/v1/{resources}/{id}Real delete only.
Business actionPOST/api/v1/{resources}/{id}/actions/{action}Status transitions, audits, money, side effects.
Collection actionPOST/api/v1/{resources}/actions/{action}Batch commands or action without one URL id.
PII revealPOST/api/v1/{resources}/pii/revealBody is exactly { "guids": [...] }; guids are positive integers; batch decrypts phone and bank fields, requires audit, and must not be a GET.
HealthGET/health, /api/v1/healthThe 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/deposit or /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.guid or agent.guid values 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:

  • 139 public HTTP operations.
  • 2 public GETs, both health checks.
  • 0 broad query/list/log/stat routes.
  • 0 internal operational routes.
  • 0 legacy-style public aliases.
  • 0 public WebSocket routes.
  • PII reveal actions:
    • POST /api/v1/players/pii/reveal
    • POST /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

CurrentCanonicalAction
POST /api/v1/players/{player_id}/lockPOST /api/v1/players/{player_id}/actions/lockAdd canonical, deprecate current.
POST /api/v1/players/{player_id}/unlockPOST /api/v1/players/{player_id}/actions/unlockAdd canonical, deprecate current.
POST /api/v1/players/{player_id}/unlock-freezePOST /api/v1/players/{player_id}/actions/unlock-freezeAdd canonical, deprecate current.
POST /api/v1/players/{player_id}/edit-passwordPOST /api/v1/players/{player_id}/actions/reset-passwordRename action.
POST /api/v1/players/{player_id}/edit-phonePUT /api/v1/players/{player_id}/phoneUse explicit audited PII subresource replace.
POST /api/v1/players/{player_id}/edit-levelPATCH /api/v1/players/{player_id}/levelUse explicit field subresource update.
POST /api/v1/players/{player_id}/edit-groupPATCH /api/v1/players/{player_id}/groupUse explicit field subresource update.
POST /api/v1/players/{player_id}/edit-agentPATCH /api/v1/players/{player_id}/agentUse explicit assignment update.
POST /api/v1/players/{player_id}/edit-bankPUT /api/v1/players/{player_id}/bank-accountTreat as sensitive subresource replace.
POST /api/v1/players/edit-level-multiPATCH /api/v1/players/bulkBody contains ids and patch.
POST /api/v1/players/edit-group-multiPATCH /api/v1/players/bulkBody contains ids and patch.
POST /api/v1/players/{player_id}/tagsPUT /api/v1/players/{player_id}/tagsRemove duplicate tag update.
POST /api/v1/players/tags/createPOST /api/v1/player-tagsUse resource collection.
POST /api/v1/players/groupsPOST /api/v1/player-groupsUse resource collection.
DELETE /api/v1/players/groups/{group_id}DELETE /api/v1/player-groups/{group_id}Use resource collection.

Finance And Approval Routes

CurrentCanonicalAction
POST /api/v1/players/deposits/{deposit_id}/agreePOST /api/v1/player-deposits/{deposit_id}/actions/approveRename.
POST /api/v1/players/deposits/{deposit_id}/declinePOST /api/v1/player-deposits/{deposit_id}/actions/declineRename.
POST /api/v1/players/deposits/{deposit_id}/recyclePOST /api/v1/player-deposits/{deposit_id}/actions/recycleRename.
POST /api/v1/players/deposits/{deposit_id}/coin-agreePOST /api/v1/player-deposits/{deposit_id}/actions/approve-coinRename.
POST /api/v1/player/deposit/coin/agreeRemove from external docsLegacy singular alias.
POST /api/v1/player/deposit/declineRemove from external docsLegacy singular alias.
POST /api/v1/player/deposit/recycleRemove from external docsLegacy singular alias.
POST /api/v1/players/deposits/shooter-agreePOST /api/v1/player-deposits/actions/approve-shooterCollection action.
POST /api/v1/players/withdrawals/{withdraw_id}/review-agreePOST /api/v1/player-withdrawals/{withdraw_id}/actions/review-approveRename.
POST /api/v1/players/withdrawals/{withdraw_id}/review-declinePOST /api/v1/player-withdrawals/{withdraw_id}/actions/review-declineRename.
POST /api/v1/players/withdrawals/{withdraw_id}/pay-agreePOST /api/v1/player-withdrawals/{withdraw_id}/actions/pay-approveRename.
POST /api/v1/players/withdrawals/{withdraw_id}/pay-declinePOST /api/v1/player-withdrawals/{withdraw_id}/actions/pay-declineRename.
POST /api/v1/players/rolling/{rolling_id}/force-completePOST /api/v1/player-rollings/{rolling_id}/actions/force-completeRename resource.

Agent Routes

CurrentCanonicalAction
POST /api/v1/agents/createPOST /api/v1/agentsRemove /create.
POST /api/v1/agents/{agent_id}/reset-passwordPOST /api/v1/agents/{agent_id}/actions/reset-passwordAdd actions namespace.
POST /api/v1/agents/{agent_id}/lockPOST /api/v1/agents/{agent_id}/actions/lockAdd actions namespace.
POST /api/v1/agents/{agent_id}/unlockPOST /api/v1/agents/{agent_id}/actions/unlockAdd actions namespace.
POST /api/v1/agents/{agent_id}/update-commissionPATCH /api/v1/agents/{agent_id}/commissionTreat as subresource update.
POST /api/v1/agents/{agent_id}/adjust-balancePOST /api/v1/agents/{agent_id}/actions/adjust-balanceMoney action.
POST /api/v1/agents/{agent_id}/add-couponPOST /api/v1/agents/{agent_id}/actions/add-couponAction.
POST /api/v1/agents/{agent_id}/domains/createPOST /api/v1/agents/{agent_id}/domainsRemove /create.

Promotion Routes

CurrentCanonicalAction
POST /api/v1/coupons/createPOST /api/v1/couponsRemove /create.
POST /api/v1/coupons/grantPOST /api/v1/coupons/actions/grantCollection action.
POST /api/v1/coupons/recyclePOST /api/v1/coupons/{coupon_id}/actions/recyclePrefer URL id.
POST /api/v1/coupon/recycleRemove from external docsLegacy singular alias.
POST /api/v1/coupons/sendPOST /api/v1/coupons/actions/sendCollection action.
POST /api/v1/coupon/sendRemove from external docsLegacy singular alias.
POST /api/v1/promotions/createPOST /api/v1/promotionsRemove /create.
POST /api/v1/events/deposit/createNot publicdeposit_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 publicSame blocker: no production deposit_event owner/table/route contract exists yet.
POST /api/v1/events/register/createPOST /api/v1/register-eventsResource collection.
POST /api/v1/events/register/updatePUT /api/v1/register-events/{event_id}URL id required.
POST /api/v1/events/attendance/createPOST /api/v1/attendance-eventsResource collection.
PUT /api/v1/events/attendance/{event_id}PUT /api/v1/attendance-events/{event_id}Resource collection.
POST /api/v1/message-templates/createPOST /api/v1/message-templatesRemove /create.
POST /api/v1/message-templates/updatePUT /api/v1/message-templates/{template_id}URL id required.

System, Brand, Wallet, Upload Routes

CurrentCanonicalAction
POST /api/v1/ip-blacklistPOST /api/v1/ip-blacklistsPluralize.
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/imagePOST /api/v1/uploads/imagesResource collection.
POST /api/v1/brands/{brand_id}/enablePOST /api/v1/brands/{brand_id}/actions/enableState action.
POST /api/v1/brands/{brand_id}/disablePOST /api/v1/brands/{brand_id}/actions/disableState action.
POST /api/v1/brands/{brand_id}/config/importPOST /api/v1/brands/{brand_id}/config/actions/importAction.
POST /api/v1/agent-brandPOST /api/v1/agent-brandsPluralize 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}/activatePOST /api/v1/wallet/topologies/{code}/actions/activateState action.
PUT /api/v1/wallet/policies/{policy_key}/activatePOST /api/v1/wallet/policies/{policy_key}/actions/activateState action.

Implemented Policy

  1. Internal router keeps legacy handlers for compatibility tests and controlled migration.
  2. External public_router exposes canonical aliases only.
  3. public_router prunes:
    • broad reads,
    • internal operational paths,
    • singular legacy aliases,
    • /create,
    • /edit-*,
    • historical action verbs outside /actions.
  4. Public surface tests fail when a canonical alias is missing or a legacy alias leaks.
  5. External API docs must be generated from public_router, not the internal router.
  6. 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.