Skip to content

Admin REST API

@adocommercekit/admin-api publishes 42 host-owned endpoints under /api/commerce/admin, plus the admin service layer both admin surfaces share.

@adocommercekit/admin-api publishes 42 host-owned endpoints under /api/commerce/admin, plus the admin service layer both admin surfaces share. A headless host can stop here: everything the Inertia panel does is reachable through this contract.

pnpm add @adocommercekit/admin-api @adonisjs/bouncer
node ace configure @adocommercekit/admin-api
node ace migration:run
node ace commerce:doctor

configure() aborts unless @adocommercekit/core is already configured, then publishes the controllers, validators, middleware, policy tree, routes, and the audit-table migration into your application and records an upgrade baseline for each file. Nothing runs from the package at request time.

WARNING — Every ability denies by default

The published policy stubs grant nothing. Implement commerceAdminRoles() in app/policies/commerce/main.ts before an operator can do anything; until you do, every endpoint answers 403. commerce:doctor reports the unmapped state as a failure. See Abilities and policies.

Conventions

  • Every request is authenticated. There is no anonymous admin route, metrics included; an unauthenticated request answers 401 E_UNAUTHORIZED.
  • Every route names exactly one ability from the frozen catalog and checks it before the controller body runs. A denial is 403 E_COMMERCE_FORBIDDEN with the ability name in details.ability — ability names are vocabulary, not secrets.
  • Shield/CSRF stays on. No admin route is exempt.
  • Responses are JSON. Money renders as { amount, currency, formatted, amountMinor }; amountMinor is the stringified integer minor-unit value, so charts and exports never parse a formatted string.
  • Everything serialized here goes through a transformer’s forAdmin variant. Provider credentials, encrypted columns, raw provider payloads, and customer cart tokens never appear.
  • Rate limits are per resolved actor: mutations 60/min, metrics 120/min, other reads 240/min. Every response carries X-RateLimit-*.
  • Validation failures answer 422 with the commerce envelope and details.messages, never a redirect.
{
  "error": {
    "code": "E_COMMERCE_FORBIDDEN",
    "message": "Missing ability commerce.payments.refund",
    "details": { "ability": "commerce.payments.refund" }
  }
}

Branch on error.code. Codes specific to this surface:

Code Status Meaning
E_UNAUTHORIZED 401 No authenticated actor.
E_COMMERCE_FORBIDDEN 403 The actor lacks the endpoint’s ability.
E_COMMERCE_INVALID_TRANSITION 409 The state machine refused; details.currentState carries the state the surface observed.
E_IDEMPOTENCY_CONFLICT 409 An Idempotency-Key was reused with a different payload.
E_VALIDATION_ERROR 422 The payload, or a value a service rejected, is invalid.

Lists

Every index endpoint is keyset paginated and takes the same shape:

Query Default Constraint
limit 25 Integer from 1 through 100
cursor none Opaque cursor from a previous meta.nextCursor; a tampered value is a 422, never a silent reset to page one
direction desc asc or desc
sort per resource See the resource’s filters
{
  "data": [],
  "meta": { "nextCursor": "eyJ...", "total": 42 }
}

meta.total is null when the filtered count is not obtainable cheaply; render that as “1–25 of many” rather than guessing.

Idempotency

POST capture, refund, void, cancel, transition, adjustment, and shipment-creation endpoints accept an Idempotency-Key header. A replay returns the first response byte-for-byte with Idempotent-Replay: true; the same key is also handed to the payment provider, so deduplication holds at both layers. Reusing a key with a different payload is 409 E_IDEMPOTENCY_CONFLICT.

Audit trail

Every mutation writes one append-only entry in the same database transaction as its effect, so the record and the change commit or roll back together. Entries carry the actor id and label, the ability, the subject, a scalar-only before/after summary, the idempotency key, and the request id. Summaries are PII-free by construction: the service rejects a summary carrying a name, address, email, phone, or credential-shaped field.

Read the trail through GET /api/commerce/admin/audit-entries, filterable by actorId, ability[], subjectType, subjectId, createdAfter, and createdBefore.

Endpoints

Catalog

Method Path Route name Ability
GET /api/commerce/admin/products commerce.admin.products.index commerce.products.view
POST /api/commerce/admin/products commerce.admin.products.store commerce.products.manage
GET /api/commerce/admin/products/:id commerce.admin.products.show commerce.products.view
PATCH /api/commerce/admin/products/:id commerce.admin.products.update commerce.products.manage
DELETE /api/commerce/admin/products/:id commerce.admin.products.destroy commerce.products.manage
POST /api/commerce/admin/products/:id/archive commerce.admin.products.archive commerce.products.manage
POST /api/commerce/admin/products/:id/options commerce.admin.products.options.store commerce.products.manage
POST /api/commerce/admin/products/:id/variants/generate commerce.admin.products.variants.generate commerce.products.manage
PUT /api/commerce/admin/products/:id/categories commerce.admin.products.categories commerce.products.manage
POST /api/commerce/admin/products/:id/images commerce.admin.products.images.store commerce.products.manage
PATCH /api/commerce/admin/products/:id/images/:imageId commerce.admin.products.images.update commerce.products.manage
PUT /api/commerce/admin/products/:id/images/order commerce.admin.products.images.order commerce.products.manage
DELETE /api/commerce/admin/products/:id/images/:imageId commerce.admin.products.images.destroy commerce.products.manage
GET /api/commerce/admin/categories commerce.admin.categories.index commerce.products.view
POST /api/commerce/admin/categories commerce.admin.categories.store commerce.categories.manage
PATCH /api/commerce/admin/categories/:id commerce.admin.categories.update commerce.categories.manage
DELETE /api/commerce/admin/categories/:id commerce.admin.categories.destroy commerce.categories.manage

