Topolo Docs Topolo Platform
Open workspace

Update a P2P capability

Amend one existing app-owned capability. Treat the change as potentially externally visible: the retained route may synchronize it automatically after saving.

Update a P2P capability

Amend one existing app-owned capability. Treat the change as potentially externally visible: the retained route may synchronize it automatically after saving.

Source-reference guide. Behavior was reviewed at retained revision 7b89bd3b39092cdc29f87540847bf99a243f0a40 and the pinned c6e543 source objects; separately recorded production descriptor reads matched the guide schemas. Public developer/publisher audience. No request below was executed. Serving runtime and P2P general availability are not established.

Before you start

PUBLIC developer/publisher audience with selected-app write access; public documentation does not grant action access. Pinned source-owner review is recorded; serving runtime currentness remains UNKNOWN.

The published credential requirement is app_topolo_developers.apps:write, authorizationMode static, readOnly false, destructive false, requiresConfirmation true, agentAccess confirm. The app access check is additional runtime policy.

The retained routes may call workspace-context helpers. Their complete identity and persistence effects are not qualified by this limited packet. Do not assert read-only prerequisite resolution.

All examples are fictional and UNRUN. Discover the real app and capability from current published reads and reconcile original effects before confirmation. Do not use fixture IDs in production.

Request

PUT /api/developer-console/apps/{appId}/p2p-capabilities/{capabilityId}

Permission: app_topolo_developers.apps:write. Confirmation required. Validate against the current published action schema before preparing any actual request.

Inputs and behavior

FieldMeaning in retained source
appIdRequired selected-app ID; source checks app access.
capabilityIdRequired for update; create generates a new ID.
name / description / categoryTrimmed; lengths 2–120 / 10–2000 / 2–80. Create needs valid values despite loose schema.
inputSchema / outputSchemaObject-shaped definitions; non-object forms fall back to existing/default objects.
pricingModel / pricingAmountMinor / currencyfree, fixed, metered, quote or invoice; amount integer ≥0; currency uppercase, first three characters then validated.
supportedContextshuman and/or agent; nonempty validated list.
trustLevelstandard, verified, trusted or restricted.
statusdraft, approved, suspended or archived.

The registered schema permits many loosely typed shared fields. This table describes the retained capability handler only; passing validation does not make irrelevant fields meaningful. publicDirectoryVisible and requiresHumanApproval are absent from this closed action schema even though the raw handler recognizes them.

Update requires completed onboarding, with an explicit403 retained branch rather than declared409; it requires selected-app write access and returns404 if the exact capability lookup is absent after that access check. App-not-found and access-helper response statuses remain UNKNOWN in this source boundary.

Fields omitted or null generally use existing values through nullish-coalescing. name/description/category are trimmed and length checked. pricing amount and currency additionally normalize empty strings to null. No blanket JSON merge or universal null-clearing contract is established.

JSON schemas must normalize to non-array objects; invalid forms fall back to existing schemas. String lists accept arrays, JSON-looking strings or comma-delimited strings and lower-case entries. Invalid boolean strings fall back to existing/default values. These permissive raw-route conversions do not expand the published action schema.

Upsert preserves capability identity and creation time, replaces the declared fields and source revision, clears last_sync_error and updates updated_at. last_synced_at can remain from an earlier version. An old successful timestamp is not proof the amended revision reached P2P.

The route can automatically synchronize an already eligible capability and then save sync state. This contradicts the descriptor statement that update does not re-sync and its suggested rollback isolation. The exact current serving behavior remains held; assume possible external effects until reconciled.

The declared schema omits publicDirectoryVisible and requiresHumanApproval even though the retained route accepts them; do not add undeclared keys to executable examples.

The source returns200 with inner {capability,sync}, after any attempted sync. Failure after save is not evidence that the local edit was rolled back. An identical retry still rewrites revision/timestamps and can repeat sync.

