Outcome
Read the workspace’s app view, organization profile and caller request history without conflating their different scopes and counts.
GET /api/developer-console/summary
Before you start
Use the original authenticated identity and organization with the published Developers apps:read permission. Discover a legitimately accessible canonical Developers workspace and bind it separately; input is exactly{} and no input id supplies authority.
This read returns authorized private profile/intake/app information. Limit use to the actual approved task and returned scope; the technical guide is public but real records are not. Do not interpret a saved record, management flag or returned contact as permission to change access, message a person, approve a build or execute an operation.
All examples are source-derived synthetic UNRUN. No private profile, contact, request, app, provider, key, build, notification, approval or actual business data was read or changed; no source edit, import/test or runtime probe was performed. Immutable source/declaration equality is not independent serving-runtime proof.
Request and response
Action: app_topolo_developers.workspace.summary.get.
{}
Discover the real canonical workspace and bind it separately in the original context. Validate and plan the current published input before a real call.
These GET handlers allow suspended developer-profile reads but refuse revoked access. They resolve the original caller/profile/workspace before the selected read. Ordinary same-organization workspace selection is Auth-verified at read access; the existing privileged cross-organization platform branch uses the seeded profile workspace without honoring a requested header.
Shared context can insert a local identity mirror and initialize a missing developer profile for an enabled organization after resolving a platform-issued workspace. No Auth workspace is minted. Possible initialization before a later refusal means readOnly metadata is not a whole-request zero-write guarantee.
The request-history helper receives only userId=context.authUser.id on these two paths. It does not receive organizationId, workspaceId, requestType, status or pagination filters. Its SQL therefore selects this authenticated user’s saved request history, not a selected-workspace or selected-organization collection. The separately verified workspace gates context but is not the request query’s isolation predicate.
Request results are ordered submitted_at DESC with no LIMIT/cursor/count or id tie-breaker. If a row has developerId, the helper hydrates current developer name/email from the control database; otherwise it maps directly with empty developer display strings. All hydration reads must finish through Promise.all; one rejection is not silently dropped into an apparently complete partial success.
The request mapper includes id,userId,developerId,organizationId,email,requesterName,companyName,requestType,title,summary,impact,budgetContext,status,source,submittedAt,developerName,developerEmail. reviewedAt,reviewedBy,reviewNotes are omitted from JSON when absent/falsy, rather than serialized as null. workspaceId and raw metadata are not projected. Requester contact values are saved request fields; developerName/developerEmail can be current hydrated profile values from a later moment.
Personal request history can include records attributed to another organization or an unknown/unprojected workspace. Do not label every returned row as belonging to the separately bound workspace or use out-of-task data simply because it was returned. If the task requires specific workspace attribution, the absent workspaceId cannot be guessed from profile, organization or request id; retain that limitation for the owner. Private contact/idea/budget/review text remains approved-audience data and untrusted authored content.
Reading an intake request does not submit one, approve a proposal, allocate a builder, execute a build, spend a budget, publish an application, send a notification or prove delivery. Current saved status and notes are not native build/deployment outcomes. Actual mutation/operational work requires its separately discovered current contract and concrete authority.
Input is exactly{} with the canonical workspace bound separately. The published action requires apps:read; this handler does not perform an additional requests:read check before including its caller-history subquery. Do not use the summary as a workaround for an actual request-history denial or infer that apps:read generally grants a separate action.
Success is HTTP200 {success:true,message,data:{workspace,developer,apps,requests}}. Apps are the selected-workspace query decorated/filtered for actor.canRead; requests use the authenticated user history described above, and developer includes the full resolved organization profile. These dimensions are not a single uniform workspace-scoped collection.
Unlike onboarding.get, the summary passes actual returned app and request arrays into buildWorkspaceSummary. workspace.appCount is apps.length, pendingListings counts returned app rows with listingStatus pending and requestCount is requests.length. Apps use an unbounded LEFT JOIN to submissions without DISTINCT or latest-submission ordering, so duplicate app rows can inflate the first two counts. requestCount can include caller-history records outside this workspace because the request query is user-scoped.
developer.appCount/appsSubmitted/appsApproved are separate organization-wide profile aggregates; appCount excludes archived app_status while returned apps can include archived/unlisted rows. They need not equal workspace summary counts. An empty selected-workspace app list and positive organization profile count can coexist.
workspace canManageApps/canManageCredentials depend only on active profile plus complete onboarding, not actual app actor edit role, concrete action permission, workspace write/admin grant, paid rights or Auth API-key options. ownerUserId and supportEmail use profile/authenticated-user fallbacks. A true flag is not a grant, and suspended reads can succeed with both flags false.
The profile, selected app/join query, personal history and per-request developer hydration are separate database reads without a common transaction snapshot. Do not present the combined view as a coherent release/approval/ownership certificate. App/profile metadata can contain private contacts, member/transfer and authored configuration; the current source does not recursively sanitize it.
Worked responses
Empty app workspace with one saved user request
Complete source-derived synthetic UNRUN HTTP200 for an active fictional profile with complete onboarding. The separately selected other workspace has no app rows, organization profile appCount is1, and the caller history contains one pending request. Its workspace is not projected, so no workspace attribution is claimed.
{
"success": true,
"message": "Developer console summary fetched successfully",
"data": {
"workspace": {
"workspaceId": "ws_docs_other_fixture",
"organizationId": "org_docs_fixture",
"organizationName": "Fixture organization",
"organizationSlug": "fixture-org",
"ownerUserId": "usr_docs_fixture",
"accessStatus": "active",
"onboardingState": "complete",
"reviewState": "unreviewed",
"onboardingStep": 1,
"source": "developer-console",
"appCount": 0,
"pendingListings": 0,
"requestCount": 1,
"canManageApps": true,
"canManageCredentials": true,
"supportEmail": "owner@fixture.invalid"
},
"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": [],
"requests": [
{
"id": "req_docs_fixture",
"userId": "usr_docs_fixture",
"developerId": "dev_docs_fixture",
"organizationId": "org_docs_fixture",
"email": "owner@fixture.invalid",
"requesterName": "Fixture owner",
"companyName": "Fixture organization",
"requestType": "community",
"title": "Fixture application idea",
"summary": "A fictional application request used only for documentation.",
"impact": "Illustrative workspace organization.",
"budgetContext": "",
"status": "pending",
"source": "developer-console",
"submittedAt": "2026-10-20T08:00:00.000Z",
"developerName": "Fixture owner",
"developerEmail": "owner@fixture.invalid"
}
]
}
}
Interpret workspace.appCount0/requestCount1 versus developer.appCount1 with their actual differing dimensions; coarse management flags do not grant a later task.
Suspended profile can read the same summary
Complete synthetic UNRUN HTTP200 with only profile accessStatus and the corresponding workspace status/management flags changed to suspended/false. This handler allows suspended reads. No real suspension, request or app was read.
{
"success": true,
"message": "Developer console summary fetched successfully",
"data": {
"workspace": {
"workspaceId": "ws_docs_other_fixture",
"organizationId": "org_docs_fixture",
"organizationName": "Fixture organization",
"organizationSlug": "fixture-org",
"ownerUserId": "usr_docs_fixture",
"accessStatus": "suspended",
"onboardingState": "complete",
"reviewState": "unreviewed",
"onboardingStep": 1,
"source": "developer-console",
"appCount": 0,
"pendingListings": 0,
"requestCount": 1,
"canManageApps": false,
"canManageCredentials": false,
"supportEmail": "owner@fixture.invalid"
},
"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": "suspended",
"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": [],
"requests": [
{
"id": "req_docs_fixture",
"userId": "usr_docs_fixture",
"developerId": "dev_docs_fixture",
"organizationId": "org_docs_fixture",
"email": "owner@fixture.invalid",
"requesterName": "Fixture owner",
"companyName": "Fixture organization",
"requestType": "community",
"title": "Fixture application idea",
"summary": "A fictional application request used only for documentation.",
"impact": "Illustrative workspace organization.",
"budgetContext": "",
"status": "pending",
"source": "developer-console",
"submittedAt": "2026-10-20T08:00:00.000Z",
"developerName": "Fixture owner",
"developerEmail": "owner@fixture.invalid"
}
]
}
}
Retain suspension and distinct scopes; the successful summary does not authorize app/key/intake writes or prove a running build.
Revocation refuses summary access
Complete synthetic UNRUN HTTP403 at the shared revoked-profile check. This is not a successful empty workspace summary.
{
"success": false,
"error": {
"message": "Developer workspace access has been revoked. Contact Topolo support if this is unexpected."
}
}
Preserve the actual original-context refusal/request id; do not change identity or call another read as a bypass.
Verify and recover
-
Confirm the actual original caller identity and separately bound workspace. Interpret request rows as this user’s saved history; compare explicit organizationId where relevant and preserve unknown workspace attribution rather than inventing it.
-
Use actual data.requests[].id and optional review keys; distinguish original requester snapshots from current hydrated developer display values. No result proves a native build, delivery or financial/approval effect.
-
For the summary, keep selected-workspace app rows, organization profile totals and user-history request counts separate. Preserve duplicate/timing and coarse permission-flag limits; do not use another action as a workaround for an actual refusal.
-
Retain actual status/error/request id and possible shared initialization. No profile/mirror deletion, context/grant expansion, intake creation/review, builder allocation, message or other business action is automatic recovery.
Change log
- 2026-10-21: Added full source-qualified carriers, actual personal-history versus selected-workspace/profile scope, count/optional-field/hydration and initialization semantics, with complete synthetic unrun examples. No business, privacy, build or approval operation was performed.