diff --git a/public-docs/hub/api.md b/public-docs/hub/api.md new file mode 100644 index 000000000..4539ede35 --- /dev/null +++ b/public-docs/hub/api.md @@ -0,0 +1,214 @@ +--- +title: Hub public API +description: Use organization API keys to install configuration, dispatch manual runs, and enroll daemons. +nav: Public API +order: 78 +category: Hub +--- + +# Hub public API + +The Hub public API lets automation operate on projects and daemons in one +organization. Set the Hub origin in `HUB_URL` below, for example +`https://hub.example.com`. + +## Authentication + +Create an organization API key from the Hub dashboard under **API keys**. Send +it as a bearer token on every API request: + +```http +Authorization: Bearer paseo_pk_... +Content-Type: application/json +``` + +API keys are organization-scoped. The organization that owns the key determines +which projects and daemon enrollment tokens it can reach; there is no +organization ID to add to these requests. A project slug from another +organization is not accessible through the key. + +Each key has one or more selectable scopes: + +| Scope | Operation | +| ----------------------- | --------------------------------------------------- | +| `configuration:install` | Replace and activate a project's configuration. | +| `runs:dispatch` | Dispatch a configured manual trigger for a project. | +| `daemons:enroll` | Issue a short-lived daemon enrollment token. | + +API keys do not grant dashboard access. They cannot manage connections, +projects, or organization members. + +Missing, invalid, or revoked credentials return `401`: + +```json +{ "error": "unauthorized" } +``` + +A valid key without the scope required by an endpoint returns `403`: + +```json +{ "error": "forbidden" } +``` + +## Configuration install + +`configuration:install` replaces the project's current configuration with the +supplied YAML after Hub validates and compiles it, then activates the new +revision. + +```http +POST /api/configurations/install +``` + +Request body: + +```json +{ + "projectSlug": "my-project", + "yaml": "environments:\n - name: production\n kind: daemon\n daemon: build-server\n cwd: /workspace\ntriggers:\n - name: deploy\n on: manual.run\n environment: production\n filters:\n from_users: [automation]\n agent:\n provider: opencode\n mode: full-access\n prompt: Deploy the project" +} +``` + +The YAML must describe a valid Hub configuration. Replace the example daemon, +working directory, and trigger values with resources in your organization. + +On success, Hub returns `201`: + +```json +{ + "projectSlug": "my-project", + "versionId": "00000000-0000-4000-8000-000000000000", + "version": 3, + "active": true +} +``` + +Common responses are `400 {"error":"invalid_request"}` for a missing or +malformed body, `404 {"error":"project_not_found"}` for an inactive or +unknown project in the key's organization, and `422` for invalid YAML or an +invalid configuration. An invalid configuration includes the provisional +`versionId` in its response. + +Example: + +```bash +curl --fail-with-body -sS -X POST "$HUB_URL/api/configurations/install" \ + -H "Authorization: Bearer $PASEO_API_KEY" \ + -H "Content-Type: application/json" \ + --data @configuration-install.json +``` + +## Manual run dispatch + +`runs:dispatch` dispatches a configured `manual.run` trigger for a project. +The trigger must exist in the active configuration, and `actor` must be listed +in that trigger's `filters.from_users` allowlist. + +```http +POST /api/manual-runs +``` + +Request body: + +```json +{ + "projectSlug": "my-project", + "expectedVersionId": "00000000-0000-4000-8000-000000000000", + "trigger": "deploy", + "actor": "automation", + "deliveryKey": "deploy-2026-08-04-001", + "input": { + "ref": "main" + } +} +``` + +`expectedVersionId` is optional. When supplied, Hub rejects the dispatch if +that configuration revision is no longer active. `input` is application data +made available to the trigger; it may be any JSON value. Use a unique, +stable `deliveryKey` for each dispatch. Reusing it makes the request resolve to +the existing trigger instead of starting a duplicate run. + +On success, Hub returns `200`: + +```json +{ + "deliveryKey": "deploy-2026-08-04-001", + "triggerId": "00000000-0000-4000-8000-000000000000", + "executionId": "00000000-0000-4000-8000-000000000000", + "status": "spawning", + "daemonId": "00000000-0000-4000-8000-000000000000", + "agentId": "agent-id" +} +``` + +Common responses are `400 {"error":"invalid_request"}`, `404` for an +unknown project, missing configuration, or missing trigger, `403 +{"error":"actor_forbidden"}` when the actor is not allowed by the trigger, +and `409` when the daemon is offline, the expected configuration is no longer +current, or the run could not be dispatched. + +Example: + +```bash +curl --fail-with-body -sS -X POST "$HUB_URL/api/manual-runs" \ + -H "Authorization: Bearer $PASEO_API_KEY" \ + -H "Content-Type: application/json" \ + --data '{ + "projectSlug": "my-project", + "trigger": "deploy", + "actor": "automation", + "deliveryKey": "deploy-2026-08-04-001", + "input": {"ref": "main"} + }' +``` + +## Daemon enrollment + +`daemons:enroll` issues a short-lived enrollment token for a daemon. The API +key mints this token; it is not the daemon's long-lived credential and must not +be used as one. + +```http +POST /api/daemons/enrollment-tokens +``` + +Send an empty JSON object as the request body. On success, Hub returns `201`: + +```json +{ + "token": "short-lived-enrollment-token", + "expiresAt": "2026-08-04T12:10:00.000Z" +} +``` + +The token expires after 10 minutes and is consumed when the daemon enrolls. +Pass it to the Paseo CLI, which exchanges it for the daemon's connection +credential: + +```bash +ENROLLMENT_TOKEN="$(curl --fail-with-body -sS -X POST \ + "$HUB_URL/api/daemons/enrollment-tokens" \ + -H "Authorization: Bearer $PASEO_API_KEY" \ + -H "Content-Type: application/json" \ + --data '{}' | jq -r .token)" + +paseo hub connect "$HUB_URL" --token "$ENROLLMENT_TOKEN" +``` + +An enrollment token cannot be reused. Revoking the API key immediately rejects +future API requests and expires any unconsumed enrollment tokens that key +issued. A race between revocation and issuance is resolved by Hub before a new +token is stored. + +## Keys, scopes, and audit information + +The complete API-key secret is shown once, immediately after creation. Store it +in your deployment's secret manager; Hub does not show it again. The dashboard +retains only the key's prefix and shows its selected scopes, creation time, +last-used time, and status. + +Hub updates `last used` after a key successfully authenticates for a scoped API +operation. API operations retain the key attribution in Hub's audit evidence, +so configuration revisions, manual dispatches, and daemon enrollment tokens +can be traced to the organization key that created them.