@objectstack/spec 17.2.0, ActionSchema.method doc comment:
HTTP method to use when type: 'api'. Defaults to POST. Use PATCH to call data-API update endpoints (e.g. /api/v1/sys_api_key/{id} with bodyExtra: { revoked: true }).
The path is missing the /data segment the router actually mounts.
Measured
@objectstack/rest 17.2.0 composes the CRUD routes as getApiBasePath() + crud.dataPrefix + /:object/:id:
getApiBasePath(){returnapi.apiPath??`${api.basePath}/${api.version}`;}// '/api' + '/v1'
dataPrefix: crud.dataPrefix??"/data"registerCrudEndpoints(basePath){constdataPath=`${basePath}${crud.dataPrefix}`;
...
this.routeManager.register({method: "PATCH",path: `${dataPath}/:object/:id`, ... })}So the update endpoint is PATCH /api/v1/data/<object>/<id>. Nothing overrides dataPrefix on the standalone/runtime path, and sys_api_key has no bespoke route of its own — plugin-auth mounts only /api/v1/auth/* and /api/v1/mcp, and sys_api_key is an ordinary platform-objects object served by the generic data API.
Why it is worth fixing rather than leaving as prose
This is the one worked example an author copies when reaching for the only declarative-looking way to set a field from an action (see #14092). Nothing catches the mistake: objectstack validate does not check target, type: 'api' has no author-time route validation, and the action renders, is clickable, and 404s at the click — the same silent shape as an unregistered handler, arriving through a doc example instead of a missing registration.
Suggested
`/api/v1/data/sys_api_key/{id}` with `bodyExtra: { revoked: true }`
and, since target is interpolated by the renderer with ${param.X} / ${ctx.X} (this same schema's target doc) rather than with {id}, spelling the id as ${ctx.recordId} in the example would stop the two conventions in one schema from reading as interchangeable. (newTabUrl is the one key that really does take a bare {recordId} placeholder.)
Found while building objectstack-ai/duly#4.
@objectstack/spec17.2.0,ActionSchema.methoddoc comment:The path is missing the
/datasegment the router actually mounts.Measured
@objectstack/rest17.2.0 composes the CRUD routes asgetApiBasePath()+crud.dataPrefix+/:object/:id:So the update endpoint is
PATCH /api/v1/data/<object>/<id>. Nothing overridesdataPrefixon the standalone/runtime path, andsys_api_keyhas no bespoke route of its own —plugin-authmounts only/api/v1/auth/*and/api/v1/mcp, andsys_api_keyis an ordinaryplatform-objectsobject served by the generic data API.Why it is worth fixing rather than leaving as prose
This is the one worked example an author copies when reaching for the only declarative-looking way to set a field from an action (see #14092). Nothing catches the mistake:
objectstack validatedoes not checktarget,type: 'api'has no author-time route validation, and the action renders, is clickable, and 404s at the click — the same silent shape as an unregistered handler, arriving through a doc example instead of a missing registration.Suggested
and, since
targetis interpolated by the renderer with${param.X}/${ctx.X}(this same schema'stargetdoc) rather than with{id}, spelling the id as${ctx.recordId}in the example would stop the two conventions in one schema from reading as interchangeable. (newTabUrlis the one key that really does take a bare{recordId}placeholder.)Found while building objectstack-ai/duly#4.