# Integrate WAMP Cloud Authenticate a backend, submit a durable agent task, and read its outcome with a complete Shell or TypeScript example. Source: https://docs.cloud.vampikez.fun/start/integrate/ Run one task through the public API, then use its Session and Run IDs to follow work or recover after a disconnect. No SDK is required. The example uses an empty workspace; repository access is a separate grant. ## Before the first request You need an organization with compute configured and an App installation granted `wamp.cloud.sessions:create`, `wamp.cloud.turns:submit` and `wamp.cloud.sessions:read`. A backend serving one organization can use an administrator-issued installation API key. A product installed by multiple organizations exchanges its signed App assertion for a short-lived installation token. [Authentication](/start/authentication/) covers both. Set `WAMP_API` to your Cloud API origin and `WAMP_TOKEN` to that installation credential. This deployment uses `https://api.vampikez.fun`; the first-party browser workspace is at [cloud.vampikez.fun](https://cloud.vampikez.fun). If Account and Cloud use separate origins, token exchange goes to Account and these `/v1` calls go to Cloud. Keep installation credentials on your backend. A discovery response separates execution capacity from repository authority. This excerpt shows available compute with no private repository grant: ```json collapse={4-30} { "capabilities": { "environments": [{ "id": "managed", "label": "WAMP managed sandbox", "availability": { "state": "available" }, "default": true, "runtimes": ["wamp", "pi"], "workspace": { "checkpoint": "portable_tree", "maxCheckpointBytes": 268435456 } }], "repositories": { "sources": [ { "kind": "public_https", "available": true }, { "kind": "github_repository_grant", "available": false, "discovery": "/v1/repositories" } ], "publications": [ { "kind": "github_draft_pull_request", "available": false }, { "kind": "github_direct_branch_push", "available": false } ], "merges": [{ "kind": "github_pull_request_merge", "available": false, "strategies": ["merge", "squash", "rebase"], "modes": ["now", "when_ready"] }] } } } ``` ## Run a task 1. **Check compute availability.** Read `capabilities.environments[].availability`. Unavailable compute requires organization configuration. A new native Session uses the organization's resolved agent slot model when `model` is omitted; if no slot is available, creation returns `400 cloud_agent_model_unavailable`. 2. **Persist the command before sending it.** Session and Turn IDs are caller-owned UUIDs. The example writes `cloud-command.json` once and reuses it on reruns, so an ambiguous network failure does not create duplicate work. In your backend, store the same information in your database. 3. **Submit and observe.** Creation with `initialTurn` commits both the Session and its queued Run. The Turn ID is also the Run ID. Poll until terminal or awaiting input, with a local deadline; reaching that deadline does not cancel Cloud work. Requires `curl`, `jq` and `uuidgen`. Export `WAMP_TOKEN` before running. ```bash title="run-cloud-task.sh" #!/usr/bin/env bash set -euo pipefail export WAMP_API="${WAMP_API:-https://api.vampikez.fun}" : "${WAMP_TOKEN:?Set WAMP_TOKEN to your installation credential}" if [ ! -f cloud-command.json ]; then CAPS=$(curl --fail-with-body --max-time 30 -sS "$WAMP_API/v1/capabilities" \ -H "Authorization: Bearer $WAMP_TOKEN") printf '%s' "$CAPS" | jq -e \ '.capabilities.environments | any(.availability.state == "available")' >/dev/null SESSION_ID=$(uuidgen | tr 'A-Z' 'a-z') TURN_ID=$(uuidgen | tr 'A-Z' 'a-z') # This local demo runs one process; use a database transaction in production. jq -n --arg a "$WAMP_API" --arg s "$SESSION_ID" --arg t "$TURN_ID" \ '{apiOrigin:$a, sessionId:$s, body:{initialTurn:{id:$t, message:"Create hello.txt containing hello, then summarize what you did."}}}' \ > cloud-command.json fi SAVED_API=$(jq -er '.apiOrigin' cloud-command.json) if [ "$SAVED_API" != "$WAMP_API" ]; then echo "Saved command belongs to a different API origin" >&2 exit 1 fi SESSION_ID=$(jq -er '.sessionId' cloud-command.json) TURN_ID=$(jq -er '.body.initialTurn.id' cloud-command.json) BODY=$(jq -c '.body' cloud-command.json) curl --fail-with-body --max-time 30 -sS -X PUT "$WAMP_API/v1/sessions/$SESSION_ID" \ -H "Authorization: Bearer $WAMP_TOKEN" \ -H 'Content-Type: application/json' -d "$BODY" | jq '{session, initialTurn}' # Follow the ordered Event log. Save the last id you handled before reconnecting. curl -N --fail-with-body -sS \ "$WAMP_API/v1/sessions/$SESSION_ID/events/stream?after=0" \ -H "Authorization: Bearer $WAMP_TOKEN" \ -H 'Accept: text/event-stream' ``` Run with a TypeScript runner on Node 22 or later. Export `WAMP_TOKEN` first. ```ts check title="run-cloud-task.ts" const api = process.env.WAMP_API ?? 'https://api.vampikez.fun'; const token = process.env.WAMP_TOKEN; if (!token) throw new Error('Set WAMP_TOKEN to your installation credential'); async function call(path: string, method = 'GET', body?: unknown) { const response = await fetch(`${api}${path}`, { method, headers: { Authorization: `Bearer ${token}`, ...(body === undefined ? {} : { 'Content-Type': 'application/json' }), }, body: body === undefined ? undefined : JSON.stringify(body), signal: AbortSignal.timeout(30_000), }); const value = await response.json(); if (!response.ok) { throw new Error(`${response.status} ${value.error}; retry-after=${ response.headers.get('Retry-After') ?? 'none'}`); } return value; } async function main() { let command; try { command = JSON.parse(await readFile('cloud-command.json', 'utf8')); } catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; const { capabilities } = await call('/v1/capabilities'); const available = capabilities.environments.some( (environment: { availability: { state: string } }) => environment.availability.state === 'available', ); if (!available) throw new Error('Configure organization compute access'); command = { apiOrigin: api, sessionId: randomUUID(), body: { initialTurn: { id: randomUUID(), message: 'Create hello.txt containing hello, then summarize what you did.', } }, }; // Exclusive creation prevents this demo from overwriting another command. await writeFile('cloud-command.json', JSON.stringify(command), { flag: 'wx' }); } if (command.apiOrigin !== api) throw new Error('Saved command belongs to a different API origin'); const sessionPath = `/v1/sessions/${command.sessionId}`; const runId = command.body.initialTurn.id; console.log(await call(sessionPath, 'PUT', command.body)); const response = await fetch(`${api}${sessionPath}/events/stream?after=0`, { headers: { Authorization: `Bearer ${token}`, Accept: 'text/event-stream' }, }); if (!response.ok || !response.body) { throw new Error(`Event stream refused: ${response.status} ${await response.text()}`); } const reader = response.body.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; process.stdout.write(decoder.decode(value, { stream: true })); } } main().catch(error => { console.error(error); process.exitCode = 1; }); ``` Both examples reuse the saved command. To start independent work, save a new command under a new application record; do not change the body under an existing ID. Never reuse the file across organizations. ## Handle the outcome | Run state | Next action | |---|---| | `queued`, `dispatching`, `running` | Continue observing. A closed Event stream leaves the Run active; reconnect from the saved cursor | | `awaiting` | Fetch open Interactions and submit a new Turn with `replyTo.interactionId` | | `completed` | Read safe message events and retained Artifacts | | `failed`, `cancelled`, `crashed` | Inspect `stopReason`, `lastError` and the result summary before retrying work | The Session's `phase` is a presentation projection. The Run's `status` and the ordered event log are the outcome authority. A file in the workspace is not necessarily a retained public Artifact; the agent must present it for retention. See [Artifacts](/concepts/artifacts/). To continue the same conversation, generate and persist another Turn ID and send `PUT /v1/sessions/{sessionId}/turns/{turnId}` with `message`. Optional agent selection belongs to that Turn; [Agents](/concepts/agents/) explains runtime, model and settings. If the workspace has expired, read Session `continuation` first. [Follow a run](/guides/follow-a-run/) covers event cursors and interaction replies in both languages. ## Add repository delivery [Connect a repository](/guides/connect-a-repository/) before creating the Session if the task needs an existing checkout. A Cloud capability is not repository authority; private GitHub access requires a repository grant. After work finishes, fetch the repository review and publish its exact revision. Choose [a pull request](/guides/open-a-pull-request/) or [direct branch delivery](/guides/push-directly-to-a-branch/). Persist Publication and Merge IDs like Turn IDs. Publication success and exact-head PR merge are separate commands and must be observed separately. ## Move the example into a backend [Drive Cloud from a backend](/guides/from-your-backend/) covers tenant mapping, credential refresh and durable state. Add bounded network retries with jitter: repeat the exact command after an ambiguous failure, honor `Retry-After` on `429`/`503`, back off on a `502`, `503` or `504` with no JSON code when the request is safe to repeat, and resolve `409` by its machine code. Bound the retries in time, not attempts, so they outlast a release's brief `503 service_unavailable`. The session quota has no timed retry; archive completed Sessions to free it. [Errors](/reference/errors/) and [Limits](/reference/limits/) contain the individual policies. Page Events from your last committed exclusive cursor and deduplicate by event ID. Keep credentials and customer content out of logs. Archive only when no further commands are needed: archive is irreversible, although reads remain available.