API reference
Acest conținut nu este încă disponibil în limba selectată.
The razorBridge REST API is public: joining events, reading event blades and their SSH credentials, the credit ledger, and razorBridge AI Gateway are all available to your own code. Self-serve GPU sessions are the exception — they are started and managed only in the web app.
Base URL
Section titled “Base URL”https://api.razorbridge.eu/v1A request to the bare root of that host returns a small JSON index of the main
endpoint groups. There is a staging deployment at
https://api.staging.razorbridge.eu/v1 with separate data.
Two conventions to note before you start:
- Paths under
/events/,/blades/,/credits/,/gpu-types/and/auth/end in a slash. The/gate/…paths do not. Getting this wrong produces a redirect or a 404. - Every response is JSON. Errors are
{"error": "<message>"}, except onPOST /gate/chat/completions: a disallowed model, a streaming request or a malformed field there returns the OpenAI error shape ({"error": {"message": "…", "type": "…"}}), and an upstream provider’s error body may be passed through unchanged.
Authentication
Section titled “Authentication”There are two kinds of credential, both presented as Authorization: Bearer <…>.
| Credential | Obtained from | Valid for |
|---|---|---|
| Session token | The device flow below | Every endpoint except the two marked AI Gateway key |
| AI Gateway key | POST /gate/keys or the web app |
POST /gate/chat/completions and GET /gate/models only |
Presenting the wrong kind is a 401, which is the usual cause of an unexpected
Invalid API key.
Session access tokens expire after 1 hour; refresh tokens after 30 days.
The device flow
Section titled “The device flow”Made for command-line and headless clients: the client never sees a password.
POST /auth/device/ (no authentication)Response:
| Field | Notes |
|---|---|
device_code |
Secret. Poll with this |
user_code |
8 characters. Show it to the user to type in the browser |
verification_uri |
Where the user goes to approve |
expires_in |
900 — the code is good for 15 minutes |
interval |
5 — suggested seconds between polls |
curl -X POST https://api.razorbridge.eu/v1/auth/device/Then poll, sending the device_code:
POST /auth/token/ (no authentication){"device_code": "…"}| Status | Meaning |
|---|---|
200 |
Approved. Returns access_token, refresh_token, token_type, expires_in |
428 |
authorization_pending — keep polling |
410 |
The code expired. Start again |
404 |
Unknown device_code |
The user must be signed in to razorBridge and type the user_code on the
Authorize razorBridge CLI page, at
https://razorbridge.eu/hub/compute/auth/verify/. Approving creates their
account, and its €10 signup credit, if they did not already have one.
A complete sign-in from a shell, using jq:
curl -s -X POST https://api.razorbridge.eu/v1/auth/device/ > device.jsonjq -r .user_code device.json # type this at razorbridge.eu/hub/compute/auth/verify/
# after approving in the browser:curl -s -X POST https://api.razorbridge.eu/v1/auth/token/ \ -H "Content-Type: application/json" \ -d "{\"device_code\": \"$(jq -r .device_code device.json)\"}" > token.jsonexport RB_TOKEN=$(jq -r .access_token token.json)Keep token.json private: its refresh_token renews the session for 30 days.
Refresh, revoke, identity
Section titled “Refresh, revoke, identity”POST /auth/refresh/ {"refresh_token": "…"} → access_token, token_type, expires_inPOST /auth/revoke/ Bearer session token → {"revoked": true, "sessions_revoked": n}GET /auth/me/ Bearer session tokenPOST /auth/revoke/ revokes every session belonging to the caller, not just
the token presented — it is a sign-out-everywhere, and the right response to a
leaked token. A token that is already invalid gets {"revoked": false}.
GET /auth/me/ returns:
{ "user": {"id": 1, "email": "you@example.org", "name": "Your Name"}, "account": {"id": "…", "name": "Your Account", "balance_eur": "10.0000"}}Monetary values are strings throughout the API, to avoid float rounding.
Events
Section titled “Events”GET /events/
Section titled “GET /events/”Returns two lists, because your relationship to an event changes what you can see:
{ "participating": [{"id": "…", "name": "…", "slug": "…", "status": "running", "starts_at": "…", "ends_at": "…"}], "organizing": [{"id": "…", "name": "…", "slug": "…", "status": "scheduled", "starts_at": "…", "ends_at": "…", "join_code": "ABCD-1234"}]}join_code appears only for events you organize — it is the credential that lets
anyone join.
POST /events/join/
Section titled “POST /events/join/”{"code": "ABCD-1234"}Codes are upper-cased server-side, so case does not matter. Returns
{"joined": true, "event": {"id", "name", "slug"}}.
| Status | Meaning |
|---|---|
404 |
Event not found |
409 |
Registration closed, Already registered, or Event is full |
429 |
More than 20 join attempts from your IP in an hour |
GET /events/<slug>/
Section titled “GET /events/<slug>/”Detail for one event: id, name, slug, status, starts_at, ends_at,
gpu_type, is_organizer, and blade — null until yours exists, otherwise
{id, status, ip_address, gpu_type, time_remaining_seconds}. Organizers also get
join_code.
Returns 403 Not your event to anyone who is neither a participant nor the
organizing account, so events cannot be enumerated by slug.
POST /events/<slug>/extend/
Section titled “POST /events/<slug>/extend/”Organizer only. Extends a running event:
{"minutes": 60}minutes must be 30, 60 or 120. Returns {"extended": true, "new_ends_at", "estimated_cost_eur"}. A non-running event gives 409 Can only extend running events; someone else’s event gives 404.
POST /events/<slug>/roster/
Section titled “POST /events/<slug>/roster/”Organizer only. Adds participants in bulk:
{"participants": [{"email": "alice@university.edu", "name": "Alice"}, {"email": "bob@university.edu"}]}email is required; name defaults to the local part of the address. Returns
{"created": n, "skipped": n}. Entries are skipped when they duplicate an existing
participant, have no @, or would exceed the event’s participant cap — so
re-posting the same roster is safe.
AI Blades
Section titled “AI Blades”These endpoints cover event blades — blades assigned to you as an event participant. Self-serve sessions do not appear here; their SSH details are on the session’s row in the web app.
GET /blades/
Section titled “GET /blades/”{"blades": [{"id": "…", "event": "ML Workshop", "gpu_type": "gpu-l40sx1-48gb", "status": "running", "ip_address": "…", "started_at": "…"}]}GET /blades/<blade_id>/
Section titled “GET /blades/<blade_id>/”Adds region, runtime_hours and, for event blades, time_remaining_seconds
derived from the event’s end time. 403 Not your blade unless you are the assigned
participant or the organizing account.
GET /blades/<blade_id>/ssh/
Section titled “GET /blades/<blade_id>/ssh/”The connection credentials:
{"host": "…", "port": 2201, "username": "user01", "password": "…", "ssh_command": "ssh -p 2201 user01@…"}Only the assigned participant may read this — the organizing account may not.
409 Credentials not yet assigned means the blade is not ready. The first
successful read is recorded against the participant.
Credits
Section titled “Credits”GET /credits/
Section titled “GET /credits/”{ "balance_eur": "8.4300", "transactions": [ {"id": "…", "type": "debit", "amount_eur": "1.4444", "source": "consumption", "balance_after": "8.4300", "description": "…", "created_at": "…"} ]}Up to 50 entries, newest first. type is credit or debit; source values are
listed in Credits and billing.
GPU types
Section titled “GPU types”GET /gpu-types/
Section titled “GET /gpu-types/”Available GPUs with the current published pricing, cheapest first:
{"gpu_types": [{"slug": "gpu-l40sx1-48gb", "display_name": "L40S", "vram_gb": 48, "best_for": "Training & workshops", "hourly_usd": "…", "hourly_eur": "…"}]}hourly_eur is the authoritative rate, per GPU-hour, per blade. hourly_usd is a
convenience conversion at a fixed reference rate, not a price you can pay in
dollars.
This endpoint needs a session token, which also makes it the CLI’s connectivity probe: any HTTP response, including 401, proves the API is up.
razorBridge AI Gateway
Section titled “razorBridge AI Gateway”AI Gateway is an OpenAI-compatible surface. For SDK usage, point the client’s
base_url at https://api.razorbridge.eu/v1/gate and let it append the rest.
POST /gate/chat/completions — AI Gateway key
Section titled “POST /gate/chat/completions — AI Gateway key”Body and response follow the OpenAI chat-completions shape; model is required.
The provider’s successful response is returned unchanged.
| Status | Meaning |
|---|---|
400 |
Invalid JSON, or model required |
401 |
Missing, unknown or revoked AI Gateway key |
402 |
Insufficient credits, or Key budget limit exceeded |
403 |
Model '…' is not allowed for this key |
502 |
Inference failed upstream |
503 |
Inference temporarily unavailable, or the requested model has no available route |
Streaming is not supported: "stream": true returns 400. Before a call is
dispatched, its maximum possible cost is held from the account’s available
credit, so 402 can occur when the balance is low even if the actual cost would
have been smaller. Each successful call writes a usage record and deducts its
actual cost from the account’s balance.
GET /gate/models — AI Gateway key
Section titled “GET /gate/models — AI Gateway key”{ "models": [{"id": "klusai/fast", "owned_by": "razorbridge"}], "object": "list", "data": [{"id": "klusai/fast", "object": "model", "owned_by": "razorbridge", "created": 1767225600}]}object and data follow the OpenAI models-list shape, so SDK model listing
works; models is the same list in the older short form. Model ids are aliases in the form klusai/<name>. The list reflects what the
platform routes today, so read it rather than caching a catalogue.
POST /gate/test — session token
Section titled “POST /gate/test — session token”A convenience wrapper for trying AI Gateway before you have a key:
{"model": "klusai/fast", "message": "Hello"}model defaults to klusai/fast; message defaults to Hello; a full messages
array may be sent instead. Billed exactly like a normal call, and returns 402
when available credit cannot cover it. Streaming is not available here.
GET /gate/usage — session token
Section titled “GET /gate/usage — session token”{"usage": [{"id": "…", "model": "klusai/fast", "prompt_tokens": 12, "completion_tokens": 48, "cost_eur": "0.000123", "created_at": "…"}]}Up to 50 entries, newest first.
GET /gate/keys and POST /gate/keys — session token
Section titled “GET /gate/keys and POST /gate/keys — session token”GET lists active keys: id, name, key_prefix, created_at, last_used_at.
POST creates one:
{"name": "Lab notebook", "allowed_models": ["klusai/fast"]}Returns 201 with the full key, which is shown only in this response —
afterwards only the prefix is retrievable. allowed_models is optional and is the
only way to scope a key to specific models; omit it for unrestricted access. A
per-key euro budget cap exists and is enforced, but cannot be set through this
endpoint.
POST /gate/keys/<key_id>/revoke — session token
Section titled “POST /gate/keys/<key_id>/revoke — session token”Returns {"revoked": true}, or 404 Key not found if the id does not belong to
your account.
Status codes
Section titled “Status codes”| Code | When |
|---|---|
400 |
Malformed JSON or a missing/invalid field |
401 |
Missing, wrong-type, expired or revoked credential |
402 |
Out of credits, or a key budget exhausted |
403 |
Authenticated, but not yours — someone else’s event, blade, or a disallowed model |
404 |
Not found, or deliberately indistinguishable from not-yours |
405 |
Wrong HTTP method |
409 |
State conflict — registration closed, event full, already registered, credentials not ready, event not running |
410 |
Device code expired |
428 |
Device authorization still pending — keep polling |
429 |
Rate limit exceeded |
502 |
Upstream inference failure |
503 |
Inference temporarily unavailable, or no route for the requested model |
Rate limits
Section titled “Rate limits”Applied per client IP, in a fixed window, to the unauthenticated and abuse-prone endpoints:
| Endpoint | Limit |
|---|---|
POST /auth/device/ |
10 per hour |
POST /auth/token/ |
20 per hour |
POST /auth/refresh/ |
30 per hour |
POST /events/join/ |
20 per hour |
Exceeding one returns 429 with Too many requests. Please slow down and try again later. Other endpoints are not rate-limited today; do not design against
that.
Related pages
Section titled “Related pages”- Quickstart — self-serve sessions in the web app.
- razorBridge AI Gateway — SDK examples and key scoping.
- Troubleshooting — what a given error usually means in practice.