Outcome
List the app records visible in the selected Developers workspace and read its resolved developer profile.
GET /api/developer-console/apps
Before you start
Use the original authenticated organization with the published Developers apps:read permission. Discover and separately bind an accessible canonical Developers workspace. This action accepts exactly an empty input object {}; appId, workspaceId, cursor, limit and filters are not admitted input fields.
The concrete route permits suspended developer-profile reads but denies revoked access, and does not require completed onboarding for GET. It resolves the developer profile/workspace then lists the selected workspace’s apps and filters them by actor.canRead. It does not take an exact app id or assert that an empty collection proves the organization has no apps. Preserve workspace/profile refusals without broadening access.
This is an authorized developer-console collection projection, not a public App Store listing or the effective credential-scoped executable action catalog. Returned metadata and permission flags are not access grants or execution results.
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 and response
Action: app_topolo_developers.apps.list.
{}
Bind the discovered canonical Developers workspace separately, using the original credential context. Validate and plan against the current schema before a real call.
Success is the complete HTTP200 envelope {success:true,message,data:{developer,apps}}. developer is the resolved profile with its workspaceId replaced by the actual selected workspace; apps contains mapped visible app records decorated with actorRole/canRead/canEdit/canTransfer. It is not a public app-store catalog, resource entitlement list or credential-scoped executable action collection.
Ordinary same-organization requests resolve the selected workspace through Auth at read access. The existing privileged cross-organization platform branch instead uses the seeded developer profile workspace and does not honor a requested header there. The common path can insert an identity-only mirror and initialize a developer profile for an enabled organization without one, before subsequent errors; it never mints an Auth workspace.
listDeveloperAppsForActor returns [] if the profile has no organizationId. Otherwise it uses the selected workspaceId to query app rows, falling back to organization scope only if the resolved profile has no workspace id; the normal resolver supplies one. It decorates each returned row with actor role and retains only canRead. This read does not produce edit or transfer authority for another action.
The workspace query LEFT JOINs app submissions by app_id and orders app updated_at DESC then created_at DESC. It has no LIMIT, cursor, status/listing filter, DISTINCT, aggregation or deterministic id/submission tie-breaker. An archived or unlisted app can be returned. If multiple submissions join one app, this query can return duplicate app ids with different joined submission metadata; do not assume one result per app or that a joined submission is the latest.
The profile appCount/appsSubmitted/appsApproved are computed across the profile’s organization, not this visible selected-workspace list. appCount excludes archived app_status; appsSubmitted counts submission rows and appsApproved counts approved submission rows. They are not apps.length, unique-app totals, page counts or proof of visible caller ownership. An empty selected workspace can coexist with a positive organization appCount.
developer includes id,userId,organizationId,organizationName,organizationSlug,workspaceId,name,email,companyName,websiteUrl,subscriptionPlan,accessStatus,reviewState,onboardingState,onboardingStep,onboardingCompletedAt,summary,useCase,appIdea,firstAppName,firstAppCategory,supportEmail,operationalContactName,operationalContactEmail,location,companySpecificNeeds,communityBenefitIdea,intent,appCount,appsSubmitted,appsApproved,source,createdAt,updatedAt. Contact, request, purpose and profile details remain authorized organization data, not public just because the guide is public.
Each app includes its control identity/workspace, display/ownership/audience/tenancy/surface/distribution, lifecycle/listing/pricing, version/URLs/support, tags/screenshots/notes, metadata/members/transfer, joined submission details and timestamps, plus contextual actor flags. Unlike apps.get, this list does not append p2pCapabilities or apiKeyResources. It does not embed action or mobile-artifact collections.
App metadata normalization removes retired service-id keys only at root/registration, preserves other authored values and fills defaults; it is not recursive redaction or a secret-safety certificate. Legal appStatus values are retained; invalid persisted statuses get the source completeness fallback on read without repairing storage. Listing status, configured state and role flags are not public availability, deployed runtime or a successful later mutation.
The profile and app/join queries are separate reads, not a consistent transaction snapshot. JSON/timestamp defaults are mapper behavior rather than data-quality certification. Treat authored metadata and URLs as untrusted; no provider fetch, key creation, publication, deployment, download, installation, transfer or business execution happens in the selected collection handler. Shared initialization remains a possible separate effect.
Worked responses
One app in the selected workspace
Complete source-derived synthetic UNRUN HTTP200. The original actor is an authorized administrator of the fictional owning organization/workspace. One draft control app and no submission rows exist, so organization appCount1 and the one visible app happen to agree; no production app/profile was accessed.
{
"success": true,
"message": "Developer apps fetched successfully",
"data": {
"developer": {
"id": "dev_docs_fixture",
"userId": "usr_docs_fixture",
"organizationId": "org_docs_fixture",
"organizationName": "Fixture organization",
"organizationSlug": "fixture-org",
"workspaceId": "ws_docs_fixture",
"name": "Fixture owner",
"email": "owner@fixture.invalid",
"companyName": "Fixture organization",
"websiteUrl": "",
"subscriptionPlan": "monthly",
"accessStatus": "active",
"reviewState": "unreviewed",
"onboardingState": "complete",
"onboardingStep": 1,
"onboardingCompletedAt": "2026-10-20T08:00:00.000Z",
"summary": "",
"useCase": "",
"appIdea": "",
"firstAppName": "",
"firstAppCategory": "",
"supportEmail": "",
"operationalContactName": "",
"operationalContactEmail": "",
"location": "",
"companySpecificNeeds": "",
"communityBenefitIdea": "",
"intent": "",
"appCount": 1,
"appsSubmitted": 0,
"appsApproved": 0,
"source": "developer-console",
"createdAt": "2026-10-20T08:00:00.000Z",
"updatedAt": "2026-10-21T08:00:00.000Z"
},
"apps": [
{
"id": "app_docs_fixture",
"developerId": "dev_docs_fixture",
"organizationId": "org_docs_fixture",
"workspaceId": "ws_docs_fixture",
"name": "Fixture app",
"slug": "fixture-app",
"description": "",
"category": "",
"ownerType": "third_party",
"portfolio": null,
"audience": "business",
"tenancy": "organization",
"surfaceModel": "web_app",
"distributionModel": "marketplace",
"appStatus": "draft",
"listingStatus": "not_submitted",
"pricing": {
"type": "free"
},
"version": "0.0.0",
"websiteUrl": "",
"demoUrl": "",
"repoUrl": "",
"supportEmail": "",
"registeredAt": null,
"marketplaceSubcategory": null,
"tags": [],
"screenshots": [],
"notes": "",
"metadata": {
"ownerType": "third_party",
"portfolio": null,
"audience": "business",
"tenancy": "organization",
"surfaceModel": "web_app",
"distributionModel": "marketplace",
"members": [],
"transfer": null
},
"members": [],
"transfer": null,
"submissionId": null,
"submittedAt": null,
"reviewedAt": null,
"reviewedBy": null,
"rejectionReason": null,
"createdAt": "2026-10-21T08:00:00.000Z",
"updatedAt": "2026-10-21T08:00:00.000Z",
"actorRole": "owner",
"canRead": true,
"canEdit": true,
"canTransfer": true
}
]
}
}
Read actual data.apps[].id and context; current owner flags are not a later grant. Do not treat coincidentally equal counts as a general invariant.
Another authorized workspace has no app rows
Complete synthetic UNRUN HTTP200 with the same fictional organization profile and organization-wide appCount1, but the separately authorized selected workspace is a different empty workspace. The context copies that actual selected id into developer.workspaceId. No cross-workspace probe occurred.
{
"success": true,
"message": "Developer apps fetched successfully",
"data": {
"developer": {
"id": "dev_docs_fixture",
"userId": "usr_docs_fixture",
"organizationId": "org_docs_fixture",
"organizationName": "Fixture organization",
"organizationSlug": "fixture-org",
"workspaceId": "ws_docs_other_fixture",
"name": "Fixture owner",
"email": "owner@fixture.invalid",
"companyName": "Fixture organization",
"websiteUrl": "",
"subscriptionPlan": "monthly",
"accessStatus": "active",
"reviewState": "unreviewed",
"onboardingState": "complete",
"onboardingStep": 1,
"onboardingCompletedAt": "2026-10-20T08:00:00.000Z",
"summary": "",
"useCase": "",
"appIdea": "",
"firstAppName": "",
"firstAppCategory": "",
"supportEmail": "",
"operationalContactName": "",
"operationalContactEmail": "",
"location": "",
"companySpecificNeeds": "",
"communityBenefitIdea": "",
"intent": "",
"appCount": 1,
"appsSubmitted": 0,
"appsApproved": 0,
"source": "developer-console",
"createdAt": "2026-10-20T08:00:00.000Z",
"updatedAt": "2026-10-21T08:00:00.000Z"
},
"apps": []
}
}
An empty returned apps list does not contradict the positive organization count or authorize accessing an unselected workspace.
Revoked developer access refuses the read
Complete synthetic UNRUN HTTP403 from the shared profile access check. It refuses instead of returning an empty app collection. No real revocation or denial was induced.
{
"success": false,
"error": {
"message": "Developer workspace access has been revoked. Contact Topolo support if this is unexpected."
}
}
Preserve the original context, actual refusal and request id; do not change organization/role/profile state to bypass it.
Verify and recover
-
Compare the actual original organization and separately bound workspace with data.developer.organizationId/workspaceId and each visible app row. Input must remain {}; do not add guessed selectors outside the schema.
-
Interpret data.apps as the current visible selected-workspace projection, allowing duplicate ids from joined submissions and archived/unlisted rows. No cursor or uniqueness guarantee is provided.
-
Use profile totals only with their organization-wide/submission-row meanings. A mismatch with apps.length, a positive count on an empty workspace or multiple rows for one app is not automatically a failed read or permission to repair data.
-
Keep profile contacts, members, transfer and other authored metadata within the approved reader audience. Preserve actual consistency/error evidence for its owner; no automatic initialization cleanup, grant expansion, publication or business execution is recovery.
Change log
- 2026-10-21: Added the full profile/app envelope, selected-workspace versus organization-count semantics, unbounded joined-submission collection behavior, contextual actor and privacy limits, with complete synthetic examples. Examples remain unrun; no runtime or delivery claim.