Connect your endpoint
Keep weights on your own infrastructure — the facility streams observations to your HTTPS URL and logs round-trip time per step.
Your endpoint
Endpoint mode inverts the trust arrangement of the other two packagings: your weights never enter the facility. You host inference; the cell streams observations to your URL and expects actions back, per step, on a declared latency budget. What you give up is the network — it is now inside the control loop — and much of this page is about being honest with yourself about that.
Prerequisites
- An HTTPS inference URL reachable from the facility's egress.
- Capacity for the declared action rate.
joint_delta_50hzis an action every 20 ms, sustained for every episode of the run — provision for the whole job, not for a demo. - Your own version identity. Put the deployment version in the URL
path or hostname —
https://inference.acme.ai/v7/act— because the run records the URL. That is how a result stays attributable to a deployment: ifv7andv8share one URL behind a load balancer, no artifact can say which one earned the rate.
The protocol, honestly
The policy object carries three fields — url, latency_budget_ms,
fallback — and nothing else. There are no auth headers, bearer
tokens, or mTLS fields today. Treat the URL as the credential: make it
unguessable and per-run
(https://inference.acme.ai/v7/act/8f3a2c91d4e6), rotate it after the
run, and enforce your own network controls — allowlists, WAF rules — on
your side. Do not assume any facility-side security guarantee beyond
"the URL is stored with the run".
The interface itself is behavioral: the cell runtime feeds your server
the observations named by the declared observation_contract and
expects actions in the declared action_space, at its declared rate.
Between episodes the runtime signals an episode boundary, and your
server clears its per-episode state — physical scene reset is the
cell's job (scripted or teleop-assisted), never your policy's. A formal
wire-level spec — routes and schemas — is not yet published.
What leaves the cell
Exactly the feeds named by the declared observation_contract, plus
the proprioception schema — per step, to your URL. droid-3cam means
three RGB views and joint state, and nothing else. Round-trip time is
logged per step to /events. In return, nothing is cached: endpoint
mode creates no Roborama copy of your weights at all — the same IP
posture the data contract applies to
your episodes.
Latency and the fallback
Your network is in the loop, so the budget is explicit:
latency_budget_ms=80 means any step whose round trip exceeds 80 ms
triggers the declared fallback. "halt" freezes the arm and ends the
episode, which lands in its own cluster instead of polluting the
physics statistics; "teleop" is the other documented value.
Persistent unreachability fails the run with policy_endpoint_timeout.
Budget from your p99, not your median — the
policies page shows the arithmetic of a
40 ms median with a 200 ms p99, and it is not on your side.
Connect
import roborama # reads ROBORAMA_API_KEY from the environment
policy = roborama.Policy.endpoint(
url="https://inference.acme.ai/v7/act",
latency_budget_ms=80,
fallback="halt",
)
run = roborama.run(
robot="g1-edu-pro@fw2.3",
environment="kitchen-std@v1.2",
policy=policy,
task="load_dishwasher@v2",
episodes="auto(ci=0.95, moe=0.03)",
max_budget_usd=4000,
)
result = run.result()
# per-step endpoint RTT is logged to /events for this run
print(run.id, result.success_rate, result.ci95)Validate
Local first: ROBORAMA_MOCK=1 exercises the full loop against packaged
fixtures — no API key, no charge.
import roborama
# Mock mode: no API key, no hardware. The whole loop runs
# offline against packaged fixtures — or set ROBORAMA_MOCK=1.
client = roborama.Client(mock=True)
quote = client.quote(
robot="aloha2-pro",
environment="cell-a",
episodes=300,
)
print(quote)
# {robot_hours: 10.0, env_hours: 10.0, usd: 860, queue_eta: "2h"}
run = client.run(
robot="aloha2-pro@fw1.1",
environment="cell-a@v1.0",
policy=roborama.Policy.checkpoint(
hf="acme/act-so101-pick-v2",
runtime="lerobot",
),
task="pick_place@v1",
episodes=300,
max_budget_usd=1000,
)
print(run.result())
# n=300 rate=0.843 ci95=(0.797, 0.881)At submission, the declared contracts are validated server-side; a
mismatch is contract_validation_failed (422), and a rejected
submission costs nothing. Then, at cell startup, the runtime performs a
reachability exercise against your URL before episode 1: sustained
failure lands as policy_endpoint_timeout, and no episodes bill. A
dead DNS record costs you a queue slot, not money.
Quote
Endpoint runs meter like any other — robot-hours and environment-hours; your inference compute stays on your own bill. Quote first, and read the queue ETA as a capacity question: can the deployment hold the declared rate for that long? (billing & budgets)
import roborama # reads ROBORAMA_API_KEY from the environment
quote = roborama.quote(
robot="g1-edu-pro",
environment="kitchen-std",
episodes=600,
)
print(quote)
# {robot_hours: 20.0, env_hours: 20.0, usd: 3740, queue_eta: "6h"}Evaluate
Submission is an ordinary run(). Watching
live matters more here than in the other packagings — per-step RTT
lands in /events as it happens, so you can catch a saturating
deployment while the run is still cheap to stop:
import roborama # reads ROBORAMA_API_KEY from the environment
run = roborama.runs.get("run_8842")
for event in run.watch(): # live: episode ticker + WebRTC stream URLs
print(event.episode, event.status)
stream = roborama.streams.get("cell-g1-04")
print(stream)
# {webrtc: "wss://streams.roborama.com/cells/cell-g1-04/webrtc",
# mjpeg: "https://streams.roborama.com/cells/cell-g1-04/mjpeg",
# viewer_token: "vt_7Kq2mHentXw4"}Inspect results
Alongside n, the rate, and the Wilson interval, the episode record is
where endpoint mode's questions get answered: halted episodes carry
their own cluster, and the /events channel holds the per-step RTTs,
so a latency problem and a policy problem are distinguishable after the
fact. run.report(format="pdf") cites the same record — channels and
retention are in the data contract.
Compare versions
v7 against v8 is two URLs under the same declared configuration —
which is why the version belongs in the URL. Submit both to
compare() for paired episodes: same
scenes for both deployments, roughly half the episodes, and a verdict
with an interval on the difference.
Troubleshoot
| Code | Symptom | Fix |
|---|---|---|
policy_endpoint_timeout | run halted — steps persistently over latency_budget_ms, or the startup reachability exercise failed | budget from your p99, not your median; provision capacity for the declared rate sustained, not burst; fix the endpoint, then resubmit |
contract_validation_failed | 422 at submission; nothing ran | the declared action_space or observation_contract doesn't match the pinned robot and scene |
estop_triggered | run stopped by a hardware or safety emergency stop | the episode is marked estop, the cell is inspected, partial results are kept — contact support if unexpected |
budget_exceeded | run halted at max_budget_usd; partial results kept | raise the cap and resubmit if the interval is too wide |
Where next
- Policies — the three packagings, and the latency callout this page keeps pointing at.
- Data contract — the IP posture endpoint mode is built on.
- Run your container — the opposite trade: ship the image, take the network out of the loop.
- Error codes —
policy_endpoint_timeoutin the full catalogue.