Topolo Docs Topolo Platform
Open workspace

Update developer profile and onboarding

Save the shared developer profile with deliberate completion intent, current-state verification and clear validation, timestamp and permission boundaries.

Outcome

Save the shared developer profile with deliberate completion intent. Read the state transition behaviour before editing a completed profile.

PUT /api/developer-console/onboarding

Before you start

Read workspace.onboarding.get in the original authorized organization. On the ordinary same-organization path this PUT requires canonical Developers workspace admin access plus separate apps:write permission; discover and bind that workspace separately. The existing privileged platform cross-organization branch uses its seeded profile workspace rather than honoring a requested header, without removing the remaining action/context checks. Review the full profile and intended completion transition before authorizing this confirmed mutation.

The source resolves a non-suspended/non-revoked context. This is an organization-scoped shared developer profile, not an independent Auth account or new workspace. Shared context may initialize a local profile/mirror before later validation refusal.

All examples are source-derived synthetic UNRUN data with fictional identities. No onboarding, signup, private identity, Auth, billing or workspace operation, app import/test or runtime probe was executed.

Request and effects

Action: app_topolo_developers.workspace.onboarding.update.

Native input admits many optional names with unconstrained field schemas. This does not implement unrelated appId,status,reviewNotes,permission or ownerType controls. Use only documented profile fields and their semantic validation.

Profile fields are name,companyName,websiteUrl,planPreference,summary,useCase,appIdea,firstAppName,firstAppCategory,supportEmail,operationalContactName,operationalContactEmail,location,companySpecificNeeds,communityBenefitIdea,intent,onboardingStep. Omitted/null text falls back to existing values, then trims; supportEmail, operationalContactEmail and planPreference are lowercased; operationalContactName is trimmed and retains case.

Nonempty name is2-100 characters; companyName max100; nonempty summary/useCase20-1000; appIdea400; firstAppName100; firstAppCategory80; operationalContactName100; location120; companySpecificNeeds/communityBenefitIdea1000; intent80. Plan is monthly or annual. Nonempty contacts need the source email pattern. Empty website is allowed; otherwise URL must parse as HTTP/HTTPS. This is not a reachability or safety certificate.

Completion intent is exactly onboardingState:"complete" or onboardingConfirmed:true. Completion additionally requires name>=2, nonempty companyName, summary or useCase>=20, firstAppName or appIdea, and supportEmail or operationalContactEmail. It is not reviewer approval, billing or an Auth grant.

Without completion intent, state is recomputed as in_progress when selected company/website/summary/useCase/firstAppName/support fields are nonempty, else not_started. Prior complete is not preserved automatically. Even an empty native-admitted object can demote a complete profile and change management flags. Deliberately review the intended transition.

The helper writes the current authenticated user_id/email, existing organization identity, source developer-console, and preserves existing access/review status. Its known profile metadata is rebuilt, not arbitrary old metadata merged. The organization-shared profile update has no version/compare-and-swap guard.

Completion sets onboardingCompletedAt to current server time, including repeated completion. A non-completing save retains an old completedAt even when current state becomes in_progress. onboardingStep uses Number and Math.max(1,...) without a safe-integer/max check; use a sensible positive integer rather than relying on the unconstrained native schema.

Explicit empty strings do not consistently clear core columns: route text normalization accepts them but database name/company/website fields use truthy fallbacks. Metadata uses separate reconstruction. Read back actual values instead of assuming null/empty semantics or preservation of arbitrary fields.

HTTP200 returns {success:true,message,data:{workspace,developer,created:false}}. workspace appCount,pendingListings,requestCount are placeholders0 because the handler passes empty arrays; developer counters are organization queries. Management flags reflect active+complete only and do not grant action/resource permissions. No Auth workspace, credential, app, payment or build is created.

The write precedes profile read/summary/response. A later failure is not automatic rollback. There is no caller idempotency key or automatic inverse; reconcile exact current profile/times and concurrent writers before any further authorized correction.

Worked requests and responses

Save progress on a completed profile

