Topolo Docs Topolo Platform
Open workspace

List an app’s action declarations

Inspect saved app actions, their publication state and bounded cursor pages without executing or publishing them.

Outcome

Inspect saved app actions, their publication state and bounded cursor pages without executing or publishing them.

GET /api/developer-console/apps/{appId}/actions

Before you start

Use the original authenticated organization with the published Developers apps:read permission. Discover an accessible canonical Developers workspace and bind it separately as resource context. Input appId is an accessible developer app record identifier; it is not a workspace id or an access grant.

The concrete route allows suspended developer profiles to read but denies revoked access. It resolves a developer profile and verified workspace, then requires app read access in that workspace. Platform authority, organization administrator status, app membership and developer ownership are distinct source branches; an app outside the selected workspace or invisible to the actor returns404. Do not broaden the organization or grant after a refusal.

These are declaration reads for an app you may administer, not the credential-scoped executable action catalog or the list of actual resources an API key may bind. Saved metadata, published timestamps and displayed capability flags are not effective caller authorization.

The shared request path can insert a local identity-only workspace mirror and, for an enabled organization without a developer profile, create the initial developer profile after resolving a platform-issued workspace. This can precede app access refusal. No canonical Auth workspace is minted, and readOnly metadata is not a promise of zero initialization writes.

All worked responses are source-derived synthetic UNRUN examples. No real developer app, declaration, provider, key, person, private record, publication or other business action was read or changed. The immutable source matches the retained declaration revision; serving-runtime adoption is not independently proven.

Request

Action: app_topolo_developers.apps.actions.list.

{
  "appId": "app_docs_fixture",
  "limit": 20
}

The identifiers above are fictional. Discover the real app and separately bind the canonical Developers workspace. Validate and plan with the current published contract before a real call. This documentation task has not executed it.

Response and behavior

HTTP200 uses the full envelope {success:true,message,data:{...}}. Read records inside data rather than assuming the top-level response is the collection. Exact JSON schemas below are copied from the current declaration, whose structural permissiveness does not substitute for the source-qualified field meanings.

The shared context first authenticates and finds or initializes a developer profile; a revoked profile is refused and a suspended profile is allowed for these GET handlers. For an ordinary same-organization request, Auth verifies the requested workspace at minimum read level. The existing privileged cross-organization platform context instead uses the seeded developer profile’s workspace and does not honor a requested workspace header on that branch. Both branches still reach the app-workspace match and actor.canRead checks. The app-specific handlers do not require completed onboarding for reads. Missing and inaccessible app records are deliberately indistinguishable404 responses.

App access is evaluated after workspace selection. In the source actor calculation, recognized platform access can produce owner role, an organization mismatch otherwise denies, an administrator of the owning organization is an owner, explicit matching user id/email membership can supply a role, and matching developer ownership is the fallback. A permission string alone does not establish all these context conditions.

No action is executed, no resource provider is fetched, no key is minted or bound, no declaration is published and no Auth projection is synchronized by the selected list handler. Shared mirror/profile initialization remains a separate possible effect; do not delete those records as an automatic rollback.

The result is data.actions plus data.page:{nextCursor,hasMore} and data.count. count counts every stored definition in the app partition, not the page length or the number of currently callable actions. The list includes saved draft, published and archived definitions because this query has no status filter.

Input requires appId; optional cursor is at most256 characters and limit is an integer1–100, default50. The published generic input contains many empty-schema optional keys; they are not meaningful options for this handler. Send only appId, cursor and limit. The route additionally rejects invalid page values with HTTP400 and error.details.page:true.

Pagination orders by action_id ascending, selects action_id > cursor and fetches limit+1 for lookahead. Carry the returned data.page.nextCursor unchanged only while hasMore is true, using the same app and workspace. No total snapshot or cross-page transaction is promised; concurrent changes can alter count and visibility between calls.

The partition reads record_json and silently omits a row when JSON parsing fails. count still counts stored rows and hasMore/nextCursor use the selected storage rows before parsing. Therefore actions.length may be below limit even when there are more pages; use hasMore rather than a short page to decide continuation, and never claim count equals the number of successfully decoded declarations.