The retained implementation gates synchronization on listingStatus approved, appStatus not archived, distributionModel marketplace, publicCatalogVisible, a nonblank app ID, capability status approved, and publicDirectoryVisible. The condition is not an exact appStatus active check.

Synchronization delegates from Developers to P2P with service scope capabilities.publish. Service identity is server-owned; it does not make this human-facing guide internal-only.

The service secret and client ID must be configured. Missing configuration can refuse with503. The source copies a downstream non-OK status through its ApiError handling; exact outer wire and current serving behavior are UNKNOWN.

P2P publication and local lastSyncedAt/lastSyncError updates are separate operations. A failed or lost response may follow a successful upstream publication. A local timestamp or null error alone does not prove the external directory outcome.

Worked examples

Amend the description

SYNTHETIC / UNRUN.

{
  "appId": "app_fixture_publisher",
  "capabilityId": "pcap_000000000001",
  "description": "Revised fictional stock lookup returning available units."
}

Assuming existing valid fields and access, source saves then returns200. If eligibility already holds, it may sync automatically. A complete synthetic formatted output appears below; no runtime result is claimed.

Source validation refusal

SYNTHETIC / UNRUN. Existing fixture capability has otherwise valid fields. Only the amended one-character name fails. The source throws ApiError422 with the name flag and no code.

Request:

{
  "appId": "app_fixture_publisher",
  "capabilityId": "pcap_000000000001",
  "name": "x"
}

Source-shaped raw HTTP 422 body (fixture message text, not a capture):

{
  "success": false,
  "error": {
    "message": "FIXTURE localized validation message; exact literal unqualified",
    "details": {
      "name": true
    }
  }
}

This is the qualified serializer shape under the stated fixture assumptions. CLI/MCP wrapping and the current serving runtime were not exercised.

Missing capability

SYNTHETIC / UNRUN. The app access check passes but the exact app-scoped capability lookup is absent. The source returns404 without code/details; this says nothing about app-not-found helper responses.

Request:

{
  "appId": "app_fixture_publisher",
  "capabilityId": "pcap_fixture_missing",
  "description": "A fictional amendment to a nonexistent capability."
}

Source-shaped raw HTTP 404 body (fixture message text, not a capture):

{
  "success": false,
  "error": {
    "message": "FIXTURE localized capability-not-found message; exact literal unqualified"
  }
}

This is the qualified serializer shape under the stated fixture assumptions. CLI/MCP wrapping and the current serving runtime were not exercised.

Complete illustrative success response

SYNTHETIC / UNRUN. Message literal, identifiers and timestamps are fictional. The sync example includes a simulated upstream provider carrier; this is not a P2P schema or production receipt. Create/update fixture is not sync-eligible.

{
  "success": true,
  "message": "FIXTURE MESSAGE; actual localized literal not qualified",
  "data": {
    "capability": {
      "capabilityId": "pcap_000000000001",
      "appId": "app_fixture_publisher",
      "developerId": "dev_fixture_publisher",
      "publisherOrgId": "org_fixture_publisher",
      "name": "Fixture stock lookup",
      "description": "Revised fictional stock lookup returning available units.",
      "category": "inventory",
      "inputSchema": {
        "type": "object",
        "properties": {
          "sku": {
            "type": "string"
          }
        },
        "required": [
          "sku"
        ],
        "additionalProperties": false
      },
      "outputSchema": {
        "type": "object",
        "properties": {
          "available": {
            "type": "integer"
          }
        },
        "required": [
          "available"
        ],
        "additionalProperties": false
      },
      "pricingModel": "free",
      "pricingAmountMinor": null,
      "currency": null,
      "requiresHumanApproval": false,
      "supportedContexts": [
        "human",
        "agent"
      ],
      "trustLevel": "standard",
      "publicDirectoryVisible": false,
      "status": "draft",
      "sourceRevision": "rev_pcap_000000000001_1792738800",
      "lastSyncedAt": null,
      "lastSyncError": null,
      "createdAt": "2026-10-23T07:00:00.000Z",
      "updatedAt": "2026-10-23T07:00:00.000Z"
    },
    "sync": null
  }
}

