Skip to main content

robovue-sdk

Report your bots' runs to Robovue, which watches every bot in one place and tells you when one fails or doesn't run. This package signs each report, retries when the network or Robovue has a problem, checks a report against Robovue's rules before sending it, and can send dry runs while you set it up. It uses only Python's standard library.

Install

pip install robovue-sdk

Requirements

  • Python 3.9 or later, on any operating system. No other packages.
  • A Robovue workspace. Signing up is free.

Can't install a package where your bot runs, such as Power Automate Desktop's built-in Python? Connections → HTTP API in Robovue gives you the same request as plain Python, C# and shell code to paste in, and the wire contract below describes it for any language.

Quickstart

  1. Sign up at robovue.co.uk.
  2. Copy your credentials from Settings → Connections → HTTP API: the webhook secret, and your tenant ID, which is the long id in the address shown there (https://app.robovue.co.uk/ingest/<tenant ID>/runs).
  3. Keep them out of your code. Set them as environment variables on the machine that runs the bot, ROBOVUE_TENANT_ID and ROBOVUE_WEBHOOK_SECRET, or in your platform's credential store, and create one client:
import os
import uuid
from datetime import datetime, timezone

from robovue_sdk import FailureCategory, RobovueApiException, RobovueClient, RunReport, RunStatus

# One client for the whole process: it's safe to share between threads.
robovue = RobovueClient(
    base_url="https://app.robovue.co.uk",
    tenant_id=os.environ["ROBOVUE_TENANT_ID"],
    webhook_secret=os.environ["ROBOVUE_WEBHOOK_SECRET"],
)
  1. Wrap your bot's job. Report the run as RUNNING when it starts, then as SUCCESS or FAILURE when it ends, with the same delivery id so Robovue knows it's one run:
delivery_id = str(uuid.uuid4())  # one per run: it ties the run's reports together
started_at = datetime.now(timezone.utc)

robovue.try_report_run(
    RunReport(
        bot_id="invoice-bot-01",
        platform="python",
        status=RunStatus.RUNNING,
        started_at=started_at,
    ),
    delivery_id=delivery_id,
)

try:
    process_invoices()  # your bot's work
except Exception as exc:
    robovue.try_report_run(
        RunReport(
            bot_id="invoice-bot-01",
            platform="python",
            status=RunStatus.FAILURE,
            started_at=started_at,
            completed_at=datetime.now(timezone.utc),
            failure_category=FailureCategory.APPLICATION_EXCEPTION,
            payload={"error": str(exc)},
        ),
        delivery_id=delivery_id,
    )
    raise

robovue.try_report_run(
    RunReport(
        bot_id="invoice-bot-01",
        platform="python",
        status=RunStatus.SUCCESS,
        started_at=started_at,
        completed_at=datetime.now(timezone.utc),
    ),
    delivery_id=delivery_id,
)

try_report_run never raises, so a problem reaching Robovue can't break your bot. The job's own exception is reported, then raised again, so the bot still fails the way it always did.

  1. Check it arrives. Run the bot once with dry_run on, then for real, and watch the report land in the Verify step on Connections → HTTP API, in the request log, and on Fleet.

Make missed runs alert

A failure alerts as soon as it's reported. A run that never happens can only be noticed if Robovue knows when to expect it, so in Manage bots, register the bot (its id is bot_id) and give it a schedule. Until a bot is registered, its runs appear under Unregistered ids, and each new id counts toward your plan's bot limit.

Reporting in more detail

Failure categories

On a FAILURE, set failure_category to say why it failed. It's shown on the run, filterable on Runs, and named in the alert. Leave it out if you don't know: Robovue records Uncategorized, and a run can be reclassified later from its page.

result = robovue.report_run(
    RunReport(
        bot_id="invoice-bot-01",
        platform="python",
        status=RunStatus.FAILURE,
        started_at=started_at,
        completed_at=datetime.now(timezone.utc),
        failure_category=FailureCategory.BUSINESS_EXCEPTION,
    )
)
Category Use it for
BUSINESS_EXCEPTION A business rule stopped the run: bad input data, a missing record, a rejected transaction.
APPLICATION_EXCEPTION Something broke in the bot or the application it drives.
TIMEOUT The run, or a step in it, took too long.
AUTHENTICATION_ERROR The bot couldn't sign in to something it needs.
CANCELLED A person or a scheduler stopped the run.
UNCATEGORIZED You don't know. The default.

Attaching context with payload

payload takes any JSON-serialisable value and is stored as sent, shown in full on the run's page. Use it for whatever would help you debug the run later: an invoice number, the step that failed, a stack trace. If it has an error, errorMessage or message key, the alert uses that text to say what went wrong.

result = robovue.report_run(
    RunReport(
        bot_id="invoice-bot-01",
        platform="python",
        status=RunStatus.FAILURE,
        started_at=started_at,
        completed_at=datetime.now(timezone.utc),
        failure_category=FailureCategory.TIMEOUT,
        payload={
            "invoiceId": "INV-2291",
            "error": "Timed out waiting for the vendor portal",
        },
    )
)

The whole report, payload included, must be under 64 KB.

Attaching a screenshot or log to a failed run

On the Team plan and above, you can attach one screenshot or log excerpt to a FAILURE run once it's reported. Screenshots are off for each bot until someone turns them on (Manage bots → Screenshots), because a screenshot can capture credentials or other sensitive data on screen.

robovue.upload_attachment(result.run_id, screenshot_bytes, "image/png")

content_type is "image/png", "image/jpeg" or "text/plain", up to 5 MB, one per run. Robovue deletes attachments after 30 days, or 7 or 14 if an Admin chose that. An upload for a bot with screenshots off, or on a plan without them, gets the same 404 as an unknown run, so catch it if a missing screenshot shouldn't fail your bot.

Reporting without ever raising

Monitoring shouldn't become a new way for a bot to fail. try_report_run never raises for a failed report: an invalid report, a rejection or a network problem comes back in the outcome for you to log.

outcome = robovue.try_report_run(report)
if not outcome.succeeded:
    print(f"Couldn't report the run to Robovue: {outcome.error}", file=sys.stderr)

When a report is rejected

report_run raises RobovueApiException. Branch on code, which is stable; the message is for people. field names the field at fault when there is one, and request_id is worth quoting if you contact support.

try:
    robovue.report_run(report)
except RobovueApiException as exc:
    if exc.code != "timestamp_out_of_range":
        raise
    # This machine's clock is more than 5 minutes out. The message gives Robovue's time.
    print(exc, file=sys.stderr)

Network errors and 5xx responses are retried automatically (3 attempts by default), and so is a 429 after its Retry-After, unless that's more than a minute, when the 429 is raised instead of blocking your bot. A report is checked against the server's own rules before anything is sent, so most mistakes fail at once with a ValueError. A datetime with no time zone is treated as UTC. Every code is in the responses table below, with whether a retry can help.

Checking a new integration with a dry run

With dry_run=True, every request is checked exactly as a real one would be (signature, fields, plan limits, delivery id) but nothing is stored. The result says what would have happened, and a request that would fail raises the real error.

robovue = RobovueClient(
    base_url="https://app.robovue.co.uk",
    tenant_id=tenant_id,
    webhook_secret=webhook_secret,
    dry_run=True,  # checked in full, stored nowhere
)

result = robovue.report_run(report)
print(result.dry_run, result.would_be)  # True created

Dry runs show in the request log, which warns you if any arrived in the last day, so a bot left in dry-run mode doesn't go unnoticed. Turn it off before going live.

Runs that take a while

The quickstart reports RUNNING first. That's optional, but it means a bot that starts a job and then hangs shows as Running straight away, rather than only when its next run fails to turn up. Reusing the delivery id is the whole mechanism: a fresh id on the second report makes two runs, one stuck Running and one that looks like nobody started it. A run left Running silences nothing: if it never completes, the bot still goes Late and then Overdue on its schedule.

Using it in your bot

One client per process. RobovueClient holds no state between calls, so share one instance across threads.

Timeouts, proxies and TLS. Each attempt times out after 30 seconds. Requests go through Python's urllib, so they use the proxy in HTTPS_PROXY (or, on Windows, the system's proxy settings) and the operating system's trusted certificates; set SSL_CERT_FILE to a certificate bundle if your network inspects TLS with its own certificate. For anything else, pass transport=: a function that takes the URL, a dict of headers and the body bytes, sends the POST, and returns (status, response_body, response_headers).

