Topolo Docs Topolo Platform
Open workspace

Publish an app’s API key resource declarations

Synchronize saved nonarchived resource declarations to Auth and distinguish upstream acceptance, local metadata and effective key eligibility.

Outcome

Synchronize saved nonarchived resource declarations to Auth and distinguish upstream acceptance, local metadata and effective key eligibility.

POST /api/developer-console/apps/{appId}/api-key-resources/publish

Before you start

Use the original organization with the published Developers apps:write grant and a separately discovered canonical Developers workspace at read_write access. Discover the exact accessible app record and current action id through supported reads; appId/actionId input is not a new permission.

The route requires an active, non-revoked/non-suspended developer context with onboardingState complete and app.canEdit. Owner/editor app access is required after the workspace match; a viewer or permission string alone does not authorize the mutation. The ordinary same-organization branch verifies the workspace with Auth. Its existing privileged cross-organization branch uses the seeded developer profile workspace, without honoring a requested workspace header; it does not bypass the app-workspace comparison.

Shared context can create a local identity mirror and initial developer profile for an enabled organization after platform workspace resolution, even before a later onboarding/app refusal. No Auth workspace is minted. Keep those possible initialization effects separate from the selected mutation.

Before a real mutation, read the entire existing resource-declaration collection and publication/error metadata, review the complete intended replacement or publication and its Auth effects, validate/plan exact input plus resource context, and obtain authority for that concrete change. CLI --confirm/MCP confirm:true is not human approval.

Every example below is source-derived synthetic UNRUN. The fictional host, ids and timestamps grant no authority. No app action/declaration, manifest, Auth projection, provider, key or notification was created, updated, published or executed for these docs. The source pin is not proof of serving-runtime adoption.

Request and effects

Action: app_topolo_developers.apps.api_key_resources.publish. This is a confirmed mutation. The examples are fictional; discover the actual app/workspace and validate/plan the full intended change before its concrete authorization.

These routes require Developers apps:write, selected workspace read_write, completed onboarding, a non-suspended/non-revoked profile and app.canEdit. They describe resource-type/provider configuration; they do not mint an API key or grant the caller a real workspace/resource. Key eligibility remains the supported caller-specific Auth api_keys.options contract.

The complete success envelope is {success:true,message,data:{apiKeyResources:[...]}} for replacement, and adds data.sync for publication. Each record is the saved19-field resource declaration projection, not an actual bindable resource instance. resourceId identifies the declaration and must not automatically become a key’s resource binding id.

No expectedRevision/conditional digest or explicit idempotency key is present in these inputs. Read exact current state before a retry after interruption; no cross-database/Auth transaction or automatic inverse is promised. Preserve original ownership/scope and any known/unknown downstream effects.

Publish first lists all saved app declarations, selects every nonarchived row and submits that projection to Auth under the original caller credentials. It does not accept a resourceId subset. Draft rows are included; archived rows are omitted from the submitted set. An empty selection still makes the Auth sync request with resources:[], whose actual server-side outcome must be verified separately.

The sync helper forwards selected original authentication/request headers and the Developers app identity to Auth. Public usage is this supported Developers action, not a recipe to call an internal Auth route. Auth performs the final policy decision; organization ownership or saved declaration metadata does not guarantee acceptance.

For provider url and endpoint fields that are environment maps, the helper selects production only when the configured environment string is production; otherwise it selects staging. It then falls back to production/staging values if needed. A selected URL is configuration, not a provider call, safe origin certificate, fetched resource list or approval to enumerate private resources.

The helper requires upstream HTTP success and truthy payload.success, returning payload.data or null as data.sync. It does not define a stable per-resource proof schema or fetch provider resources. A null sync value can occur on accepted upstream data:null, but is not independently verified registration, scope or key-eligibility evidence.

After accepted Auth sync, the app updates each selected local row sequentially: status published, lastPublishedAt/updatedAt now and error null, then reads all local rows including archived ones. sourceRevision remains the declaration’s saved marker. Auth sync and local metadata are not one transaction; concurrent replacement or a local failure can make the returned/saved state differ from the submitted set.

On failure the route attempts to set each selected row’s lastPublishedAt null and a bounded error string, leaving status unchanged; errors in this metadata compensation are swallowed. It does not revert Auth or restore old declarations. An already-applied upstream change can coexist with failed local recording, so do not retry blindly or call null timestamps a rollback.

