Skip to content

Developer portal

Build on the Hermida platform.

Hermida holds the live record of projects, services, and billing. These are the same APIs our own client workspace runs on — authenticate with a token, call a function, get scoped JSON back.

Transport
HTTPS, JSON in and JSON out
Auth
Bearer token, scoped to the member
Surface
Backend functions behind /functions
Money
Integer cents, ISO dates
curl
curl -X POST "$HERMIDA_API/functions/clientProjects" \
  -H "Authorization: Bearer $HERMIDA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"list","slug":"acme-co"}'

Quickstart

From a token to your first integration.

Three steps. No SDK required — the API is plain HTTPS and JSON, so it works from a script, a server, or a spreadsheet automation.

  1. 01

    Issue a personal access token

    Open your workspace settings, then Secrets → Personal access tokens, and create a token for the integration. It is shown once. Revoke it there the moment a machine stops using it.

    header
    Authorization: Bearer hmd_live_…
  2. 02

    Call a function

    Every endpoint is a POST to /functions/<name> with a JSON body. The action in the body selects the behaviour of that function.

    curl
    curl -X POST "$HERMIDA_API/functions/clientProjects" \
      -H "Authorization: Bearer $HERMIDA_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"action":"list","slug":"acme-co"}'
  3. 03

    Read the scoped response

    Responses are JSON with an ok flag. A failure returns ok: false plus a code you can branch on — never a partial payload.

    javascript
    const res = await fetch(`${BASE}/functions/clientProjects`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.HERMIDA_TOKEN}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ action: 'list', slug: 'acme-co' }),
    });
    
    const { ok, projects, code } = await res.json();
    if (!ok) throw new Error(code);

Authentication

A bearer token, and the permissions already attached to it.

Hermida has no separate permission model for integrations. The token inherits the member, the member inherits their memberships, and the API enforces both on every request.

A personal access token runs as the person who created it. It sees exactly the organizations, projects, files, and invoices that membership already grants — nothing wider, and nothing across organizations.

  • Projects

    Read a project with its milestones, tasks, and published updates, or mark a client-visible task complete.

  • Services

    Browse the self-service catalog, then add services to a project — the batch becomes one invoice.

  • Billing

    The organization statement: outstanding, overdue, paid year-to-date, invoices, payments, and services.

  • Files

    Client-visible files, version metadata, and signed download URLs that expire in five minutes.

request
curl -X POST "$HERMIDA_API/functions/clientProjects" \
  -H "Authorization: Bearer $HERMIDA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"billing","slug":"acme-co"}'
typescript
const res = await fetch(`${BASE}/functions/clientProjects`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.HERMIDA_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ action: 'billing', slug: 'acme-co' }),
});

const { ok, billing } = await res.json();
if (!ok) throw new Error('request_failed');

Create a token in your workspace under Settings → Secrets → Personal access tokens, or from the API page. It is shown once; revoke it there and access ends immediately.

API reference

Six functions behind every workspace screen.

Each endpoint takes a JSON body with an action. Responses are scoped to the token's member, and failures come back as ok: false with a code — never a partial payload.

POST/functions/clientProjects

Projects, dashboard figures, and the billing statement.

Returns the projects the signed-in member can access, one project with its milestones, tasks, and updates, the workspace dashboard figures, or the organization's billing statement. Every response is narrowed to that member's membership — there is no cross-organization read.

Parameters

actionrequired
string — dashboard · billing · list · get · complete-task
slugrequired
string — Organization slug.
project_slug
string — Required for get and complete-task.
task_id
string — complete-task only.
request body
{
  "action": "list",
  "slug": "acme-co"
}
response
{
  "ok": true,
  "projects": [
    {
      "id": "6ae25c21df4d4eed49b7444a",
      "project_code": "HT-2026-0006",
      "name": "Dispatch console",
      "summary": "Real-time dispatch for a regional freight broker.",
      "status": "active",
      "target_end_date": "2026-11-30",
      "latest_update_at": "2026-10-24T14:02:00.000Z",
      "progress": 42
    }
  ],
  "total": 1
}

The billing action returns outstanding, overdue, paid year-to-date, invoices, payments, and services. All money is integer cents.

Errors and limits

One shape for every failure.

A failed request returns a JSON body with ok: false and a stable code, plus an HTTP status you can branch on. Nothing partial is ever written.

  • invalid_action400

    The action is missing or not one this endpoint accepts.

  • access_denied403

    No active membership grants access, or the record belongs to another organization.

  • membership_suspended403

    The membership exists but is suspended. An organization admin can restore it.

  • organization_suspended403

    The organization is suspended or archived.

  • not_found404

    The requested record does not exist.

  • rate_limited429

    Too many requests from this token. Back off and retry after the Retry-After header.

  • server_error500

    The request failed on our side. Safe to retry with backoff; contact us if it persists.

403 forbidden
{
  "ok": false,
  "code": "access_denied"
}

Requests are rate limited per token. On a 429, back off exponentially and honour the Retry-After header. Retry 5xx responses with backoff; never retry a 4xx unchanged.

Downloads

Ambient OS, and anything else we ship.

Installers distributed directly by Hermida. Ambient OS is the corporate build for our own servers and office machines — download it here, then follow the install note your account contact sent.

1 package

  • Ambient OS

    macOS · 413 KB · AmbientOSapp.zip

    The Ambient OS build for Hermida corporate servers and office machines.

Changelog

What changed, and when.

Platform releases that affect the API surface. Additive changes ship without a breaking note; anything that can break an integration is called out here first.

  1. 2026.10

    October 2026

    Self-service services

    • projectServices assign issues one invoice for a whole batch of services.
    • A project without its own billing account now inherits the organization's account.
    • Every service request writes client-visible activity against the project.
  2. 2026.09

    September 2026

    Files and notifications

    • clientFiles: signed downloads, version metadata, and approval state.
    • myNotifications: unread counts and read controls for members.
  3. 2026.08

    August 2026

    Hermida ID

    • One account across organizations, with a primary organization and per-organization roles.
    • myAccount context returns every membership in a single call.
  4. 2026.07

    July 2026

    Billing Center

    • Invoices, payments, credits, and adjustments as first-class records.
    • Organization statements built from project-linked invoices.

Need an integration built instead?

If the work is connecting Hermida to a system you already run, our team builds and maintains that integration — scoped, documented, and handed over.

We use optional analytics to understand how this website is used. No analytics loads until you allow it, and declining keeps everything on the site working.