REST API v1 Free until the end of 2026 · Self-service API keys

The eCMR API for your transport stack.

Create legally valid electronic consignment notes, collect eIDAS-compliant signatures and receive signed PDFs — all over a JSON API your TMS or ERP can integrate in an afternoon.

< 5 min
to your first eCMR
3
signature roles
6
document languages
10 yrs
legal archive
terminal — from zero to a draft eCMR
# 1. Create an account — no API key needed
curl -X POST https://ecmr.dev.40pes.pt/api/v1/ecmr/signup \
  -H "Content-Type: application/json" \
  -d '{ "email": "dev@acme.io", "company_name": "ACME Logistics", "country_code": "PT" }'

# 2. Click the link in the email, then read your key
curl "https://ecmr.dev.40pes.pt/api/v1/ecmr/signup/verify?token=<TOKEN>"
# → { "success": true, "data": { "api_key": "…", "rate_limit_per_minute": 60 } }

# 3. Create your first eCMR
curl -X POST https://ecmr.dev.40pes.pt/api/v1/ecmr/documents \
  -H "X-API-Key: $ECMR_API_KEY" \
  -H "Content-Type: application/json" \
  -d @ecmr.json
# → 201 { "success": true, "data": { "cmr_number": "…", "status": "draft" } }

Built on the standards your customers audit

  • CMR Convention & e-CMR Protocol
  • eIDAS (EU 910/2014)
  • GDPR (EU 2016/679)
  • JSON over HTTPS
  • HMAC-signed webhooks
  • 6 languages
Free until 31 December 2026

Start now, straight from this page

Fill in five fields and we create your account. Everything is free until the end of 2026 — no card, no sales call.

  1. 1 You receive an email with a verification link and the Postman collection attached
  2. 2 Click the link and your API key is ready — 60 requests per minute
  3. 3 Import the collection, paste the key and run the Getting Started flow

With country prefix, e.g. PT123456789. Checked against VIES / NIF.PT.

We only use these details to create your tenant and send the verification email.

Quick start

Four requests to a signed eCMR

No sales call, no sandbox request form. Create an account from the API, verify your email and start creating documents.

1

Create an account

One public POST with your company details. No API key needed yet — we send a verification link to the email you provide.

POST /api/v1/ecmr/signup
{
  "email": "dev@acme.io",
  "company_name": "ACME Logistics",
  "vat_number": "PT123456789",
  "company_type": "carrier",
  "country_code": "PT"
}
2

Verify and get your API key

Open the link (or call the verify endpoint with the token). The response contains your API key and your rate limit — 60 requests per minute by default.

GET /api/v1/ecmr/signup/verify
{
  "success": true,
  "data": {
    "tenant_id": "tnt_8f2c…",
    "api_key": "ecmr_live_…",
    "status": "active",
    "rate_limit_per_minute": 60
  }
}
3

Create an eCMR

Send the consignor, carrier, consignee, goods and transport data. The document is created as a draft and validated against the CMR field rules.

POST /api/v1/ecmr/documents
curl -X POST https://ecmr.dev.40pes.pt/api/v1/ecmr/documents \
  -H "X-API-Key: $ECMR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "document": {
      "external_reference": "TMS-2026-001",
      "consignor": { "name": "Shipper Lda", "address": "Lisboa, PT", "country": "PT" },
      "carrier":   { "name": "ACME Logistics", "address": "Porto, PT", "country": "PT" },
      "consignee": { "name": "Receiver GmbH", "address": "Berlin, DE", "country": "DE" },
      "goods": [{ "description": "Electronic components", "quantity": 10, "packaging": "pallets", "weight_kg": 2500 }],
      "transport": { "vehicle_registration": "12-AB-34", "loading_date": "2026-09-10", "delivery_date": "2026-09-12" }
    }
  }'
4

Issue and collect signatures

Issue the draft, then request signatures per role. Signatories receive a link by SMS or email, validate a one-time code and sign on their phone. Your webhook fires at every state change.