Options

RobovueClient(...) Default What it does
base_url required https://app.robovue.co.uk
tenant_id required Your workspace's tenant ID.
webhook_secret required Signs every request. Never put it in source code.
max_retry_attempts 3 Attempts per call, the first included.
retry_base_delay 0.2 Seconds before the first retry; each later wait doubles.
dry_run False Check every request in full and store nothing.
transport urllib How requests are sent (see above).

Troubleshooting

What you see What to do
invalid_signature The secret is wrong or was rotated. Copy the current one from Connections → HTTP API.
timestamp_out_of_range The machine's clock is more than 5 minutes out. Sync it (on Windows, w32tm /resync).
unknown_tenant The tenant ID is wrong. Copy it from the address on Connections → HTTP API.
bot_cap_reached A new bot id would go over your plan's bot limit. Delete a bot you no longer use, or change plan.
payload_too_large The report is over 64 KB. Send less in payload, and attach a log file instead.
A 403 with no code Something between your bot and Robovue blocked the request, such as a proxy rewriting it. Check with whoever runs your network, or send us the time it happened.
An OSError or URLError The machine can't reach app.robovue.co.uk. Check its proxy and firewall.
CERTIFICATE_VERIFY_FAILED Your network inspects TLS. Point SSL_CERT_FILE at a bundle that includes its certificate.
The report succeeds but nothing shows on Fleet dry_run is still on, the bot is archived (see Manage bots → Archived bots), or the credentials belong to another workspace.
A bot stopped running and nobody was told It has no schedule. See Make missed runs alert.