The sync helper uses the upstream response status on an ApiError even when that status is200 but payload.success is false/missing. The console can therefore emit rawHTTP200 with success:false/error, rather than a success envelope. Inspect both actual status and body; do not count an HTTP200 alone as completed publication or invent a stable raw error.code.

Onboarding incomplete is rawHTTP403/error.message on this concrete route. Other upstream Auth errors can carry status/message and error.details.auth. Preserve sanitized request/error evidence and current original state; do not expose forwarded credentials or broaden context to make a refused sync succeed.

Worked requests and responses

Auth accepts a projection with no data payload

Complete source-derived synthetic UNRUN HTTP200 assuming the saved row shown in the replacement example, accepted upstreamHTTP200 {success:true,data:null}, and successful local metadata updates at08:01Z. The source forwards null sync; this does not invent a stable Auth receipt.

Input:

{
  "appId": "app_docs_fixture"
}

Response:

{
  "success": true,
  "message": "API key resources published successfully",
  "data": {
    "apiKeyResources": [
      {
        "resourceId": "app_docs_fixture.workspace",
        "appId": "app_docs_fixture",
        "developerId": "dev_docs_fixture",
        "organizationId": "org_docs_fixture",
        "resourceType": "workspace",
        "resourcePattern": "workspace:*",
        "resourceConcept": "workspace",
        "canonicalResourceType": "workspace",
        "resourceLabel": "Workspace",
        "resourceEmptyLabel": "No workspaces",
        "defaultEntitlement": "enterprise",
        "exceptionPolicy": "app_or_organization",
        "syncProvider": {
          "kind": "http",
          "url": "https://fixture.invalid/api/workspaces"
        },
        "status": "published",
        "sourceRevision": "rev_app_docs_fixture.workspace_1792569600",
        "lastPublishedAt": "2026-10-21T08:01:00.000Z",
        "lastPublishError": null,
        "createdAt": "2026-10-21T08:00:00.000Z",
        "updatedAt": "2026-10-21T08:01:00.000Z"
      }
    ],
    "sync": null
  }
}

Verify actual current registration/eligibility through supported reads; saved published metadata and null sync do not grant a key/resource.

An empty saved set is still submitted

Complete synthetic UNRUN HTTP200 with no saved rows, accepted upstreamHTTP200 {success:true,data:null} and the subsequent successful empty list. This action still submitted resources:[]; no real sync occurred and its server-side clearing semantics are not claimed from this carrier.

Input:

{
  "appId": "app_docs_fixture"
}

Response:

{
  "success": true,
  "message": "API key resources published successfully",
  "data": {
    "apiKeyResources": [],
    "sync": null
  }
}

Do not infer that every old Auth registration was cleared merely from the empty local list.

Upstream unavailable with no JSON body

Complete synthetic UNRUN rawHTTP503 from an upstream503 whose body cannot be parsed as JSON. Source uses its fallback message/status/details and attempts local error recording; upstream effects and that compensation are not proven absent. No actual outage or retry was induced.

Input:

{
  "appId": "app_docs_fixture"
}

Response:

{
  "success": false,
  "error": {
    "message": "Could not publish API key resources to Auth.",
    "details": {
      "auth": null
    }
  }
}

Retain actual status/request id, original set and partial/unknown effects. Do not blindly resubmit or call the error-metadata attempt a rollback.

Verify and recover

  1. Confirm actual body.success, saved declaration state and sanitized sync result; HTTP200, null sync or a published label alone is not complete registration proof. Compare the actual submitted/current set in its original app/workspace.

  2. Use supported current Auth application/resource/key-options reads appropriate to the intended configuration to verify actual caller-specific eligibility. Do not infer working providers, grants or key issuance from local timestamps, opaque sync data or defaultEntitlement.

  3. After uncertainty/failure preserve known/unknown Auth and local metadata outcomes for the legitimate owner. Do not clear/repopulate declarations, change permissions or repeat sync until current state and concrete recovery authority are established.

There is no automatic inverse across local deletion/insertion and Auth synchronization. Any intentional corrective replacement or publication needs a fresh complete review and concrete authority; retain history/effects rather than masking uncertainty.

Change log

  • 2026-10-21: Added current source-qualified complete-set/resource-type validation, saved-versus-Auth distinctions, actual success/error carriers and nontransactional recovery, with synthetic unrun examples. No key, provider or production business operation was performed.