POST …/lifecycle/issue · POST …/signatures
# Issue the document (draft → issued)
curl -X POST https://ecmr.dev.40pes.pt/api/v1/ecmr/documents/$CMR_NUMBER/lifecycle/issue \
  -H "X-API-Key: $ECMR_API_KEY"

# Ask the consignor to sign: they get a link by SMS or email and validate an OTP
curl -X POST https://ecmr.dev.40pes.pt/api/v1/ecmr/documents/$CMR_NUMBER/signatures \
  -H "X-API-Key: $ECMR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "signatory_role": "consignor", "signatory_name": "Maria Silva", "contact": "+351912345678", "signature_type": "remote" }'

# Repeat for carrier and consignee — your webhook gets `completed` when all three have signed

Every request above is in the Postman collection, with variables populated automatically as you go: Postman

Postman

Import the collection and run the whole flow

A ready-to-run Postman collection with every endpoint, realistic request bodies and test scripts that carry your API key, tenant and CMR number from one request to the next.

  • Guided Getting Started folder — from signup to a signed PDF
  • Post-response scripts that set api_key, tenant_id and cmr_number for you
  • Error Codes Reference folder to see every machine-readable error
  • Reseller API folder for partners managing multiple tenants
Download collection
ecmr_api_collection.json
123 KB · 85 requests in 10 folders · Postman v2.1

Import in 3 steps

  1. Download the JSON file above
  2. In Postman, choose File → Import and drop the file
  3. Open the Getting Started folder and run requests 1 to 9 in order

Collection variables

{{base_url}}
API host — set it to https://ecmr.dev.40pes.pt
{{api_key}}
Your key, filled in after email verification
{{tenant_id}}
Your tenant, filled in after signup
{{cmr_number}}
Last document created, filled in automatically
eCMR API - 40 PES
85 requests
  • Reseller API 12 requests
  • Getting Started 19 requests
  • Account Management 3 requests
  • API Status 2 requests
  • Tenant 6 requests
  • Documents 12 requests
  • Lifecycle 3 requests
  • Signatures 9 requests
  • Shares 8 requests
  • Error Codes Reference 11 requests
Folders and request counts are read from the collection file at runtime, so this always matches what you download.

API reference

One resource model, predictable responses

Everything is JSON over HTTPS, authenticated with an X-API-Key header. Every response uses the same envelope, every error carries a machine-readable code.

Documents

Create, read, update, search and download eCMRs as PDF.

  • GET /documents
  • POST /documents
  • GET /documents/:cmr_number
  • PATCH /documents/:cmr_number
  • GET /documents/:cmr_number/pdf
  • GET /documents/search?q=

Lifecycle

Move a document from draft to issued, cancel it, and audit its history and compliance.

  • POST /documents/:cmr_number/lifecycle/issue
  • POST /documents/:cmr_number/lifecycle/cancel
  • GET /documents/:cmr_number/lifecycle/status
  • GET /documents/:cmr_number/history
  • GET /documents/:cmr_number/compliance

Signatures

Request signatures per role, validate one-time codes and record the signature.

  • POST /documents/:cmr_number/signatures
  • POST …/signatures/:id/validate_otp
  • POST …/signatures/:id/sign
  • POST …/signatures/:id/resend_otp
  • POST …/signatures/:id/cancel

Shares & QR codes

Tokenised links and QR codes so drivers, warehouses and authorities can view or sign.

  • POST /documents/:cmr_number/shares
  • GET /documents/:cmr_number/shares/active
  • POST …/shares/generate_qr
  • POST …/shares/:token/revoke
  • GET /signatures/public/:token

Tenant & webhooks

Profile, usage statistics and your webhook endpoint configuration.

  • GET /tenant/profile
  • PUT /tenant/profile
  • GET /tenant/usage
  • PUT /tenant/webhook
  • POST /tenant/test_webhook

Reseller

For integrators managing many customers: create and manage tenants and read usage.

  • POST /reseller/signup
  • POST /reseller/tenants
  • GET /reseller/tenants
  • POST /reseller/tenants/:id/suspend
  • GET /reseller/billing/usage

