Sync a P2P capability
Request synchronization of one eligible capability into P2P. Review the external publication effect and reconcile any uncertain earlier attempt before retrying.
Source-reference guide. Behavior was reviewed at retained revision
7b89bd3b39092cdc29f87540847bf99a243f0a40and the pinnedc6e543source 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
POST /api/developer-console/apps/{appId}/p2p-capabilities/{capabilityId}/sync
Permission: app_topolo_developers.apps:write. Confirmation required. Validate against the current published action schema before preparing any actual request.
Inputs and behavior
| Field | Meaning |
|---|---|
| appId | Required nonblank real selected application ID. |
| capabilityId | Required existing capability ID under that same app. |
This sync route resolves workspace and checks selected-app write access, then capability existence. Unlike create/update, it does not contain a separate explicit onboardingState complete branch; the shared resolver behavior is UNKNOWN.
The eligibility refusal in the retained helper is422, whereas declared capability_not_syncable metadata says409. Details include appApproved, publicCatalogVisible, catalogListingState, capabilityApproved, publicDirectoryVisible and appRegistered; they do not expose every tested condition.
The server sends app/publisher/capability IDs, schemas, pricing, human-approval, context, trust, visibility and source revision to P2P. These are sourced from the saved record, not arbitrary body overrides to the sync action.
On upstream success the route updates local sync state, re-reads the capability and returns200 with inner {capability,sync}. Nested sync includes success true, data.capability from the upstream response and request_id. The local formatter is qualified; the upstream capability is arbitrary pass-through data and its P2P schema remains UNKNOWN.
On sync failure the route best-effort writes lastSyncedAt null and lastSyncError, then rethrows. If that state write fails it is ignored. Upstream publication can already have happened before local failure; no safe blanket retry-once policy follows from the generic descriptor.
The explicit sync action has no withdrawal route or cross-service rollback in this retained packet. A later edit or sync is another effect, not proof the prior public version was withdrawn.
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
Synchronize a reviewed capability
SYNTHETIC / UNRUN.
{
"appId": "app_fixture_publisher",
"capabilityId": "pcap_000000000001"
}
Given all eligibility predicates, configured service, successful upstream publication and successful local sync-state write, source returns200. The local carrier is qualified and a complete synthetic response appears below. Its simulated upstream value is not P2P conformance proof.
Ineligible capability
SYNTHETIC / UNRUN. The fixture app is approved, nonarchived and marketplace-distributed, but commerce remains at the pinned formatter's default draft catalog state with public visibility false, and the capability is draft. The already-pinned formatter passes draft as the catalog-state fallback; false public visibility is an explicit fixture assumption. No supplied code.
Request:
{
"appId": "app_fixture_publisher",
"capabilityId": "pcap_000000000001"
}
Source-shaped raw HTTP 422 body (fixture message text, not a capture):
{
"success": false,
"error": {
"message": "FIXTURE localized eligibility message; exact literal unqualified",
"details": {
"appApproved": true,
"publicCatalogVisible": false,
"catalogListingState": "draft",
"capabilityApproved": false,
"publicDirectoryVisible": true,
"appRegistered": true
}
}
}
This is the qualified serializer shape under the stated fixture assumptions. CLI/MCP wrapping and the current serving runtime were not exercised.
Missing service configuration
SYNTHETIC / UNRUN. An otherwise eligible fixture capability reaches the absent service-secret branch; no details/code is supplied. The route may still try to record local failure state.
Request:
{
"appId": "app_fixture_publisher",
"capabilityId": "pcap_000000000001"
}
Source-shaped raw HTTP 503 body (fixture message text, not a capture):
{
"success": false,
"error": {
"message": "FIXTURE localized service-configuration 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.
Publication followed by local failure
SYNTHETIC / UNRUN. This illustration explicitly chooses a plain Error with this message after simulated upstream success. The shared catch therefore yields500 without code/details. ApiError or non-Error values have their separately documented conditional behavior;500 is not promised for every failure in this branch.
Request:
{
"appId": "app_fixture_publisher",
"capabilityId": "pcap_000000000001"
}
Source-shaped raw HTTP 500 body (fixture message text, not a capture):
{
"success": false,
"error": {
"message": "Fixture local sync-state write failed"
}
}
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": "Read fictional stock counts for a fixture item.",
"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": true,
"status": "approved",
"sourceRevision": "rev_pcap_000000000001_1792738800",
"lastSyncedAt": "2026-10-23T07:00:00.000Z",
"lastSyncError": null,
"createdAt": "2026-10-23T07:00:00.000Z",
"updatedAt": "2026-10-23T07:00:00.000Z"
},
"sync": {
"success": true,
"data": {
"capability": {
"capability_id": "pcap_000000000001"
}
},
"request_id": "fixture-request-not-observed"
}
}
}
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; the commerce fixture uses the pinned draft fallback and explicitly assumed false visibility; 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.