Every mapped action has actionId,target,apiBaseUrl,appId,developerId,organizationId,name,title,description,method,path,permissionName,principalAccess,authorizationMode,allowedServices,inputSchema,outputSchema,agentContract,readOnly,destructive,requiresConfirmation,agentAccess,status,sourceRevision,lastPublishedAt,lastPublishError,createdAt,updatedAt. accessPolicy is present only when its stored field is truthy. Schemas/contracts/services are JSON-parsed with fallback; timestamps can use a mapping-time fallback, so display values do not establish source validity or publication. The mapper does not include workspaceId.

The stored schemas and agent contract are returned without checking that an example will validate or that a target is safe to call. Do not execute an endpoint, follow a provider URL, treat draft as callable or override effective permissions from this response. Discover the concrete callable action through the credential-scoped catalog and validate its current contract for any separate approved task.

Worked responses

One saved draft action, final page

Complete synthetic UNRUN HTTP200 with one valid decoded stored row and no further storage rows. Its draft status and null publication timestamp are intentional; this response does not make it callable. Fictional URL is inert.

{
  "success": true,
  "message": "App actions fetched successfully",
  "data": {
    "actions": [
      {
        "actionId": "app_docs_fixture.items.list",
        "target": "api",
        "apiBaseUrl": "https://fixture.invalid",
        "appId": "app_docs_fixture",
        "developerId": "dev_docs_fixture",
        "organizationId": "org_docs_fixture",
        "name": "items.list",
        "title": "List fixture items",
        "description": "Return fixture items for illustration.",
        "method": "GET",
        "path": "/api/items",
        "permissionName": "items:read",
        "principalAccess": null,
        "authorizationMode": "static",
        "allowedServices": [],
        "inputSchema": {
          "type": "object",
          "properties": {},
          "additionalProperties": false
        },
        "outputSchema": {
          "type": "object",
          "properties": {
            "items": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "items"
          ],
          "additionalProperties": false
        },
        "agentContract": {},
        "readOnly": true,
        "destructive": false,
        "requiresConfirmation": false,
        "agentAccess": null,
        "status": "draft",
        "sourceRevision": "rev_docs_fixture_unrun",
        "lastPublishedAt": null,
        "lastPublishError": null,
        "createdAt": "2026-10-21T08:00:00.000Z",
        "updatedAt": "2026-10-21T08:00:00.000Z"
      }
    ],
    "page": {
      "nextCursor": null,
      "hasMore": false
    },
    "count": 1
  }
}

Interpret data.actions[].actionId only within the verified original app/workspace; no subsequent mutation or execution is authorized.

A successful empty result

Complete source-derived synthetic UNRUN HTTP200 after all authentication/workspace/app checks succeed and the successful query finds no stored declarations. This is not a dependency failure or an inaccessible app.

{
  "success": true,
  "message": "App actions fetched successfully",
  "data": {
    "actions": [],
    "page": {
      "nextCursor": null,
      "hasMore": false
    },
    "count": 0
  }
}

Retain the empty result in its real context; do not create or publish a declaration to make it nonempty.

App unavailable in this context

Complete source-derived synthetic UNRUN HTTP404 for a missing app or a workspace/actor visibility mismatch after shared context resolution. This is deliberately not proof of global nonexistence. Initialization may already have occurred.

{
  "success": false,
  "error": {
    "message": "Developer app not found"
  }
}

Preserve the original identity, workspace and request id. Do not try another organization or guess identifiers to bypass the access decision.

Verify and recover

  1. Use data.actions[].actionId and inspect status, lastPublishedAt and lastPublishError without treating them as a current permission or execution result. Compare the selected app/organization against the actual context. At this source revision the action partition uses the developer app record’s validated canonical appId; use its discovered identifier unchanged.

  2. Use data.page.hasMore and return the actual nextCursor unchanged in the next same-context request. count is the stored definition count, not an output length or complete callable catalog. A short/empty mapped page does not override hasMore.

  3. If pagination or parsed records are inconsistent, preserve exact page/cursor/source/status/request-id evidence for the owner. Do not edit, publish, replay a write or repair malformed rows through this read task.

The list handler has no declaration mutation to undo. Its shared initialization effects are separate; automatic deletion of a developer profile or mirror is not a recovery step.

Change log

  • 2026-10-21: Added source-qualified wrapped responses, original app/workspace access, declaration-versus-effective-authorization semantics, precise collection behavior and complete synthetic examples. Examples remain unrun.