Quickstart for agents
Sign in, learn the fields, create a draft and issue it after a person approves. Written for an AI agent working for one company.
In build (CLI, OAuth and approvals)
The ecmrhub CLI, OAuth sign-in and approvals are part of phase 1 and not released yet. The steps
below follow the agreed contract; run ecmrhub --help for the exact flags of your version.
You are an agent working for one company. Everything you do happens in that company's account, within the permissions the company granted you. Follow these steps in order.
Rules before you start
- Never guess data. Names, addresses, plates, dates and goods come from the user or from a source document. If a required value is missing, ask.
- Never approve for the user. When Hub answers
APPROVAL_REQUIRED, give the person the approval link and wait. There is no flag or tool that approves. - Repeat exactly. After approval, repeat the same call with the same input plus the approval id. Any change of input needs a new approval.
- Use JSON. Pass
--json(or pipe the output) and parse stdout. Errors arrive on stderr as{"error":{"status","code","message","retryable"}}. Decide bycode; messages may be in Polish. - eFTI gaps do not block issuing. Report them to the user; never promise eFTI registration.
1. Sign in
Ask the user to run the sign-in once, or run it if you have a terminal and the user is present:
ecmrhub loginIt opens the browser. The person signs in to Hub, and the company owner chooses what you may do
(read documents, write drafts, signing links...) and whether issuing needs their approval. Tokens are stored in
~/.config/ecmr-hub/config.json (mode 0600) and refreshed automatically.
In CI or scripts use a company API key instead, never on the command line:
export ECMR_HUB_API_KEY=ecmr_sk_... # from the dashboard, API keysMore: Authentication.
2. Check who you are
ecmrhub whoami --jsonRead the company and permissions. If a permission you need is missing, tell the user which one;
only a person in the dashboard can grant it.
3. Learn the fields
ecmrhub fields --jsonEach field has a key, whether it is required to issue, whether eFTI needs it
(eftiRequired) and, for coded fields, the allowed codes. Values are strings, dates are
YYYY-MM-DD, countries are ISO 3166 alpha-2 codes.
4. Check the data locally
Write the values as one flat JSON object keyed by field keys, then:
ecmrhub drafts check --data note.json --jsonRepeat until ok is true. The report lists errors, missingRequired and eftiGaps. The
check runs locally and sends nothing.
5. Create the draft
ecmrhub drafts create --data note.json --jsonIt returns the draft id and revision. To fix something, send only the changed fields:
ecmrhub drafts update <draftId> --data changes.json --jsonAn empty string clears a field. A 409 VERSION_CONFLICT means the draft changed meanwhile: list
drafts, read it again, retry.
6. Issue it, with approval
Issuing creates an immutable document, so a person approves it first:
ecmrhub drafts issue <draftId> --jsonThe first call exits with code 5 and prints the pending approval:
{
"error": {
"status": 428,
"code": "APPROVAL_REQUIRED",
"message": "...",
"retryable": false,
"approval": {
"id": "6f1c...",
"url": "https://ecmrhub-v2.lucaspiera.com/dashboard/approvals/6f1c...",
"expiresAt": "2026-10-20T10:00:00Z",
"operation": "draft.publish",
"retry": "ecmrhub drafts issue <draftId> --revision 3 --approval 6f1c... --json"
}
}
}Tell the user: "Please approve issuing this consignment note: <url>". Then wait, and run the
command from error.approval.retry exactly as given:
ecmrhub approvals wait <approvalId> --json # 0 approved, 4 rejected, 1 expired or used, 5 still pending
ecmrhub drafts issue <draftId> --revision 3 --approval <approvalId> --jsonIssuing the same draft again returns the same document, so a retry after a timeout is safe.
7. After issuing
ecmrhub sign request <documentId> --role carrier --json # needs approval too
ecmrhub documents pdf <documentId> --out note.pdf # hash-checked PDF
ecmrhub documents evidence <documentId> --json # versions, events, signatures
ecmrhub efti check <documentId> --json # eFTI gaps, nothing is sentSend a signing link only to the party it is for. It is valid for 7 days.
The same flow over MCP
In a chat client connected through MCP the steps are tools: draft_save,
draft_publish, signing_createLink and so on. A tool that needs approval returns
status: "approval_required" with approvalId and url in its result. Show the link to the user,
and after they approve call the same tool with the same arguments plus approvalId.