<!--
Architecture:
This Markdown template is the canonical public agent quick-start. Runtime code
only injects deployment-specific URLs into placeholder tokens.
-->

# Connect an AI Agent to AppsPonds

## First-Shot Rule

- Decide the final operation before starting browser authorization.
- Request the complete scope set for that operation in one authorization request:
  - Inspect ponds and apps: `ponds:read`, `apps:read`.
  - Create a draft app: `ponds:read`, `apps:read`, `apps:create`.
  - Create and publish a new app: `ponds:read`, `apps:read`, `apps:create`, `apps:publish`.
  - Publish a new version of an existing app: `ponds:read`, `apps:read`, `apps:publish`.
  - Test local app roles: `apps:dev-auth` only.
- `publish_folder` and `POST /api/agent/apps` with `publishMode=publish` create and publish; both `apps:create` and `apps:publish` are mandatory.
- Never request `apps:create` alone for a create-and-publish operation.
- Never discover a missing scope by uploading first. Check the token before packaging.

## One Browser Approval

- Reuse a stored token only when `GET /api/agent/capabilities` shows every required scope and the required `canCreateApps`/`canPublishApps` value is `true`.
- Otherwise call `POST /api/integrations/authorize/start` once with the exact scope set above.
- Keep its `requestId` and `deviceCode` together.
- Start polling `POST /api/integrations/authorize/poll` immediately and keep polling that same request while the user approves.
- Show the returned request `name`, `userCode`, and `approvalUrl`; do not create another request because the user cannot see the first link.
- Capture the token synchronously from the first poll response containing it. The browser page never returns the token, and a later `consumed` response cannot recover it.
- Call `GET /api/agent/capabilities` immediately after capture.
- Stop before packaging when publish early-access is unavailable or pending. Do not start another authorization request for that blocker.

## One Publish Attempt

- Call `GET /api/agent/apps` before packaging to determine whether the requested slug already exists.
- Existing app: call `POST /api/agent/apps/{slug}/publish`; required write scope: `apps:publish`; do not send `publishMode`.
- New draft: call `POST /api/agent/apps` with `publishMode=draft`; required write scope: `apps:create`.
- New app published immediately: call `POST /api/agent/apps` with `publishMode=publish`; required write scopes: `apps:create` and `apps:publish`.
- New-app multipart fields: `displayName`, `slug`, `kind` (`static` or `server`), `sourceType`, optional `pondId`, `artifact`, and `publishMode`.
- Existing-app multipart fields: `artifact`, optional `sourceType`, `packageManifest`, `dockerfilePath`, `healthCheckPath`, `releaseCommand`, and `environmentVariables`.
- If AppsPonds reports pending approval, report that the candidate is staged; do not claim it is live.
- Report success only when AppsPonds identifies the deployment as current/live.

## MCP Tools

- `publish_folder` always creates and publishes a new app. Authorize once with `ponds:read`, `apps:read`, `apps:create`, `apps:publish`.
- `publish_existing_app` publishes an existing app. Authorize once with `ponds:read`, `apps:read`, `apps:publish`.
- Use `appSlug` from `list_apps`; a pond name is not an app slug or a local folder.
- Pass exactly one of `folderPath` or `artifactPath` to `publish_existing_app`.
- Use `appKind` (`static` or `server`) and `sourceType` (`static` or `docker-zip`).
- Do not invent `type`, `kind`, `mode`, or `publishMode` arguments unless the selected MCP tool schema lists them.

## Package Rules

- Put `appsponds-package.json` at the zip root.
- Static zip: `"app": { "kind": "static", "sourceType": "static" }`.
- Server zip: `"app": { "kind": "server", "sourceType": "docker-zip" }`.
- Never use `"docker-git"` inside a zip package manifest.
- Write strict UTF-8 JSON without a BOM and validate it before zipping.
- Exclude `node_modules`, `.git`, `_apdeploy`, `.env`, `.env.*`, `.npmrc`, private keys, and certificates.
- Server apps must listen on `0.0.0.0:3000` or `process.env.PORT`.
- Use the package schema for optional database, SQLite, storage, environment, and release-command fields.

## Published App User Context

- AppsPonds supplies every authenticated principal through `X-Auth-Principal-Type`, `X-Auth-Principal-Id`, `X-Auth-Principal-Name`, `X-Auth-App`, `X-Auth-Role`, `X-Auth-Role-Guid`, `X-Auth-Entitlement`, and `X-Auth-Context-Jwt`.
- Human requests use principal type `user` and also receive `X-Auth-User-Id` and `X-Auth-Email`. Service requests use principal type `service` and intentionally omit the human-only headers.
- Browser apps may call `GET /auth/context?appHost=<app-hostname>` from the matching app origin with credentials.
- Missing `X-Auth-Principal-Type` means anonymous access; do not trust client-supplied `X-Auth-*` headers outside the AppsPonds proxy.
- App-user roles are not AppsPonds Owner/App Manager/Marketing Manager permissions.

## Service Principals and API Keys

- API keys are app-scoped service credentials created by an Access Manager in App Management > Access > API Keys.
- Each key has one app role. The hosted app owns endpoint, HTTP-method, and business-rule authorization for that role.
- Exchange the long-lived key with `POST /api/app-auth/token`, content type `application/x-www-form-urlencoded`, and body field `api_key`. The response contains an `apat_` bearer token valid for 30 minutes.
- Send the `apat_` token to the app hostname as `Authorization: Bearer <token>`. Never send the raw `apk_` key to an app endpoint or store it in app code.
- AppsPonds validates live key state and role on every app request. Revocation, expiry, and role changes apply to existing access tokens immediately.
- Invalid AppsPonds tokens return `401` and never become anonymous access on public apps.
- Never put a long-lived API key in a URL or query parameter. Query-based token exchange is not supported because browsers, proxies, monitoring systems, and client logs may retain URLs.
- AppsPonds consumes valid `apat_` authorization before forwarding. Unrelated `Authorization` schemes remain available to the app.
- Publisher guide and Power BI example: `https://admin.appsponds.com/help/api-keys`.

## Testing Roles Locally

- Use `npx appsponds-agent@latest auth-proxy --appsponds-url https://admin.appsponds.com --app <app-slug> --target http://localhost:<port>`.
- Authorize with `apps:dev-auth` only.
- Browse the returned proxy URL and keep the companion running.
- Do not add an app-local role switcher, mock identity store, or `/apps/<app-slug>/` preview.

## Public References

- These documents are public and require no login; fetch them directly instead of searching for snippets.
- Canonical guide: https://admin.appsponds.com/agents.md
- Compact guide: https://admin.appsponds.com/llms.txt
- OpenAPI: https://admin.appsponds.com/openapi.json
- Package schema: https://admin.appsponds.com/schemas/appsponds-package.schema.json
- Agent manifest: https://admin.appsponds.com/.well-known/appsponds-agent.json