Topolo Docs Topolo Platform
Open workspace

Read an app’s entry-experience authoring state

Inspect saved entry-experience authoring and read-time publication reconciliation without treating a displayed state or canWrite flag as permission or runtime proof.

Outcome

Read an app’s saved entry-experience draft and publication state, including whether a displayed catalog reconciliation remains unpersisted.

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

Before you start

Use the original authenticated organization and published Developers apps:read permission. Discover the accessible canonical app and Developers workspace and bind workspace separately; appId input is not a grant.

The route permits suspended profile reads but refuses revoked access and requires app.canRead after a workspace match. It does not require completed onboarding for this GET. Ordinary same-organization workspace selection is Auth-verified; the existing privileged cross-organization platform context uses its seeded profile workspace without honoring a requested header.

This reads saved entry-experience authoring, not the running login/signup experience or a published UI preview. Read actual current action/resource/actor policy for any separately authorized update, import or publication; canWrite from this response is not a grant.

Shared context can insert a local workspace identity mirror and initialize a developer profile for an enabled organization after platform workspace resolution, even before some later refusal. No canonical Auth workspace is minted.

Examples are complete source-derived synthetic UNRUN carriers with fictional ids. No actual app/profile/private authoring, Auth, catalog publication, signup/login, provider, key or business operation was read or changed; no source edit, import/test or runtime probe was performed. Source/declaration equality is not serving-runtime proof.

Request and behavior

Action: app_topolo_developers.apps.experience.get.

{"appId":"app_docs_fixture"}

Discover the real app and canonical workspace, bind the workspace separately, then validate/plan against the current schema before a real read.

Success is HTTP200 {success:true,message,data:{appId,kind:"entryExperience",state,canWrite}}. Input requires appId. This GET does not read the commercial broker or return an executable commercial manifest; pricing is a separate authoring kind/action.

The store reads metadata.appAuthoring.entryExperience. If absent it returns exactly revision0,draft:null,published:null,publication:null. If present it checks only that revision is a nonnegative safe integer and otherwise returns the stored state; it does not validate every saved draft, publication, migration archive or experience schema on this read. Invalid revision throws and is surfaced by the console as rawHTTP500 error.message, not silently replaced with empty authoring.

The state can include draft, a published snapshot {revision,draft,publishedAt}, publication attempt {revision,attemptId, optional executionId/draftDigest/publishedAt,status,outcomes} and preserved extra metadata such as migrationArchive. Read saved revision and full outcomes as state, not proof that each external effect or running UI matches them. No draft validation, native browser journey or full source-to-runtime adoption is established by this handler.

canWrite is initially app.canEdit and not context.isReadOnly, where a suspended developer profile is read-only. For this entryExperience GET it does not independently evaluate the selected workspace’s write/admin grant, future action permission or future source/concurrency state. Actual later writes recheck their own current context and schema; neither a true flag nor an absent draft authorizes one.

For a stored publishing/failed attempt whose draft exists and publication.revision equals state.revision, the GET tries to read the durable app catalog receipt under the retained app/kind/attempt identity. If the receipt’s operation draftDigest matches the current draft digest, it returns a virtual published snapshot and publication.status published with catalog success/receipt plus control.status reconciliation_required. It does not persist that reconciled authoring state. A published-looking returned view can therefore explicitly require control reconciliation.

If that catalog receipt read or digest calculation throws, the GET makes canWrite false and returns a view whose publication.outcomes.catalogReadback.status is unknown. It does not throw this caught lookup failure as an empty/successful no-publication result, discard the original attempt, authorize resume or declare the catalog rolled back.

The receipt path’s read-time matching and virtual view are not a universal complete publication/financial/native receipt. Preserve original attempt/revision/digest and actual current outcomes, then use a separately discovered current recovery/publication contract. Do not reset a failed/publishing attempt or repeat writes just because the read changed the displayed status.

The selected GET does not save a draft, accept a legacy import, publish a manifest, create prices, change Auth experience or run signup/login. Shared context initialization is its separate possible write. A local authoring revision or response status is not proof of running production experience.

Worked responses

No saved entry-experience authoring

Complete source-derived synthetic UNRUN HTTP200. The fictional app has no entryExperience authoring property, the current actor can edit it and the profile is not suspended. No catalog receipt lookup is required in this absent-state branch; no actual app or login state was accessed.

{
  "success": true,
  "message": "OK",
  "data": {
    "appId": "app_docs_fixture",
    "kind": "entryExperience",
    "state": {
      "revision": 0,
      "draft": null,
      "published": null,
      "publication": null
    },
    "canWrite": true
  }
}

Retain the default empty authoring state. canWrite:true is a coarse source projection, not current workspace/action authority or permission to create/publish a draft.

A viewer reads the same absent authoring state

Complete synthetic UNRUN HTTP200 for an authorized app viewer with app.canEdit false and the same empty saved state. This case is not the separate receipt-read-failure path; false has an explicitly stated cause here.

{
  "success": true,
  "message": "OK",
  "data": {
    "appId": "app_docs_fixture",
    "kind": "entryExperience",
    "state": {
      "revision": 0,
      "draft": null,
      "published": null,
      "publication": null
    },
    "canWrite": false
  }
}

Preserve the actual read-only actor decision; do not broaden scope or treat the absent state as an invitation to import or publish.

App absent or inaccessible

Complete synthetic UNRUN HTTP404 for missing app or selected-workspace/actor mismatch after shared context. It does not imply global nonexistence or prove no initialization happened.

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

Retain actual original context/refusal/request id; no alternate organization, guessed id or state reset as recovery.

Verify and recover

  1. Compare actual data.appId/kind and original app/workspace identity, then retain the complete saved revision, draft, published snapshot and publication attempt/outcomes. Do not guess omitted fields or treat returned canWrite as a grant.

  2. Distinguish absent authoring from invalid stored revision, a saved draft, an unresolved attempt and a virtual receipt-reconciled view. In particular control.reconciliation_required is not a durable control write, while catalogReadback.unknown is not a rollback or replay clearance.

  3. For an intended future mutation, discover/validate/plan its actual current schema with the exact retained revision and original context, then obtain concrete authority. A source-derived example or displayed published status is not permission to retry an unknown effect.

  4. Preserve actual status/error/request id and owner custody. No authoring reset, mirror/profile deletion, import, Auth/price/catalog mutation or login/signup run is an automatic inverse or read-verification step.

Change log

  • 2026-10-21: Added the full entry-experience read carrier, default-state and coarse-canWrite semantics, read-only virtual receipt reconciliation and unknown-readback limits, with complete synthetic unrun examples. No authoring/publication/login operation was performed.