Topolo Docs Topolo Platform
Open workspace

Create local application record

Creates an entry in One’s local applications D1 table; it does not register an Auth service or change organization entitlements.

Outcome

Creates an entry in One’s local applications D1 table; it does not register an Auth service or change organization entitlements.

Before you start

  • Verify the intended environment, authenticated identity and explicitly authorized ownership context.
  • Discover the published apps:write permission and any required resource grant.
  • Approve the actual effects before confirming the unchanged planned request. Fixture identifiers, recipients and provider responses authorize no live operation.

Discover the target

topolo whoami --json
topolo actions get app_topolo_one.apps.create --detail --json
  • id: Exact identifier retained from the corresponding create response or permitted list; dashboard ids and local application ids are distinct.

Request

POST /api/apps

Inspect and test this action.

Input fields

FieldRequiredMeaningConstraints
idRequiredExact identifier retained from the corresponding create response or permitted list; dashboard ids and local application ids are distinct.{"type":"string","minLength":1}
nameRequiredname{"type":"string","minLength":1}
descriptionOptionaldescription{"type":"string","minLength":1}
iconOptionalicon{"type":"string","minLength":1}
categoryOptionalcategory{"type":"string","minLength":1}
developerOptionaldeveloper{"type":"string","minLength":1}
versionOptionalversion{"type":"string","minLength":1}
base_urlRequiredbase_url{"type":"string","minLength":1}
launch_url_templateOptionallaunch_url_template{"type":"string","minLength":1}
required_permissionsOptionalrequired_permissions{"type":"array","items":{"type":"string","minLength":1}}
optional_permissionsOptionaloptional_permissions{"type":"array","items":{"type":"string","minLength":1}}
statusOptionalstatus{"type":"string","enum":["active","inactive","maintenance"]}
is_system_appOptionalis_system_app{"type":"boolean"}

Inspect nested fields.

Interpret the response

Inspect returned app.id and stored fields. apps.list and admin.apps.list read Auth-backed catalogs and cannot verify this local table mutation; retain the result for authorized operator inspection.

Inspect the declared output schema and captured variants.

Worked examples

Actual exported One Worker with local Miniflare D1/KV. Authentication, application discovery, checkout, billing and remote widget providers are controlled synthetic responses; outbound access is sealed. No live provider outcome is established.

Save the reviewed input below as payload.json; substitute discovered identifiers and bind credential/resource context separately. CLI:

topolo actions validate app_topolo_one.apps.create --data @payload.json --json
topolo actions plan app_topolo_one.apps.create --data @payload.json --json
topolo actions call app_topolo_one.apps.create --data @payload.json --confirm --detail --json

MCP planning and execution use the same reviewed input:

{
  "tool": "topolo_prepare_action",
  "arguments": {
    "actionId": "app_topolo_one.apps.create",
    "input": {
      "id": "acme-tool",
      "name": "Acme Tool",
      "description": "A tool",
      "category": "productivity",
      "base_url": "https://acme.topolo.app",
      "required_permissions": [
        "acme:read"
      ]
    }
  }
}
{
  "tool": "topolo_call_action",
  "arguments": {
    "actionId": "app_topolo_one.apps.create",
    "input": {
      "id": "acme-tool",
      "name": "Acme Tool",
      "description": "A tool",
      "category": "productivity",
      "base_url": "https://acme.topolo.app",
      "required_permissions": [
        "acme:read"
      ]
    },
    "confirm": true,
    "detail": true
  }
}

Successful response · HTTP 201

Input · payload.json:

{
  "id": "acme-tool",
  "name": "Acme Tool",
  "description": "A tool",
  "category": "productivity",
  "base_url": "https://acme.topolo.app",
  "required_permissions": [
    "acme:read"
  ]
}

Captured response:

{
  "success": true,
  "message": "Application created successfully",
  "app": {
    "id": "acme-tool",
    "name": "Acme Tool",
    "description": "A tool",
    "icon": "📱",
    "category": "productivity",
    "developer": "Unknown",
    "version": "1.0.0",
    "base_url": "https://acme.topolo.app",
    "launch_url_template": "https://acme.topolo.app",
    "status": "active",
    "is_system_app": 0,
    "required_permissions": [
      "acme:read"
    ],
    "optional_permissions": [],
    "created_at": "2026-10-06 01:43:24",
    "updated_at": "2026-10-06 01:43:24"
  }
}

Inspect returned app.id and stored fields. apps.list and admin.apps.list read Auth-backed catalogs and cannot verify this local table mutation; retain the result for authorized operator inspection.

Refused request · HTTP 400 · http_error

Input · payload.json:

{
  "id": "bad id!",
  "name": "Bad",
  "base_url": "https://x.topolo.app"
}

Captured response:

{
  "error": "INVALID_REQUEST",
  "message": "App ID must contain only letters, numbers, hyphens, and underscores"
}

Inspect the retained raw error body and request details before correcting or retrying; this SDK code groups multiple distinct handler refusals.

Refused request · HTTP 409 · http_error

Input · payload.json:

{
  "id": "dup-app",
  "name": "Dup Again",
  "base_url": "https://dup.topolo.app"
}

Captured response:

{
  "error": "CONFLICT",
  "message": "Application with this ID already exists"
}

Inspect the retained raw error body and request details before correcting or retrying; this SDK code groups multiple distinct handler refusals.

Refused request · HTTP 403 · permission_denied

Input · payload.json:

{
  "id": "noperm",
  "name": "No Perm",
  "base_url": "https://x.topolo.app"
}

Captured response:

{
  "error": "INSUFFICIENT_PERMISSIONS",
  "message": "Required permissions: apps:write"
}

Discover the required permission and resource grants; re-authentication alone does not grant access.

Refused request · HTTP 401 · invalid_token

Input · payload.json:

{
  "id": "x",
  "name": "X",
  "base_url": "https://x.topolo.app"
}

Captured response:

{
  "error": "INVALID_TOKEN",
  "message": "Token validation failed"
}

Authenticate with a valid credential for the selected environment.

Refused request · HTTP 401 · auth_error

Input · payload.json:

{
  "id": "acme-tool",
  "name": "Acme Tool",
  "description": "A tool",
  "category": "productivity",
  "base_url": "https://acme.topolo.app",
  "required_permissions": [
    "acme:read"
  ]
}

Captured response:

{
  "error": "Token validation failed"
}

Authenticate using the required credential type for this action; service-only actions require the registered Auth service identity.

Verify the result

  • Inspect returned app.id and stored fields. apps.list and admin.apps.list read Auth-backed catalogs and cannot verify this local table mutation; retain the result for authorized operator inspection.

Errors and recovery

  • http_error (HTTP 400): Inspect the retained raw error body and request details before correcting or retrying; this SDK code groups multiple distinct handler refusals.
  • http_error (HTTP 409): Inspect the retained raw error body and request details before correcting or retrying; this SDK code groups multiple distinct handler refusals.
  • permission_denied (HTTP 403): Discover the required permission and resource grants; re-authentication alone does not grant access.
  • invalid_token (HTTP 401): Authenticate with a valid credential for the selected environment.
  • auth_error (HTTP 401): Authenticate using the required credential type for this action; service-only actions require the registered Auth service identity.

These are observed local refusals; inspect the raw error body because one SDK code can represent several handler messages. Preserve the request id when available.

Recovery boundary

Remove the exact local entry with apps.delete only when removal is intended. This does not revoke an Auth service or entitlement.

Evidence

Actual exported One Worker with local Miniflare D1/KV. Authentication, application discovery, checkout, billing and remote widget providers are controlled synthetic responses; outbound access is sealed. No live provider outcome is established.