cloudrundocs

API

Manage your Hermes engines and agents over HTTP — the same API the UI runs on.

API

Everything the cloudrun UI does goes through this API — it is not a separate facade, so what you can click you can curl. Platform resources (projects, engines, templates, your organization, API keys) have their own namespaces; engine operations are namespaced by engine kind, and today that means Hermes:

  • /api/projects, /api/templates, /api/org, /api/keys, … — the platform
  • GET /api/engines/{engineId} — what an engine is, whatever its kind
  • /api/hermes/{engineId}/… — every Hermes operation

Authentication

Two credentials work on every endpoint:

  • An API key — create one in Settings → API keys (org admins only). Keys are org-scoped: one key manages every project and engine the organization owns. The full key is shown once at creation and stored hashed.
  • Your browser session — the app itself calls these endpoints with your Clerk session, so anything you can do signed in, the API does too.
curl -H "Authorization: Bearer fl_..." \
  https://your-cloudrun.example/api/engines/$ENGINE_ID

The engine id is a UUID — it's in the URL (?engine=...) when you open an engine from the sidebar.

Errors

Every failure is JSON: { "error": "message" } with a meaningful status — 400 invalid body or an engine kind that doesn't support the resource · 401 missing or invalid credentials · 403 not yours to touch · 404 unknown resource (including ones owned by another org) · 409 a name or address is already taken · 502 the engine or an upstream service failed (box down, unknown profile, …).

Projects

A project groups engines. Creating one also provisions its first Hermes engine, so a fresh project is immediately usable.

method + pathbodyreturns
GET /api/projectsthe org's projects
POST /api/projects{ name?, description? } — name defaults to a generated onethe project (with its slug)
GET /api/projects/{projectId}the project
PATCH /api/projects/{projectId}{ name, slug, description? }the updated project
DELETE /api/projects/{projectId}{ ok } — tears down every engine in it

Engines

method + pathbodyreturns
GET /api/projects/{projectId}/engines[{ id, kind, name, isDefault, nodes }]
GET /api/engines/{engineId}the engine and its agents, for any kind
POST /api/hermes{ projectId, name }{ id } — provisions a new Hermes engine
PATCH /api/engines/{engineId}{ name }{ ok }
DELETE /api/engines/{engineId}{ ok } — the project's default engine can't be deleted

Engine creation is kind-specific (POST /api/hermes); future engine kinds get their own namespace root.

Agents

method + pathbodyreturns
GET /api/hermes/{e}/profiles[{ id, name, description }]
POST /api/hermes/{e}/profiles{ name, description?, from? }from clones an existing agent idthe created agent
PATCH /api/hermes/{e}/profiles/{p}{ name, description }{ ok }
DELETE /api/hermes/{e}/profiles/{p}{ ok }
curl -X POST -H "Authorization: Bearer fl_..." -H "Content-Type: application/json" \
  -d '{ "name": "researcher", "description": "Finds and summarizes sources" }' \
  https://your-cloudrun.example/api/hermes/$E/profiles

Identity

The agent's SOUL — its standing instructions.

method + pathbodyreturns
GET /api/hermes/{e}/profiles/{p}/identity{ instructions }
PUT /api/hermes/{e}/profiles/{p}/identity{ instructions }{ ok }

Chat & sessions

POST /api/hermes/{e}/chat?profile={p} streams a conversation turn (AI SDK UI-message stream). Send { "messages": [...], "id": "<session id>" } — the id names the engine-side session, created on first use and resumed forever after.

method + pathreturns
GET /api/hermes/{e}/profiles/{p}/sessions[{ id, title, updatedAt }]
GET /api/hermes/{e}/profiles/{p}/sessions/{sessionId}/messagesthe transcript as UI messages

Models

method + pathbodyreturns
GET /api/hermes/{e}/profiles/{p}/models[{ id, name, provider, active }]
PUT /api/hermes/{e}/profiles/{p}/models{ model, provider }{ ok }
GET /api/hermes/{e}/profiles/{p}/reasoning{ effort }
PUT /api/hermes/{e}/profiles/{p}/reasoning{ effort }{ ok }

Providers

