# Vessium Documentation > Full documentation for LLM consumption. > Source: https://vessium.com/docs --- # Introduction Vessium is an infrastructure platform for software automation. Two products, one API: - **Passthrough API** — Turns platforms without public APIs into programmable REST endpoints. Vessium handles authentication, session management and credential security. You get structured JSON via simple API calls. - **Agent API** — AI agents that operate any software with a screen. Point an agent at a task and it clicks, types, reads and navigates — just like a human would. Both products use the same API keys, the same authentication model and the same organization structure. --- ## Passthrough API Connect to platforms designed for browser-only access — no public APIs, no developer docs, no OAuth. Get the platform's data back as structured JSON via standard REST calls. Credentials stored in AWS with KMS encryption, or bring your own AWS account with cross-account BYOK. Every API call recorded with method, status, bytes and duration — full visibility into usage. ### API Example ```bash curl https://vessium.com/api/v1/integrations/pulse/your-connection-id/appointments?dateFrom=2026-05-12&dateTo=2026-05-19 \ -H "Authorization: Bearer vess_your_api_key" ``` Returns the platform's data as structured JSON. Vessium handles the authentication lifecycle behind the scenes. --- ## Agent API Works with any desktop or web application through visual understanding — no integrations needed. Trigger tasks programmatically and get structured results via webhooks or polling. Get notified in real time when runs complete, error, or abort. Each box has a full filesystem — your agent remembers files, credentials and state across runs. Set up recurring tasks on cron schedules — runs execute automatically. Fine-grained access control with keys scoped to specific boxes or entire organizations. ### Quick Start Sign up at [vessium.com/app](https://vessium.com/app) and create a box — a cloud virtual machine where your agent operates. Open the box and set up whatever software you want to automate. The agent sees the screen exactly like you do. Type a natural language instruction and the agent gets to work — clicking, typing and navigating through your software. ### API Example ```bash curl -X POST https://vessium.com/api/v1/runs \ -H "Authorization: Bearer vess_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "boxId": "your-box-id", "message": "Check my email and summarize anything urgent", "webhook": "https://your-app.com/hook" }' ``` API keys are org-scoped and can be restricted to specific boxes. See the [Authentication](/docs/authentication) guide for details. --- ## Next Steps ### Passthrough API - **[Overview](/docs/passthrough-overview)** — How Passthrough works and URL structure - **[Connections](/docs/passthrough-connections)** — Create and manage connections to upstream platforms - **[Credentials & Security](/docs/passthrough-credentials)** — Credential storage, BYOK and security - **[Call History](/docs/passthrough-calls)** — Usage tracking and call history ### Agent API - **[Authentication](/docs/authentication)** — Create API keys and understand auth scoping - **[Runs](/docs/runs)** — Trigger tasks, poll for status and abort running sessions - **[Webhooks](/docs/webhooks)** — Set up real-time notifications for run lifecycle events - **[Downloads](/docs/downloads)** — Retrieve files and artifacts from completed runs - **[Examples](/docs/examples)** — End-to-end integration patterns and recipes --- # Authentication All API requests — both [Agent API](/docs/runs) and [Passthrough](/docs/passthrough-overview) — are authenticated with a Bearer token in the `Authorization` header: ``` Authorization: Bearer vess_abc123... ``` ## Creating an API Key Navigate to the **Vault** section in the Vessium app. This is where all API keys for your organization are managed. Choose which boxes the key can access — all boxes in your org, or a specific set of box IDs. Your key starts with `vess_` — copy it immediately. It won't be shown again. API keys are hashed before storage — Vessium cannot retrieve a lost key. Store it in your environment variables or a secrets manager. ## Key Scoping Every key belongs to an organization and can only access boxes within it. Keys can target all boxes in the org or be restricted to an explicit list of box IDs. Use box-scoped keys for production integrations — follow the principle of least privilege. ## Error Responses All error responses follow a consistent shape: ```json { "error": "Human-readable message", "success": false } ``` | Status | Meaning | | ------ | ------------------------------------------------- | | `401` | Missing, invalid, or expired API key | | `403` | API key does not have access to the requested box | | `404` | Box not found or doesn't belong to the key's org | --- # Runs ## Overview A run is a single task execution in the Vessium API. Each run sends an AI agent inside a box to complete a task — checking email, navigating websites, filling forms, or anything a human could do with a screen. Runs triggered via the API execute asynchronously inside the box. Use [webhooks](/docs/webhooks) for real-time notifications or poll for status. For production workflows, prefer [webhooks](/docs/webhooks) over polling — they're faster and more efficient. Use polling as a fallback. ### Monitoring API runs appear in the chat interface just like regular conversations. Switch to the **API** tab to watch them stream in real time. ![Monitoring a run from the chat interface](/images/docs/runs-chat-monitor.png) --- **Request:** ```bash curl -X POST https://vessium.com/api/v1/runs \ -H "Authorization: Bearer vess_..." \ -H "Content-Type: application/json" \ -d '{ "boxId": "feaaf263-...", "message": "Check my email and summarize anything urgent", "model": "regular", "webhook": "https://example.com/hook" }' ``` | Field | Type | Required | Description | | ----------- | ---------------------------------- | -------- | ------------------------------------------------------------------------------------------ | | `boxId` | string | Yes | Target box ID. Box must be running. | | `message` | string | Yes | Prompt text (max 100,000 chars). | | `model` | `"max"` \| `"regular"` \| `"fast"` | No | Model to use. Default: `regular`. Fast is auto-upgraded to regular (minimum for API runs). | | `sessionId` | string | No | Custom session ID. Default: `api-{random}`. | | `webhook` | string (URL) | No | URL to receive completion [webhook](/docs/webhooks). If omitted, use polling. | **Response (202):** ```json { "success": true, "sessionId": "api-gqB11W56HMRGmLGf", "requestId": "req_epxlIiSZcpfdUiUp", "status": "processing", "pollUrl": "https://vessium.com/api/v1/runs/api-gqB11W56HMRGmLGf?boxId=feaaf263-..." } ``` | Status | Condition | | ------ | --------------------------- | | `401` | Invalid or missing API key | | `403` | Box not in key's scope | | `404` | Box not found / wrong org | | `503` | Box is not running | | `503` | Bridge unreachable | | `500` | Bridge rejected the request | --- Use this when webhooks aren't configured, or as a fallback to check on a running task. **Request:** ```bash curl "https://vessium.com/api/v1/runs/api-gqB11W56HMRGmLGf?boxId=feaaf263-..." \ -H "Authorization: Bearer vess_..." ``` | Parameter | Location | Required | Description | | ----------- | -------- | -------- | ------------------------------------ | | `sessionId` | Path | Yes | Session ID from the trigger response | | `boxId` | Query | Yes | Box ID (for auth validation) | **Response (processing):** ```json { "success": true, "sessionId": "api-gqB11W56HMRGmLGf", "status": "processing", "messageCount": 1, "streaming": true } ``` **Response (complete):** ```json { "success": true, "sessionId": "api-gqB11W56HMRGmLGf", "status": "complete", "messageCount": 2, "response": "You have 3 urgent emails...", "streaming": false, "downloads": { "fileCount": 1, "totalBytes": 24576, "files": ["report.csv"], "exportUrl": "https://vessium.com/api/v1/runs/api-gqB11W56HMRGmLGf/downloads?boxId=feaaf263-..." } } ``` | Field | Description | | -------------- | ------------------------------------------------------------------------- | | `status` | `"processing"` (still running) or `"complete"` (finished) | | `messageCount` | Number of messages in the run (user + assistant) | | `response` | Full assistant response text. Only present when `status` is `"complete"`. | | `streaming` | Whether the run is actively generating | | `downloads` | File download metadata. Only present when the run produced output files. | **Polling Strategy:** Runs can take from seconds to hours depending on the task. Start with 5-second intervals and back off to 15-30 seconds for long-running tasks. Use [webhooks](/docs/webhooks) instead of polling when possible. --- If the run has a webhook configured, a `session.aborted` webhook will be delivered. **Request:** ```bash curl -X POST "https://vessium.com/api/v1/runs/api-gqB11W56HMRGmLGf/abort?boxId=feaaf263-..." \ -H "Authorization: Bearer vess_..." ``` | Parameter | Location | Required | Description | | ----------- | -------- | -------- | ---------------------------- | | `sessionId` | Path | Yes | Session ID to abort | | `boxId` | Query | Yes | Box ID (for auth validation) | **Response (success):** ```json { "success": true, "sessionId": "api-gqB11W56HMRGmLGf", "status": "aborted" } ``` **Response (not running):** ```json { "success": false, "error": "Session is not currently running", "sessionId": "api-gqB11W56HMRGmLGf" } ``` | Status | Condition | | ------ | --------------------------------------------------------------- | | `200` | Run aborted successfully | | `409` | Run is not currently active (already finished or never started) | | `401` | Invalid or missing API key | | `403` | Box not in key's scope | | `404` | Box not found / wrong org | | `503` | Box is not running / bridge unreachable | --- ## Run Lifecycle ```mermaid %% width: 600 graph TB classDef input fill:#e1f5fe,stroke:#01579b,stroke-width:2px,color:#000 classDef process fill:#fff3e0,stroke:#ff6f00,stroke-width:3px,color:#000 classDef agent fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#000 classDef success fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#000 classDef error fill:#ffebee,stroke:#c62828,stroke-width:2px,color:#000 classDef abort fill:#f3e5f5,stroke:#6a1b9a,stroke-width:2px,color:#000 API["POST /api/v1/runs"]:::input Route(Route to Box):::input subgraph Orchestrator [" Orchestrator "] Plan(Plan Task):::process Sub(Sub-Agent Execution):::agent Analyze(Analyze Result):::process end Complete([session.complete]):::success Error([session.error]):::error Aborted([session.aborted]):::abort API --> Route Route --> Plan Plan --> Sub Sub --> Analyze Analyze --> Complete Analyze --> Error Route -.->|POST /abort| Aborted ``` --- ## Limits | Constraint | Value | | ------------------ | ------------------------------------------ | | Message max length | 100,000 characters | | Model minimum | `regular` (fast auto-upgraded) | | Available models | `max`, `regular`, `fast` | | Run duration | No limit (runs until complete/error/abort) | --- # Downloads ## Overview When a run produces output files (screenshots, exports, generated documents, etc.), they're available for download via these endpoints. Download metadata is included in the [poll response](/docs/runs) and [webhook payloads](/docs/webhooks) when files are present. Check the `downloads` field in your [poll response](/docs/runs) or [webhook payload](/docs/webhooks) to know when files are available. It includes the file count, total size and a direct export URL. ### Previewing in the Chat Downloaded files from API runs are also visible in the chat interface. Open the run's conversation to preview files inline before downloading them programmatically. ![Previewing downloads in the chat interface](/images/docs/downloads-chat-preview.png) --- **Download as ZIP (default):** ```bash curl -o session-files.zip \ "https://vessium.com/api/v1/runs/api-gqB11W56HMRGmLGf/downloads?boxId=feaaf263-..." \ -H "Authorization: Bearer vess_..." ``` **Get file listing:** ```bash curl "https://vessium.com/api/v1/runs/api-gqB11W56HMRGmLGf/downloads?boxId=feaaf263-...&format=json" \ -H "Authorization: Bearer vess_..." ``` | Parameter | Location | Required | Description | | ----------- | -------- | -------- | ------------------------------------------------------- | | `sessionId` | Path | Yes | Session ID | | `boxId` | Query | Yes | Box ID (for auth validation) | | `format` | Query | No | Set to `json` to return a file listing instead of a ZIP | **Response (ZIP):** Binary ZIP stream with `Content-Type: application/zip` **Response (JSON):** ```json { "fileCount": 2, "totalBytes": 48120, "files": [ { "name": "screenshot.png", "size": 24576, "mtime": "2026-02-11T05:02:00.000Z" }, { "name": "report.csv", "size": 23544, "mtime": "2026-02-11T05:02:05.000Z" } ] } ``` | Status | Condition | | ------ | ------------------------------------------- | | `200` | Files returned (ZIP or JSON) | | `404` | No downloads available or session not found | | `401` | Invalid or missing API key | | `403` | Box not in key's scope | | `503` | Box is not running / bridge unreachable | --- ```bash curl -X DELETE \ "https://vessium.com/api/v1/runs/api-gqB11W56HMRGmLGf/downloads?boxId=feaaf263-..." \ -H "Authorization: Bearer vess_..." ``` | Parameter | Location | Required | Description | | ----------- | -------- | -------- | ---------------------------- | | `sessionId` | Path | Yes | Session ID | | `boxId` | Query | Yes | Box ID (for auth validation) | **Response:** ```json { "success": true, "sessionId": "api-gqB11W56HMRGmLGf" } ``` | Status | Condition | | ------ | ----------------------------------------- | | `200` | Files deleted | | `404` | No downloads available or already deleted | | `401` | Invalid or missing API key | | `403` | Box not in key's scope | | `503` | Box is not running / bridge unreachable | --- ```bash curl -o screenshot.png \ "https://vessium.com/api/v1/runs/api-gqB11W56HMRGmLGf/downloads/screenshot.png?boxId=feaaf263-..." \ -H "Authorization: Bearer vess_..." ``` | Parameter | Location | Required | Description | | ----------- | -------- | -------- | ----------------------------------- | | `sessionId` | Path | Yes | Session ID | | `filename` | Path | Yes | File name (no directory separators) | | `boxId` | Query | Yes | Box ID (for auth validation) | **Response:** Binary file stream with appropriate `Content-Type` and `Content-Disposition` headers. | Status | Condition | | ------ | --------------------------------------- | | `200` | File returned | | `400` | Invalid filename | | `404` | File not found | | `401` | Invalid or missing API key | | `403` | Box not in key's scope | | `503` | Box is not running / bridge unreachable | --- # Webhooks ## Overview When a `webhook` URL is provided in the [trigger request](/docs/runs), Vessium delivers POST requests to that URL at key points in the run lifecycle. Delivery is best-effort — one attempt, 10-second timeout, no retries. If your endpoint is down, the webhook is lost. Use [polling](/docs/runs) as a fallback for critical workflows. --- ## Lifecycle Events Each run with a webhook configured receives **two** deliveries — a start event and one terminal event: Sent when the agent begins processing the run. Run finished normally — includes the full response and optional downloads. Generation failed — error details included in the payload. Run was cancelled via the [abort endpoint](/docs/runs) or box shutdown. --- ## Payload ``` POST https://your-endpoint.com/hook Content-Type: application/json X-Vessium-Event: session.started X-Vessium-Request-Id: req_epxlIiSZcpfdUiUp ``` **Started:** ```json { "event": "session.started", "sessionId": "api-gqB11W56HMRGmLGf", "requestId": "req_epxlIiSZcpfdUiUp", "status": "started", "response": "", "messageCount": 1, "timestamp": "2026-02-11T05:01:40.000Z" } ``` **Complete:** ```json { "event": "session.complete", "sessionId": "api-gqB11W56HMRGmLGf", "requestId": "req_epxlIiSZcpfdUiUp", "status": "complete", "response": "You have 3 urgent emails...", "messageCount": 2, "downloads": { "fileCount": 1, "totalBytes": 24576, "files": [{ "name": "report.csv", "size": 24576 }], "exportUrl": "https://vessium.com/api/v1/runs/api-gqB11W56HMRGmLGf/downloads?boxId=feaaf263-...", "expiresAt": "2026-02-12T05:01:47.153Z" }, "timestamp": "2026-02-11T05:01:47.153Z" } ``` **Error:** ```json { "event": "session.error", "sessionId": "api-abc123", "requestId": "req_xyz", "status": "error", "response": "", "messageCount": 1, "error": "API Error: 503 Service temporarily overloaded", "timestamp": "2026-02-11T05:10:00.000Z" } ``` --- ## Event Reference | `status` | Event | Meaning | `response` | `error` | `downloads` | | ---------- | ------------------ | -------------------- | ---------------- | ------- | -------------- | | `started` | `session.started` | Generation has begun | Empty | No | No | | `complete` | `session.complete` | Finished normally | Full response | No | If files exist | | `error` | `session.error` | Generation failed | Partial (if any) | Yes | No | | `aborted` | `session.aborted` | Session was aborted | Partial (if any) | No | No | --- ## Headers | Header | Value | Purpose | | ---------------------- | ---------------------------------------------------------------------------- | -------------------------------------------- | | `Content-Type` | `application/json` | Always JSON | | `X-Vessium-Event` | `session.started`, `session.complete`, `session.error`, or `session.aborted` | Event type | | `X-Vessium-Request-Id` | `req_...` | Correlates with the original trigger request | --- ## Best Practices - **Always set up polling as a fallback.** Webhooks are best-effort with no retries. - **Respond quickly.** Return a 2xx within the 10-second timeout to avoid delivery failure. - **Use `requestId` for deduplication.** Match webhook events to your original trigger requests. - **Check the `downloads` field** on `session.complete` events to know if files are available for [download](/docs/downloads). --- # Examples Ready-to-use patterns for integrating with the Vessium API. All examples assume you have an [API key](/docs/authentication) and a running box. --- ## Fire-and-forget with webhook The simplest integration — trigger a run and let the webhook handle the result. No polling loop, no waiting. Perfect for kicking off tasks from Zapier, Make, or any webhook-capable platform. ```bash curl -X POST https://vessium.com/api/v1/runs \ -H "Authorization: Bearer vess_..." \ -H "Content-Type: application/json" \ -d '{ "boxId": "feaaf263-...", "message": "Triage today's unread emails and draft replies for the urgent ones", "webhook": "https://hooks.zapier.com/abc123" }' ``` --- ## Polling loop Trigger a run and poll until it completes. Good for shell scripts, CLI tools and environments where you can't receive webhooks. ```bash # Trigger RESPONSE=$(curl -s -X POST https://vessium.com/api/v1/runs \ -H "Authorization: Bearer vess_..." \ -H "Content-Type: application/json" \ -d '{"boxId":"...","message":"Check my calendar for today"}') SESSION_ID=$(echo $RESPONSE | jq -r '.sessionId') BOX_ID="..." # Poll until complete while true; do STATUS=$(curl -s "https://vessium.com/api/v1/runs/${SESSION_ID}?boxId=${BOX_ID}" \ -H "Authorization: Bearer vess_...") if [ "$(echo $STATUS | jq -r '.status')" = "complete" ]; then echo $STATUS | jq -r '.response' break fi sleep 10 done ``` --- ## Webhook + polling fallback This is the recommended pattern for production. Webhooks give you speed, polling gives you reliability. Provide a webhook for fast notification, but poll as a safety net in case the webhook delivery fails: ```javascript // Trigger with webhook const { sessionId } = await fetch('https://vessium.com/api/v1/runs', { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ boxId, message, webhook: 'https://my-app.com/hook', }), }).then((r) => r.json()) // Start polling fallback (in case webhook fails) const pollInterval = setInterval(async () => { const status = await fetch( `https://vessium.com/api/v1/runs/${sessionId}?boxId=${boxId}`, { headers: { Authorization: `Bearer ${apiKey}` } }, ).then((r) => r.json()) if (status.status === 'complete') { clearInterval(pollInterval) handleResult(status.response) } }, 15000) // Webhook handler (cancel polling when received) app.post('/hook', (req) => { clearInterval(pollInterval) handleResult(req.body.response) }) ``` --- ## Download output files When a run produces files (screenshots, exports, documents), download them after completion. The `downloads` field in the [poll response](/docs/runs) or [webhook payload](/docs/webhooks) tells you when files are available. ```bash # Check for downloads in the poll response STATUS=$(curl -s "https://vessium.com/api/v1/runs/${SESSION_ID}?boxId=${BOX_ID}" \ -H "Authorization: Bearer vess_...") # Download all files as ZIP curl -o session-files.zip \ "https://vessium.com/api/v1/runs/${SESSION_ID}/downloads?boxId=${BOX_ID}" \ -H "Authorization: Bearer vess_..." # Or list files first, then download individually curl -s "https://vessium.com/api/v1/runs/${SESSION_ID}/downloads?boxId=${BOX_ID}&format=json" \ -H "Authorization: Bearer vess_..." | jq '.files[].name' curl -o report.csv \ "https://vessium.com/api/v1/runs/${SESSION_ID}/downloads/report.csv?boxId=${BOX_ID}" \ -H "Authorization: Bearer vess_..." ```