Image upload uses multipart/form-data with one through four images file fields. Each file may be JPEG, PNG, or WebP and at most 4 MiB; the service verifies the actual bytes. Update accepts { altText?: string | null, variantIds?: string[] }. Reorder accepts every current image exactly once as { imageIds: string[] }; omissions, duplicates, and foreign image ids are rejected. Upload and delete audit entries are committed with their database changes, and managed objects are cleaned up on rollback or after committed deletion.

Orders

Method Path Route name Ability
GET /api/commerce/admin/orders commerce.admin.orders.index commerce.orders.view
GET /api/commerce/admin/orders/:id commerce.admin.orders.show commerce.orders.view
POST /api/commerce/admin/orders/:id/transition commerce.admin.orders.transition commerce.orders.transition
POST /api/commerce/admin/orders/:id/cancel commerce.admin.orders.cancel commerce.orders.cancel
POST /api/commerce/admin/orders/:id/adjustments commerce.admin.orders.adjustments.store commerce.orders.adjust
GET /api/commerce/admin/orders/:id/payments commerce.admin.orders.payments.index commerce.payments.view

Payments

Method Path Route name Ability
POST /api/commerce/admin/payments/:id/capture commerce.admin.payments.capture commerce.payments.capture
POST /api/commerce/admin/payments/:id/refund commerce.admin.payments.refund commerce.payments.refund
POST /api/commerce/admin/payment-intents/:id/void commerce.admin.paymentIntents.void commerce.payments.void

Shipments

Method Path Route name Ability
POST /api/commerce/admin/orders/:id/shipments commerce.admin.shipments.store commerce.shipments.manage
POST /api/commerce/admin/shipments/:id/transition commerce.admin.shipments.transition commerce.shipments.manage

Inventory

Method Path Route name Ability
GET /api/commerce/admin/inventory/levels commerce.admin.inventory.index commerce.inventory.view
POST /api/commerce/admin/inventory/adjustments commerce.admin.inventory.adjust commerce.inventory.adjust

Customers

Method Path Route name Ability
GET /api/commerce/admin/customers commerce.admin.customers.index commerce.customers.view
GET /api/commerce/admin/customers/:id commerce.admin.customers.show commerce.customers.view
POST /api/commerce/admin/customers/:id/anonymize commerce.admin.customers.anonymize commerce.customers.anonymize

Channels and metrics

Method Path Route name Ability
GET /api/commerce/admin/channels commerce.admin.channels.index commerce.metrics.view
GET /api/commerce/admin/metrics/summary commerce.admin.metrics.summary commerce.metrics.view
GET /api/commerce/admin/metrics/revenue commerce.admin.metrics.revenue commerce.metrics.view
GET /api/commerce/admin/metrics/orders-by-status commerce.admin.metrics.ordersByStatus commerce.metrics.view
GET /api/commerce/admin/metrics/top-products commerce.admin.metrics.topProducts commerce.metrics.view
GET /api/commerce/admin/metrics/payment-mix commerce.admin.metrics.paymentMix commerce.metrics.view
GET /api/commerce/admin/metrics/checkout-funnel commerce.admin.metrics.checkoutFunnel commerce.metrics.view
GET /api/commerce/admin/metrics/low-stock commerce.admin.metrics.lowStock commerce.metrics.view

Audit

Method Path Route name Ability
GET /api/commerce/admin/audit-entries commerce.admin.audit.index commerce.audit.view

Metrics

Every metrics endpoint takes currency (required), from, and to as ISO 8601 instants, plus an optional channelId. metrics/revenue also takes interval (day, week, or month); metrics/top-products and metrics/low-stock take limit.

Amounts are stringified minor units. Revenue is derived from the payment ledger — captures minus refunds — not from order totals, so the dashboard reconciles with the PPN and ledger exports to the cent. There is no cross-currency aggregation: one currency per request, mirroring the ledger’s integrity rule.

Customizing and upgrading

The generated files are yours. Add authentication strategies, response fields, observability, or extra guards directly in the application; keep the service-layer rule intact — controllers validate, authorize, and transform, and everything else belongs in a service so both admin surfaces and your jobs share it.

Run commerce:upgrade after upgrading the package. Files you changed are never overwritten; incoming versions are staged under .commercekit/incoming for review.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close