method + pathbodyreturns
GET /api/hermes/{e}/profiles/{p}/providersprovider list with connection state
PUT /api/hermes/{e}/profiles/{p}/providers/{slug}{ key }{ ok } — connect with an API key
DELETE /api/hermes/{e}/profiles/{p}/providers/{slug}{ ok }
POST /api/hermes/{e}/profiles/{p}/providers/{slug}/oauththe oauth start (url + session)
GET …/oauth?sessionId={s}poll the pending oauth
PUT …/oauth{ sessionId, code }{ ok }
DELETE …/oauth{ ok }

Skills

Skill names may contain slashes (category/name) and go in the path as-is.

method + pathbodyreturns
GET /api/hermes/{e}/profiles/{p}/skillsinstalled skills with enabled, usage, source
POST /api/hermes/{e}/profiles/{p}/skillsa template skill (below){ ok }
GET …/skills/{name}{ name, description, local, hub }
PUT …/skills/{name}{ content }{ ok }
PATCH …/skills/{name}{ enabled }{ ok }
DELETE …/skills/{name}{ ok }

Install body — local content or a hub reference:

{ "name": "greeting", "description": "Says hi properly",
  "local": { "content": "---\nname: greeting\n---\n\nAlways greet warmly.", "files": [] },
  "hub": null }

The registry (search before you install):

method + pathreturns
GET …/skill-registry?query={q}registry search results
GET …/skill-registry/preview?identifier={id}the skill's full content
GET …/skill-registry/scan?identifier={id}the security scan verdict

Channels

method + pathbodyreturns
GET /api/hermes/{e}/profiles/{p}/channelsall platforms with connection state
PUT …/channels/{channelId}{ values: { "ENV_VAR": "…" } } — the platform's credentials{ ok }
DELETE …/channels/{channelId}{ ok }

Automations

method + pathbodyreturns
GET /api/hermes/{e}/profiles/{p}/automationsscheduled jobs
POST …/automations{ id: null | "existing", name, schedule, prompt, deliver, skills } — create or update{ ok }
PATCH …/automations/{id}{ enabled }{ ok }
DELETE …/automations/{id}{ ok }

Integrations

method + pathreturns
GET /api/hermes/{e}/profiles/{p}/integrationstoolkits with connection state
POST …/integrations/{toolkit}{ url } — an OAuth url to open, or null if connected immediately
DELETE …/integrations/{toolkit}{ ok }

Engine access

method + pathbodyreturns
GET /api/hermes/{e}/dashboard{ url, username, password } — the engine's own dashboard. As sensitive as your API key.
POST /api/hermes/{e}/terminal{ profileId, attach? }{ url } — a one-shot websocket url for the agent's TUI
POST /api/hermes/{e}/shellthe shell request the UI sendsstreamed output

Publishing & templates

An agent snapshots into a portable payload (identity, skills, automations, connected integrations and channels); publishing puts that payload on the marketplace; installing turns it back into a live agent.

method + pathbodyreturns
GET /api/hermes/{e}/profiles/{p}/snapshotthe agent's portable payload
GET /api/hermes/{e}/profiles/{p}/listingthe agent's marketplace listing, or null
PUT …/listing{ name, description, visibility, tags, payload } — visibility is public | org | private{ ok } — creates or versions the listing
DELETE …/listing{ ok } — author only
GET /api/templates?search=&tag=&page=&pageSize={ items, pageCount, total }
POST /api/templates/{templateId}/install{ projectId, engineId, name, description }the created agent

Automation templates

method + pathreturns
GET /api/automation-templates{ templates, categories } — ready-made automation recipes

Organization

Admin only — an API key counts as an admin.

method + pathbodyreturns
GET /api/orgthe organization with members and invitations
PATCH /api/org{ name, slug?, imageUrl? }imageUrl: null removes the logo{ ok }
PATCH /api/org/members/{userId}{ role }admin | member{ ok }
DELETE /api/org/members/{userId}{ ok }
POST /api/org/invitations{ emailAddress, role }{ ok }
DELETE /api/org/invitations/{invitationId}{ ok }

API keys

Admin only. Only a hash is stored — the full key appears once, in the create response.

method + pathbodyreturns
GET /api/keysthe org's keys (prefix, name, last used — never the key)
POST /api/keys{ name }{ key, keyPrefix }
DELETE /api/keys/{apiKeyId}{ ok }

Your profile

PATCH /api/me{ firstName, lastName, imageUrl? }{ ok }. Session only: an API key is an organization credential, not a person, so this endpoint rejects it.

On this page