eCMRhub.docs

Authentication

How an agent or program connects to a company account, with OAuth or an API key, and what each permission allows.

Every call to Hub acts for exactly one company. The company decides who may act for it and what they may do. There are two ways to connect, and both show up in the dashboard as a connection the company can change or disconnect.

OAuth connectionAPI key
Use it forAgents with a person nearby: the CLI, Claude, ChatGPT, other MCP clients; programs serving many companies (TMS)Scripts, CI and server automation of one company
How it startsA person signs in to Hub in the browser and approves the requested permissionsThe company owner creates a key in the dashboard; the secret is shown once
CredentialAccess token plus refresh token, per companyecmr_sk_... secret
Approvals by a personOn by default (the person can switch it off for this connection)Off by default (the owner can switch it on for this key)
RevokeDashboard, ConnectionsDashboard, Connections or API keys

In build (OAuth and dashboard)

OAuth sign-in, the Connections list and the per-connection approval switch are part of phase 1. API keys are available today. The default approval setting per kind of connection is still open and may change.

OAuth sign-in

Hub runs an OAuth 2.1 authorization server (Supabase Auth). Clients register themselves with Dynamic Client Registration, so neither you nor the company needs to create a client by hand. Metadata: https://api-ecmr-dev.lucaspiera.com/.well-known/oauth-authorization-server/auth/v1.

What the person sees:

  1. The agent or program opens the Hub sign-in page in the browser.
  2. The person signs in with their Hub account (the company owner, for a new connection).
  3. The consent screen names the program and its redirect address, and lists the permissions. By default it grants reading documents, writing and issuing drafts and reading the address book; signing links and tracking only when the person ticks them.
  4. A switch "Require my approval to issue, send and delete" is on by default.
  5. After approval the program receives a token for this one company, with a refresh token.

The token is a JWT for the person who approved, bound to the OAuth client. Hub accepts it only while the connection is active: after the company disconnects it, every call answers 401 CONNECTION_REVOKED.

From the CLI

ecmrhub login            # browser sign-in, OAuth 2.1 with PKCE, local callback on 127.0.0.1
ecmrhub whoami --json    # company, connection and permissions
ecmrhub logout           # forget the tokens of this profile

Profiles (--profile) keep several companies apart on one machine. ecmrhub login --no-browser prints the sign-in address instead of opening a browser. On a machine without a browser (SSH, CI) use an API key. whoami reports how the CLI is connected (auth: oauth, apikey, env-key or env-token).

From a chat client

Add the Hub MCP server as a connector; the client runs the same sign-in. Steps per client: MCP & connectors.

From a program serving many companies

Register one OAuth client (Dynamic Client Registration, or ask us to create one). Each company clicks "Connect with eCMR Hub" in your program, signs in to Hub and approves the permissions. You receive a separate token with refresh for each company, and each company sees your program in its dashboard and can disconnect it.

API keys

The company owner creates a key in the dashboard under API keys, names it, picks permissions and an optional expiry. The secret ecmr_sk_<key id>_<secret> is shown once and stored nowhere in Hub; only its last four characters stay visible. A company has at most 20 active keys.

Use the key through the CLI or the SDK; both sign in for you:

export ECMR_HUB_API_KEY=ecmr_sk_...
ecmrhub whoami --json
import { ECMR } from "@ecmrhub/sdk";

const hub = new ECMR({
  url: "https://api-ecmr-dev.lucaspiera.com",
  // public, from GET https://api-ecmr-dev.lucaspiera.com/functions/v1/api-keys/config
  anonKey: "<anon key>",
  apiKey: process.env.ECMR_HUB_API_KEY!,
});

Never print, log or commit a key, and do not pass it as a command-line argument (it stays in the shell history). A revoked or expired key answers 401 API_KEY_INVALID.

Calling HTTP directly with a key

The key is a sign-in for a technical account. Exchange it for an access token, then call the API with the token:

  1. Read the public anonKey: GET https://api-ecmr-dev.lucaspiera.com/functions/v1/api-keys/config.
  2. Split the key ecmr_sk_<32 hex>_<43 chars>. The 32 hex characters are the account id; write them as a UUID (8-4-4-4-12). The last 43 characters are the password.
  3. Sign in:
curl -s "https://api-ecmr-dev.lucaspiera.com/auth/v1/token?grant_type=password" \
  -H "apikey: $ANON_KEY" -H "Content-Type: application/json" \
  -d '{"email":"<account uuid>@api-keys.ecmr-one.invalid","password":"<43 chars>"}'
  1. Send apikey: $ANON_KEY and Authorization: Bearer <access_token> with every request. The token expires; sign in again when you get 401.

Permissions

A connection or key has a subset of these permissions. Reading documents is always included.

PermissionAllows
documents:readList and read drafts and documents, PDFs, evidence
documents:writeCreate, update and delete drafts; issue documents
signing:linksCreate, renew, revoke and send signing links
library:readRead the company address book: contractors, vehicles, templates
library:writeChange the address book
tracking:manageLive tracking of a transport

What a connection or key can never do, whatever its permissions:

  • sign a document as a party (sender, carrier, consignee),
  • invite or remove people, create or revoke API keys, change connections,
  • approve an approval request,
  • delete an account.

A missing permission answers 403: API_KEY_FORBIDDEN for API keys, CONNECTION_FORBIDDEN for OAuth connections. The operation catalog shows the permission of every operation.

Headers

Every HTTP call sends:

HeaderValue
apikeyThe public anon key of Hub (not secret)
AuthorizationBearer <access token> from OAuth or from an API key sign-in
x-ecmr-approvalOnly when repeating a call after approval: the approval id
Idempotency-KeyWhere an endpoint asks for it: a stable key per logical write

The company comes from the token. There is no header that selects a company.

On this page