All paths are relative to https://ecmr.dev.40pes.pt/api/v1/ecmr

Response envelope

Successful responses always return success: true, a data object and a meta block with the server timestamp.

201 Created
{
  "success": true,
  "data": {
    "cmr_number": "CMR-2026-000123",
    "status": "draft",
    "external_reference": "TMS-2026-001"
  },
  "meta": { "timestamp": "2026-09-04T10:32:11Z" }
}

Errors you can branch on

Failed responses return success: false and an error with a stable code, a human message and per-field details. Map the code, not the message.

422 Unprocessable Entity
{
  "success": false,
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Validation failed",
    "details": ["Consignor name can't be blank"]
  },
  "meta": { "timestamp": "2026-09-04T10:32:11Z" }
}

Webhooks

Configure one URL and a secret on your tenant. We POST a JSON payload on every state change and sign the raw body with HMAC-SHA256 so you can reject anything that did not come from us.

Events

  • issued
  • accepted
  • delivered
  • completed
  • cancelled
verify.js
import { createHmac, timingSafeEqual } from "node:crypto"

const expected = createHmac("sha256", process.env.ECMR_WEBHOOK_SECRET)
  .update(rawBody)
  .digest("hex")

const received = req.headers["x-webhook-signature"] ?? ""
const valid = timingSafeEqual(Buffer.from(expected), Buffer.from(received))
POST https://your-tms.example/webhooks/ecmr
Content-Type: application/json
X-eCMR-Event: completed
X-Webhook-Signature: 3f9a1c…   // HMAC-SHA256 hex of the raw body

{
  "event": "completed",
  "tenant_id": "tnt_8f2c…",
  "document": {
    "cmr_number": "CMR-2026-000123",
    "status": "completed",
    "external_reference": "TMS-2026-001"
  },
  "data": {},
  "timestamp": "2026-09-04T14:05:42Z"
}

Authentication

Send your key in the X-API-Key header on every request. Keys are per tenant and can be regenerated at any time without downtime.

Rate limits

60 requests per minute per tenant by default, returned as HTTP 429 with code RATE_LIMIT_EXCEEDED when exceeded. Higher limits on request.

Six languages

PDFs, notification emails and signature pages are available in pt, en, es, de, fr and it. Pass locale on the PDF endpoint or preferred_locale when requesting a signature.

Security & compliance

Legally solid by design

A digital consignment note is only useful if it holds up in an audit or in court. These guarantees are built into every document, not bolted on.

One-time code before every signature

Each signatory validates a code sent by SMS or email before signing, tying the signature to a verified contact and protecting against impersonation.

RFC 3161 timestamps

Every signature is stamped by a certified time-stamping authority, proving the exact date and time in a legally binding way.

eIDAS advanced signatures

Signatures follow the eIDAS Regulation (EU 910/2014): they identify the signer, detect later changes and prevent repudiation. Qualified certificates (QES) are supported.

Immutable audit trail

Every action is appended to an audit log that nobody can edit — who, what, when and from where. Versions are kept whenever a document changes.

SHA-256 integrity and platform seal

Each document carries a unique hash and a platform signature under our own certificate, so tampering with a single character is detected immediately.

GDPR and 10-year retention

Data minimisation, TLS in transit, encryption at rest, and automatic archiving for the legal retention period with deletion afterwards.

40 Pés

From the makers of 40 PÉS

eCMR is one piece of a complete transport management platform

40 PÉS is integrated management software for freight transport, built in Portugal: services, fleet tracking, billing, driver communication and CMR documents in one place. The eCMR API on this page is the same engine that powers it — and if you would rather not build the integration yourself, 40 PÉS already has it.

  • Track & Trace and fleet monitoring
  • Billing integration that cuts invoicing time in half
  • Load marketplace and collaboration with partner carriers
  • Cost and profitability per vehicle and driver
Discover 40 PÉS
2,000+
vehicles managed every day
1,000+
active users
3M+
services launched
500k+
documents issued

Start integrating today

Grab the collection, create your account from the API and ship your first eCMR before the next stand-up.

Live API status: /api/v1/ecmr/health