Verify and recover

Read the current published apps.p2p_capabilities.list contract and use the real selected app/capability identity. Compare the actual saved fields/source revision and last-sync status. A local list read alone does not prove external visibility.

When sync could have occurred, separately discover the authorized P2P read contract and verify the exact app/publisher/capability/revision there. No P2P endpoint or credential is inferred from this source-only packet.

After a failure/lost response, reconcile local record and external state before any retry. A create may already exist; a sync may already have published. Preserve exact request, IDs, original values and real response/request identifiers.

No automatic rollback is established across declaration persistence, P2P publication and sync-state recording. Reconcile both systems under their current owners; any approved corrective update may itself auto-sync. Do not follow generic retry-once or unsynced-is-private claims without current source-owner reconciliation.

All examples now include source-shaped response illustrations under explicit fixture assumptions. None is an actual capture. Message literals are fictional; one commerce-state detail is an explicit template value; the last sync failure chooses a specific generic Error branch. SDK/MCP wrappers and current serving behavior remain unqualified.

Contract provenance

Contract provenance: the retained raw application manifest declares permission apps:write and a generic object output schema. The separately captured credential-discovered descriptor at sourceRevision 7b89bd3b39092cdc29f87540847bf99a243f0a40 supplies the namespaced requiredPermission, static authorizationMode and a richer outputSchema marked derived-action-metadata / structural. This guide preserves that descriptor schema; the separate approved-profile production reads matched it at their recorded check. This is metadata association, not byte-equivalence to the raw manifest, a serving-Worker revision check, execution, P2P general availability, or a captured response. Source currentness beyond the pinned objects and serving behavior remain UNKNOWN.

Evidence and remaining gaps

Source: Topolo-io/TopoloDevelopers@7b89bd3b39092cdc29f87540847bf99a243f0a40:apps/api/functions/api/developer-console/app-integration-routes.ts and p2p-capabilities.ts. Source inspection is not a runtime test.

The formatter, http helpers and constants are qualified from retained immutable bytes. Formatter returns null on absent DB read; timestamps normalize to ISO strings or null, with created/updated fallbacks to current time. Synthetic timestamps are fixture values, not observed runtime.

The shared developer-console catch and errorResponse helper are source-qualified at pinned c6e543, unchanged from7b89. For the reviewed calls, ApiError status/message/details pass through and no code is supplied; plain Error maps to500/message only. Exact localized literals, SDK/gateway normalization, serving behavior and raw response captures remain unqualified.

Retained enums: pricing free/fixed/metered/quote/invoice; status draft/approved/suspended/archived; contexts human/agent; trust standard/verified/trusted/restricted. Pricing amount must be an integer>=0 when present; currency normalizes uppercase and truncates to three characters before the three-letter check.

Source-owner review and separate production descriptor/schema association are recorded. These establish source-reference documentation, not raw-manifest/derived-schema byte equivalence, serving runtime, P2P general availability or executed provider effects.

Changelog

2026-10-23 — Prepared a provisional guide from the retained source snapshot. Disclosed automatic-sync and error-status discrepancies. No product fix, live test or production publication is claimed.

2026-10-23 — Offline v3 copy correction: distinguish the capability-only 404 from unqualified app-access behavior where applicable, describe currency truncation as characters, and separate raw-manifest declarations from captured derived descriptor metadata. No schema/example rewrite, currentness, runtime result or publication is claimed.

2026-10-23 — Added seven source-shaped error response illustrations from the separately qualified shared serializer. Preserved all ten request inputs, the three normal success outputs and both schemas per action. No raw response capture, business execution, current serving claim or publication is asserted. 2026-10-23 — Reconciled the completed pinned source review and independent content acceptance; retained explicit runtime and execution limits. This changelog records documentation review, not a product release or executed P2P operation.