The request log, in Connections, shows every request from the last 7 days and the exact reason any was turned away.

The wire contract (any language)

This is exactly what RobovueClient sends. From a language with no Robovue SDK, send the same request yourself. The tables below are generated from the server's own contract, so they list every field, header, response and limit, including the optional ones.

POST /ingest/{tenantId}/runs

The endpoint both SDKs call, and the one to use from any other language. A JSON object with the fields below.

Headers

Header Value Description
Content-Type application/json The body is JSON. A charset parameter is fine.
X-Robovue-Delivery-Id 1–200 printable ASCII characters, no spaces Unique to this run. Sending it again — a retry, or the Success/Failure that ends a Running run — updates or confirms the same run instead of creating a second one. A GUID, or your bot's own run id, both work.
X-Robovue-Timestamp Unix time in whole seconds When you signed the request. It must be within 5 minutes of Robovue's clock either way, so a captured request stops working within minutes. Sign each attempt, retries included, just before sending it.
X-Robovue-Signature v1=<lowercase hex> HMAC-SHA256, keyed with your webhook secret, of the timestamp, the word run, the delivery id — each followed by a line break — then the exact request body; hex-encoded in lowercase and prefixed with v1=. Sign the exact bytes you send: re-serialising the JSON after signing changes them. Several comma-separated v1= values are accepted, and any one matching is enough.
X-Robovue-Dry-Run true Checks the request exactly as a real one — authentication, every field, the delivery id, your plan's bot limit — and stores nothing. The response says what would have happened. Use it to test an integration without creating runs. Any other value, or no header, is an ordinary request.

Body fields

Field Type Required Description
botId string, ≤ 200 chars Yes Your name for the bot. Every run with the same botId is the same bot on your dashboard. Spaces and punctuation are fine; not / or , no spaces at either end, and no tabs or line breaks. The same id registers and manages the bot through the Management API.
platform string, ≤ 50 chars Yes What runs the bot, shown beside it on the dashboard — any short label, such as dotnet, python, go or blue-prism. Same whitespace rules as botId.
status one of Success, Failure, Running Yes How the run went. Running reports a run that has started and not finished yet; send the same delivery id again with Success or Failure when it ends. Values are case-sensitive.
startedAt timestamp Yes When the run started, as an RFC 3339 timestamp with a time zone offset — Z for UTC or ±hh:mm, e.g. 2026-07-17T09:00:00Z. Not before 2000, and not more than an hour ahead of Robovue's clock. Unix times aren't accepted.
completedAt timestamp Required when status is Success or Failure; leave it out when status is Running When the run finished. Same format and limits as startedAt, and not before it.
failureCategory one of Uncategorized, BusinessException, ApplicationException, Timeout, AuthenticationError, Cancelled No — only with status Failure Why the run failed, if your bot can tell. Leave it out if not — it becomes Uncategorized and can be changed later on the run's page. Values are case-sensitive.
payload object No Anything that will help you debug the run later — an invoice number, the step that failed, a stack trace. Must be a JSON object; it's stored as sent and shown on the run's page. A top-level error, errorMessage or message string becomes the summary in the failure alert. Counts toward the 64 KB request limit.

Responses

Every error body is {"error": "…", "code": "…", "field": "…", "requestId": "…"} — field only when the error is about one field — and every response carries an X-Request-Id header.