Complete synthetic UNRUN HTTP200 assuming existing state exactly matches the documented onboarding-read fixture, no concurrent writes, and successful authorization/validation. Completion intent is absent: state becomes in_progress while its historical completedAt remains.

Input:

{
  "summary": "Plan a fixture inventory application.",
  "supportEmail": "owner@fixture.invalid"
}

Response:

{
  "success": true,
  "message": "Developer onboarding updated successfully",
  "data": {
    "workspace": {
      "workspaceId": "ws_docs_fixture",
      "organizationId": "org_docs_fixture",
      "organizationName": "Fixture organization",
      "organizationSlug": "fixture-org",
      "ownerUserId": "usr_docs_fixture",
      "accessStatus": "active",
      "onboardingState": "in_progress",
      "reviewState": "unreviewed",
      "onboardingStep": 1,
      "source": "developer-console",
      "appCount": 0,
      "pendingListings": 0,
      "requestCount": 0,
      "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_fixture",
      "name": "Fixture owner",
      "email": "owner@fixture.invalid",
      "companyName": "Fixture organization",
      "websiteUrl": "",
      "subscriptionPlan": "monthly",
      "accessStatus": "active",
      "reviewState": "unreviewed",
      "onboardingState": "in_progress",
      "onboardingStep": 1,
      "onboardingCompletedAt": "2026-10-20T08:00:00.000Z",
      "summary": "Plan a fixture inventory application.",
      "useCase": "",
      "appIdea": "",
      "firstAppName": "",
      "firstAppCategory": "",
      "supportEmail": "owner@fixture.invalid",
      "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"
    },
    "created": false
  }
}

Verify the deliberate state transition and changed flags; do not present this as a completion-preserving patch.

Explicitly complete the reviewed profile

Complete synthetic UNRUN HTTP200 assuming existing fixture name/company/context and all validation are valid. Time is illustrative. created:false and management flags do not imply Auth workspace creation, permissions, billing or review approval.

Input:

{
  "summary": "Plan a fixture inventory application.",
  "firstAppName": "Fixture Inventory",
  "supportEmail": "owner@fixture.invalid",
  "onboardingConfirmed": true
}

Response:

{
  "success": true,
  "message": "Developer onboarding updated successfully",
  "data": {
    "workspace": {
      "workspaceId": "ws_docs_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": 0,
      "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_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-21T08:00:00.000Z",
      "summary": "Plan a fixture inventory application.",
      "useCase": "",
      "appIdea": "",
      "firstAppName": "Fixture Inventory",
      "firstAppCategory": "",
      "supportEmail": "owner@fixture.invalid",
      "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"
    },
    "created": false
  }
}

Read back the exact intended complete state, profile and timestamp in the original organization.

A short summary fails semantic validation

Complete synthetic UNRUN rawHTTP422 assuming other existing fixture fields are valid. Broad native schema admits the string; the handler rejects its trimmed length before selected profile upsert. Shared context effects are separate.

Input:

{
  "summary": "short"
}

Response:

{
  "success": false,
  "error": {
    "message": "Please review the highlighted onboarding fields.",
    "details": {
      "summary": true
    }
  }
}

Correct intended summary and completion intent before a newly authorized write.

Verify and recover

  1. Read workspace.onboarding.get in the original organization/workspace and compare exact identity, intended fields, current onboardingState, onboardingCompletedAt and preserved access/review fields. Flags do not replace permissions.

  2. Use app/history reads for actual counts. Workspace counters here are placeholders while profile counts are organization-scoped; do not infer zero actual apps/requests.

  3. After uncertain response, read exact current profile/state/time and reconcile concurrent edits before any further write. No blind repeat of completion or assumption empty/null cleared a core column.

  4. Any deliberate correction is a new authorized current-state update, preserving access/review ownership. No automatic rollback or permission override exists in the broad admitted field list.

Change log

  • 2026-10-21: Added completion/demotion, shared identity, semantic validation, fallback/clearing, timestamp and concurrency boundaries with complete synthetic unrun examples.