CUA API Reference
Computer Use Agent (CUA) — human-in-the-loop browser automation for EU government portals. Start sessions, stream real-time actions, confirm or rollback steps.
Overview
The CUA API enables automated interaction with EU government portals (e.g., TED, Simap, national procurement platforms) through a supervised browser agent. Every action requires human confirmation before execution, ensuring full auditability and control.
Base URL
https://staging.pauhu.eu
The base URL is configurable via the VITE_CUA_API environment variable in client applications.
Workflow
- Start a session targeting a specific portal and task.
- Stream real-time events via Server-Sent Events (SSE).
- Confirm or reject each pending action before it executes.
- Rollback to a previous step if needed.
- Stop the session when the task is complete or abandoned.
Authentication
All endpoints require authentication. The CUA API supports the same authentication methods as the main Pauhu API:
| Method | Header | Example |
|---|---|---|
| JWT (EdDSA) | Authorization: Bearer <token> |
Issued by the Pauhu auth endpoint |
| API Key | Authorization: Bearer pk_* |
Generated from the API Keys dashboard |
The free tier (3 requests/day) applies. Higher tiers unlock additional session concurrency and action throughput.
POST /v1/cua/start
Start a new CUA session targeting a government portal.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
portal | string | Yes | Target portal domain (must be allowlisted) |
task | string | Yes | Human-readable description of the task |
url | string | No | Starting URL within the portal |
Example request
curl -X POST https://staging.pauhu.eu/v1/cua/start \
-H "Authorization: Bearer pk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"portal": "ted.europa.eu",
"task": "Fill procurement notice form CN-2024-12345",
"url": "https://ted.europa.eu/en/notice/-/detail/123456"
}'
Example response
{
"session_id": "cua_abc123",
"status": "running",
"portal": "ted.europa.eu",
"stream_url": "/v1/cua/stream?session=cua_abc123"
}
| Field | Type | Description |
|---|---|---|
session_id | string | Unique session identifier (prefixed cua_) |
status | string | Session status: running |
portal | string | Confirmed target portal |
stream_url | string | Relative URL for the SSE event stream |
POST /v1/cua/action
Manually trigger a CUA action within a running session.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
session_id | string | Yes | Session identifier |
type | string | Yes | Action type (see below) |
target | string | No | CSS selector of the target element |
description | string | No | Human-readable description of the action |
coordinates | object | No | { "x": number, "y": number } — viewport coordinates |
value | string | null | No | Value for type and select actions |
Action types
| Type | Description |
|---|---|
click | Click on an element or coordinates |
type | Type text into an input field |
scroll | Scroll the viewport |
navigate | Navigate to a URL |
select | Select an option from a dropdown |
submit | Submit a form |
wait | Wait for a condition or timeout |
Example request
curl -X POST https://staging.pauhu.eu/v1/cua/action \
-H "Authorization: Bearer pk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"session_id": "cua_abc123",
"type": "click",
"target": "#submit-button",
"description": "Click the submit button",
"coordinates": { "x": 450, "y": 320 },
"value": null
}'
POST /v1/cua/screenshot
Upload a viewport screenshot for the current session. Uses multipart/form-data encoding.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
session_id | string | Yes | Session identifier |
screenshot | file | Yes | PNG image, max 5 MB |
Example request
curl -X POST https://staging.pauhu.eu/v1/cua/screenshot \
-H "Authorization: Bearer pk_your_api_key" \
-F "session_id=cua_abc123" \
-F "screenshot=@viewport.png"
Example response
{
"screenshotUrl": "https://..."
}
GET /v1/cua/stream
Subscribe to a Server-Sent Events (SSE) stream for real-time session updates.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
session | string | Yes | Session identifier |
Example (curl)
curl -N "https://staging.pauhu.eu/v1/cua/stream?session=cua_abc123" \
-H "Authorization: Bearer pk_your_api_key" \
-H "Accept: text/event-stream"
Event types
| Event | Description |
|---|---|
action_pending | An action is awaiting human confirmation |
action_executing | An approved action is being performed |
action_completed | An action finished successfully |
action_failed | An action failed (includes error message) |
session_update | Session status changed |
form_confirmation | Form fields are ready for human review |
screenshot | A new screenshot is available |
Event payload
{
"type": "action_pending",
"sessionId": "cua_abc123",
"action": {
"id": "act_001",
"type": "click",
"status": "pending",
"target": "#procurement-type",
"description": "Select procurement type",
"coordinates": { "x": 200, "y": 150 },
"screenshotUrl": "https://...",
"timestamp": "2026-03-11T10:00:00Z"
}
}
JavaScript example
const source = new EventSource(
'https://staging.pauhu.eu/v1/cua/stream?session=cua_abc123',
// Note: EventSource does not support custom headers natively.
// Use a library like eventsource-polyfill or fetch-event-source
// to pass Authorization headers.
);
source.addEventListener('action_pending', (event) => {
const data = JSON.parse(event.data);
console.log('Action pending:', data.action.description);
// Show confirmation UI to the user
});
source.addEventListener('action_completed', (event) => {
const data = JSON.parse(event.data);
console.log('Action completed:', data.action.id);
});
source.addEventListener('action_failed', (event) => {
const data = JSON.parse(event.data);
console.error('Action failed:', data.action.id, data.action.error);
});
source.addEventListener('screenshot', (event) => {
const data = JSON.parse(event.data);
document.getElementById('viewport').src = data.screenshotUrl;
});
source.addEventListener('form_confirmation', (event) => {
const data = JSON.parse(event.data);
console.log('Form ready for review:', data.fields);
});
source.onerror = () => {
console.error('SSE connection lost, reconnecting...');
};
POST /v1/cua/confirm
Confirm or reject a pending action. Every action must be explicitly approved before it executes.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
session_id | string | Yes | Session identifier |
action_id | string | Yes | Action identifier from the action_pending event |
approved | boolean | Yes | true to execute, false to reject |
edits | object | No | Field overrides before execution (e.g., correcting a value) |
Example — approve an action
curl -X POST https://staging.pauhu.eu/v1/cua/confirm \
-H "Authorization: Bearer pk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"session_id": "cua_abc123",
"action_id": "act_001",
"approved": true
}'
Example — approve with edits
curl -X POST https://staging.pauhu.eu/v1/cua/confirm \
-H "Authorization: Bearer pk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"session_id": "cua_abc123",
"action_id": "act_001",
"approved": true,
"edits": { "procurement_type": "open_procedure" }
}'
Example — reject an action
curl -X POST https://staging.pauhu.eu/v1/cua/confirm \
-H "Authorization: Bearer pk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"session_id": "cua_abc123",
"action_id": "act_001",
"approved": false
}'
POST /v1/cua/rollback
Rollback a session to a previous step. All actions after the target step are undone.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
session_id | string | Yes | Session identifier |
to_step | integer | Yes | Step number to rollback to (1-based) |
Example request
curl -X POST https://staging.pauhu.eu/v1/cua/rollback \
-H "Authorization: Bearer pk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"session_id": "cua_abc123",
"to_step": 3
}'
POST /v1/cua/stop
Stop a running CUA session. The session will be terminated and resources released.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
session_id | string | Yes | Session identifier |
Example request
curl -X POST https://staging.pauhu.eu/v1/cua/stop \
-H "Authorization: Bearer pk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"session_id": "cua_abc123"
}'
GET /v1/cua/stats
Retrieve dashboard statistics for CUA portal activity.
Example request
curl https://staging.pauhu.eu/v1/cua/stats \
-H "Authorization: Bearer pk_your_api_key"
Example response
{
"portals": [
{
"portal": "ted.europa.eu",
"activeSessions": 2,
"totalActions": 145,
"successRate": 94.5,
"lastActivity": "2026-03-11T10:30:00Z"
}
]
}
| Field | Type | Description |
|---|---|---|
portal | string | Portal domain |
activeSessions | integer | Currently running sessions |
totalActions | integer | Total actions performed |
successRate | number | Success percentage (0–100) |
lastActivity | string | ISO 8601 timestamp of last activity |
Error Codes
| Status | Code | Description |
|---|---|---|
| 400 | Bad Request | Invalid or missing request fields |
| 401 | Unauthorized | Missing or invalid authentication credentials |
| 403 | Forbidden | Portal is not on the allowlist |
| 404 | Not Found | Session not found or expired |
| 409 | Conflict | Action conflict (e.g., confirming an already-executed action) |
| 413 | Payload Too Large | Screenshot exceeds 5 MB limit |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Unexpected server error |
Error response shape
{
"error": {
"code": 403,
"message": "Portal not allowlisted: example.com",
"request_id": "req_abc123"
}
}
Support
Technical: support@pauhu.eu
Sales: sales@pauhu.eu