Status Code Meaning Retry?
201 — A new run was recorded. Body: {"runId": "…", "duplicate": false} No need — nothing more to send
200 — The run was already known. duplicate is false when this completed a run reported as Running, and true when it repeated a report already recorded — a retry, which is safe. No need — nothing more to send
200 — A dry run passed every check. Body: {"dryRun": true, "wouldBe": "created"} — or "completed" or "duplicate". Nothing was stored. No need — nothing more to send
400 invalid_body The body isn't a single valid JSON object. The message gives the line and position. No — fix the request first
400 duplicate_field A field appears twice. No — fix the request first
400 unknown_field A field isn't one of the documented names. Names are case-sensitive; put your own data inside payload. No — fix the request first
400 missing_field A required field is missing, blank or null. field names it. No — fix the request first
400 invalid_field A field's value breaks its rule. field names it and the message says how. No — fix the request first
400 missing_delivery_id X-Robovue-Delivery-Id is missing. No — fix the request first
400 invalid_delivery_id X-Robovue-Delivery-Id is longer than 200 characters, or has spaces or non-ASCII characters. No — fix the request first
400 bot_cap_reached This would be a new bot and your plan's bot limit is reached. Archive and delete a bot you no longer use, or raise the limit on the Billing page. No — fix the request first
401 missing_signature X-Robovue-Signature is missing. No — fix the request first
401 missing_timestamp X-Robovue-Timestamp is missing. No — fix the request first
401 invalid_timestamp X-Robovue-Timestamp isn't whole Unix seconds — milliseconds are the usual cause. No — fix the request first
401 timestamp_out_of_range X-Robovue-Timestamp is more than 5 minutes from Robovue's clock. The message gives the server's time; fix the sending machine's clock. Yes, once re-signed with the current time
401 invalid_signature The signature doesn't match. Check the secret, and that you sign the timestamp, the delivery or run id and the exact bytes you send. No — fix the request first
404 unknown_tenant No workspace has this tenant id. Copy the URL from your Connect page. No — fix the request first
409 delivery_id_conflict The delivery id already belongs to a different bot's run. Reuse a delivery id only for the same bot's run. No — fix the request first
413 payload_too_large The body is over 64 KB. No — fix the request first
415 unsupported_media_type Content-Type isn't application/json. No — fix the request first
429 rate_limited Too many requests. Retry-After says how many seconds to wait. Yes, after the Retry-After seconds
500 internal_error Something went wrong on Robovue's side. Quote the requestId if it keeps happening. Yes, with backoff — the same delivery id makes it safe

Limits

Limit Value Notes
Run report size 64 KB Per request, payload included. Anything larger is rejected with 413 — it isn't truncated.
Run reports 600 per minute per workspace Shared by the run report, Power Automate and UiPath endpoints. A start and its completion count as two.
Attachment size 5 MB One file per failed run: PNG, JPEG or UTF-8 text.
Attachment uploads 50 per hour per workspace Across all bots.
Attachment storage 500 MB per workspace Attachments are deleted after the workspace's retention, 30 days at most, which frees their space.
Management API 300 requests per minute per workspace Every /tenants/… request.
Per address 3,000 requests per minute per client IP Across ingestion and the Management API together, for every workspace sending from that address.

Signing, in one line: "v1=" + hex(HMAC-SHA256(key: webhook secret, message: timestamp + "\nrun\n" + delivery id + "\n" + the exact body bytes)), lowercase, sent with the same timestamp in X-Robovue-Timestamp (Unix seconds). Sign each attempt, retries included, just before sending it. Sign the bytes you actually send: re-serialising the JSON after signing (a different key order, different whitespace) changes them and the request is rejected with invalid_signature. Connections → HTTP API has a signature checker to compare yours against.

The attachment upload (POST /ingest/{tenantId}/runs/{runId}/attachment) uses the same scheme with the word attachment in place of run and the run id (lowercase, hyphenated) in place of the delivery id, computed over the raw file bytes instead of a JSON body. It has no delivery-id header: one attachment per run is what makes it safe to retry.

This scheme is only for the /ingest/{tenantId}/runs endpoint. UiPath Orchestrator, Power Automate and Automation Anywhere connect to Robovue without code; Connections in Robovue sets each one up.

Versions and support

Licence

MIT, so you can use, change and ship this package freely (see LICENSE in the package). Using the Robovue service is covered by the terms.

Metadata

Release files for robovue-sdk 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for robovue-sdk 0.2.0
File Size Uploaded
robovue_sdk-0.2.0.tar.gz 19.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for robovue-sdk 0.2.0
File Interpreter ABI Platform
robovue_sdk-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 40.8 kB

Release files / robovue_sdk-0.2.0.tar.gz

Download URL robovue_sdk-0.2.0.tar.gz
Size 19.7 kB
Tags Source
SHA-256 checksum
How to use checksums
c692bec05408041cc0c28ddbdf7d0c01318e5798e5e28e8e20c74aacce5bd47c
BLAKE2b-256 checksum
How to use checksums
42f4fd11c27e103eb3408951b3b90019e0e7e2b1116dcd47b8de945769c095c0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / robovue_sdk-0.2.0-py3-none-any.whl

Download URL robovue_sdk-0.2.0-py3-none-any.whl
Size 21.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
49fcbc462d7e4930c7dd5eb4c4fbd6b4ddc82b5f83f3c757d9cc981b4fa675ee
BLAKE2b-256 checksum
How to use checksums
5dd87dbba72c4ea763899ad0afe6008c30c435c23c88bb71563b2f885882aae9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page