{
	"info": {
		"_postman_id": "9a84eb7a-3147-4628-b4db-d55f8bd1a81e",
		"name": "eCMR API - 40 PES",
		"description": "Complete REST API for eCMR (electronic Consignment Note) management.\n\nCompliant with:\n- CMR Convention\n- eIDAS Regulation (EU 910/2014)\n- GDPR (EU 2016/679)\n\n**Multi-language Support:**\nThe API supports 6 languages: Portuguese (pt), English (en), Spanish (es), German (de), French (fr), Italian (it).\n\n**Authentication:** Use X-API-Key header with your tenant API key.\n\n**Error Handling:**\nAll error responses include a machine-readable `code` field for i18n mapping:\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"OTP_EXPIRED\",\n    \"message\": \"OTP has expired. Please request a new one.\",\n    \"details\": []\n  }\n}\n```\nSee the 'Error Codes Reference' folder for the complete list of error codes by category.",
		"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
		"_exporter_id": "20604527"
	},
	"item": [
		{
			"name": "1. Getting Started",
			"item": [
				{
					"name": "01 Create Account",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"if (pm.response.code === 201) {",
									"    const response = pm.response.json();",
									"    pm.collectionVariables.set('tenant_id', response.data.tenant_id);",
									"    pm.collectionVariables.set('signup_email', pm.request.body.raw ? JSON.parse(pm.request.body.raw).email : '');",
									"    console.log('Account created! Tenant ID:', response.data.tenant_id);",
									"    console.log('Check your email for verification link');",
									"}"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"email\": \"developer@mycompany.com\",\n  \"company_name\": \"My Transport Company\",\n  \"vat_number\": \"PT123456789\",\n  \"company_type\": \"carrier\",\n  \"address\": \"Rua do Transporte, 123, 4100-000 Porto, Portugal\",\n  \"country_code\": \"PT\"\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/signup",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"signup"
							]
						},
						"description": "Create your eCMR API account. You'll receive a verification email.\n\n**Required fields:**\n- email: Your company email\n- company_name: Your company name\n- vat_number: EU VAT number (validated)\n- company_type: Your role in eCMRs - 'consignor', 'consignee', or 'carrier'. This determines which party's data is auto-populated when creating eCMRs.\n- address: Your company full address (street, postal code, city, country)\n\n**Optional fields:**\n- country_code: Two-letter ISO country code (e.g., 'PT', 'DE', 'ES') - auto-detected from VAT number if not provided\n- rate_limit_per_minute: Custom rate limit (default: 60)\n\n**What happens:**\n- Account created with status 'pending_verification'\n- Verification email sent\n- You get a tenant_id\n\n**Rate limit:** 5 signups per IP per hour\n\n**Next step:** Check your email and copy the verification token from the link."
					},
					"response": []
				},
				{
					"name": "02 Verify Email & Get API Key",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"if (pm.response.code === 200) {",
									"    const response = pm.response.json();",
									"    pm.collectionVariables.set('api_key', response.data.api_key);",
									"    console.log('Email verified!');",
									"    console.log('API Key:', response.data.api_key);",
									"    console.log('');",
									"    console.log('IMPORTANT: Save your API key now!');",
									"    console.log('This is the ONLY time it will be shown in full.');",
									"}"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "GET",
						"header": [
							{
								"key": "Accept",
								"value": "application/json"
							}
						],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/signup/verify?token={{verification_token}}",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"signup",
								"verify"
							],
							"query": [
								{
									"key": "token",
									"value": "{{verification_token}}",
									"description": "Paste the token from your verification email here"
								}
							]
						},
						"description": "Verify your email address and receive your API key.\n\n**How to get the token:**\n1. Check your email inbox\n2. Open the verification email from ecmr@40pes.pt\n3. Copy the token from the link\n4. Paste it in the {{verification_token}} variable\n\n**What you get:**\n- Your API key (shown only once!)\n- Account activated (status: 'active')\n- Rate limit: 60 requests/minute (default)\n- Ready to use the API"
					},
					"response": []
				},
				{
					"name": "03 Test API Key",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"if (pm.response.code === 200) {",
									"    console.log('API Key is valid!');",
									"    const response = pm.response.json();",
									"    console.log('Tenant:', response.data.name);",
									"    console.log('Rate Limit:', response.data.rate_limit_per_minute, 'req/min');",
									"} else if (pm.response.code === 401) {",
									"    console.log('ERROR: Invalid API Key. Check your api_key variable.');",
									"} else {",
									"    console.log('Unexpected response:', pm.response.code);",
									"}"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/tenant/profile",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"tenant",
								"profile"
							]
						},
						"description": "Test that your API key is working by fetching your tenant profile.\n\n**Expected result:** 200 OK with your tenant profile\n\n**If you get 401 Unauthorized:**\n- Check that your api_key variable is set correctly\n- Make sure you completed the email verification step\n\n**What you'll see:**\n- Your company name\n- API key (masked)\n- Rate limit configuration\n- Account status"
					},
					"response": []
				},
				{
					"name": "04 Create Your First eCMR",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"if (pm.response.code === 201) {",
									"    const response = pm.response.json();",
									"    pm.collectionVariables.set('cmr_number', response.data.cmr_number);",
									"    console.log('eCMR created!');",
									"    console.log('CMR Number:', response.data.cmr_number);",
									"    console.log('Status:', response.data.status);",
									"}"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"document\": {\n    \"external_reference\": \"MY-FIRST-CMR-001\",\n    \"carrier\": {\n      \"name\": \"My Transport Company\",\n      \"address\": \"Rua do Transporte, 123, 4100-000 Porto, Portugal\",\n      \"vat_number\": \"PT123456789\",\n      \"country\": \"PT\",\n      \"phone\": \"+351910000001\",\n      \"email\": \"carrier@mycompany.com\"\n    },\n    \"consignor\": {\n      \"name\": \"Shipper Company Ltd\",\n      \"address\": \"Shipping Street, 456, 1000-000 Lisboa, Portugal\",\n      \"country\": \"PT\",\n      \"vat_number\": \"PT987654321\",\n      \"email\": \"shipper@example.pt\",\n      \"phone\": \"+351912345678\",\n      \"contact_person\": \"Maria Shipper\"\n    },\n    \"consignee\": {\n      \"name\": \"Receiver GmbH\",\n      \"address\": \"Empfangerstrasse 789, 10115 Berlin, Germany\",\n      \"country\": \"DE\",\n      \"vat_number\": \"DE123456789\",\n      \"email\": \"receiver@example.de\",\n      \"phone\": \"+49301234567\",\n      \"contact_person\": \"Hans Receiver\"\n    },\n    \"goods\": [\n      {\n        \"description\": \"Electronic components for automotive industry\",\n        \"quantity\": 10,\n        \"packaging\": \"pallets\",\n        \"weight_kg\": 2500,\n        \"volume_m3\": 12,\n        \"marks_and_numbers\": \"LOT-2025-DEMO-001\",\n        \"statistical_number\": \"8542.31\",\n        \"dangerous_goods\": false\n      }\n    ],\n    \"transport\": {\n      \"vehicle_registration\": \"12-AB-34\",\n      \"trailer_registration\": \"56-CD-78\",\n      \"driver_name\": \"John Driver\",\n      \"driver_license\": \"P-1234567\",\n      \"driver_phone\": \"+351935000000\",\n      \"loading_place_name\": \"Main Warehouse\",\n      \"loading_place_address\": \"Lisboa, Portugal\",\n      \"delivery_place_name\": \"Distribution Center\",\n      \"delivery_place_address\": \"Berlin, Germany\",\n      \"loading_date\": \"2025-01-20\",\n      \"delivery_date\": \"2025-01-22\"\n    },\n    \"consignor_observations\": \"Goods packed on 10 standard EUR pallets. 2 pallets with reinforced packaging due to fragile contents. Handle with care. Keep dry.\",\n    \"instructions\": {\n      \"observations\": \"Side loading required. Stack maximum 2 pallets.\",\n      \"payment_terms\": \"collect\",\n      \"documents_attached\": [\"Invoice\", \"Packing List\"]\n    },\n    \"metadata\": {\n      \"demo\": true,\n      \"created_from\": \"Getting Started Guide\"\n    }\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents"
							]
						},
						"description": "Create your first eCMR document.\n\n**Important:** Your tenant is configured as a 'carrier', so:\n- The carrier's base data (name, address, country, vat_number, email) is automatically populated from your tenant profile\n- If you try to specify carrier base fields, you'll get an error\n\n**If your tenant was configured as 'consignor' or 'consignee':**\n- That party's base data would be auto-populated instead\n- You'd need to provide full data for the other parties\n\n**Driver and vehicle information** is provided in the `transport` object:\n- `vehicle_registration`, `trailer_registration`: Registrations\n- `driver_name`: Name of the driver\n- `driver_license`: Driver's license number\n- `driver_phone`: Driver's phone number\n- `driver_email`: Driver's email (optional)\n\nAll of these are optional at every stage — the carrier normally assigns the driver after the document is issued. They belong to `transport` only: sending them inside `carrier`, `consignor` or `consignee` is rejected with `PARTY_FIELD_NOT_ALLOWED`.\n\n**Goods object includes:**\n- `statistical_number` (Box 10): HS/NC code for customs\n\n**Observation fields (top-level):**\n- `consignor_observations` (Box 13): Instructions and observations from the shipper\n- `carrier_observations` (Box 18): Carrier reservations and remarks\n- `consignee_observations`: Receiver notes\n\n**Instructions object (CMR Boxes):**\n- `payment_terms` (Box 14): Payment terms - 'prepaid' or 'collect'\n- `cash_on_delivery` (Box 15): COD amount if applicable\n- `special_agreements` (Box 19): Special transport agreements\n- `documents_attached` (Box 5): List of attached documents\n\n**Status after creation:** `draft`\n\n**Next step:** Issue the document to make it active."
					},
					"response": []
				},
				{
					"name": "05 Issue Document (Draft -> Issued)",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"location\": \"Lisboa, Portugal\"\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/lifecycle/issue",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"lifecycle",
								"issue"
							]
						},
						"description": "Issue the document to make it active.\n\n**Transition:** draft -> issued\n\n**What happens:**\n- Document becomes active\n- Ready for carrier acceptance\n- Platform signature applied"
					},
					"response": []
				},
				{
					"name": "06 Update Loading Info (Before Signatures)",
					"request": {
						"method": "PATCH",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"document\": {\n    \"timestamps\": {\n      \"loading_started_at\": \"2025-01-20T08:00:00Z\",\n      \"loading_completed_at\": \"2025-01-20T11:30:00Z\"\n    },\n    \"carrier_observations\": \"Waiting time at loading: 3h30. Consignor warehouse not ready on arrival at 08:00. Loading only started at 10:00. All 10 pallets verified and loaded by 11:30.\"\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}"
							]
						},
						"description": "**SCENARIO: Carrier adds loading delay info BEFORE signatures**\n\n**What's happening:**\n- Driver arrived at 08:00 but warehouse wasn't ready\n- Loading only completed at 11:30 (3h30 waiting time)\n- Carrier documents this delay as a reservation/observation\n\n**Why do this BEFORE signatures?**\n- Consignor will sign WITH this information visible\n- Both parties acknowledge the delay (CMR Article 8 compliance)\n- The signature snapshot will include these observations\n- No disputes later about waiting time charges\n\n**Timing matters:**\n- If called BEFORE signatures: All parties will sign with this info\n- If called AFTER Consignor signs: Info recorded but marked as 'not signed by Consignor'\n\n**Editable fields (in issued/accepted/delivered states):**\n- `loading_started_at`, `loading_completed_at`\n- `delivery_started_at`, `delivery_completed_at`\n- `carrier_observations`\n- `vehicle_registration`, `trailer_registration`\n- `consignor_observations` (until Consignor signs)\n- `consignee_observations` (until Consignee signs)\n\n**Result:** When Consignor signs in step 9, their signature snapshot includes the loading times and carrier observations. This is verifiable via the `integrity` field in the document response."
					},
					"response": []
				},
				{
					"name": "07 Request Consignor Signature",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"if (pm.response.code === 201) {",
									"    const response = pm.response.json();",
									"    pm.collectionVariables.set('consignor_signature_id', response.data.id);",
									"    pm.collectionVariables.set('consignor_share_token', response.data.share_token);",
									"    console.log('Consignor signature requested!');",
									"    console.log('Share Token:', response.data.share_token);",
									"    console.log('Signature ID:', response.data.id);",
									"}"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"signatory_role\": \"consignor\",\n  \"signatory_name\": \"Maria Shipper\",\n  \"contact\": \"shipper@example.pt\",\n  \"signature_type\": \"remote\",\n  \"preferred_locale\": \"pt\"\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures"
							]
						},
						"description": "Request signature from consignor (shipper) using remote OTP.\n\n**Note:** The consignor will sign a document that now includes the loading delay info added in step 6. Their signature snapshot will capture this state.\n\n**Signature type: remote (OTP)**\n- For remote signatures, the OTP validation automatically completes the signature\n- No biometric data or signature image required\n- Ideal for parties not physically present\n\n**Parameters:**\n- signatory_role: consignor, carrier, or consignee\n- signatory_name: Name of the person signing\n- contact: Email or phone for OTP delivery\n- signature_type: remote (OTP-only signature)\n- preferred_locale: Language for OTP email (pt, en, es, de, fr, it)\n\n**What happens:**\n- Signature request created with status 'pending'\n- OTP sent to consignor's email (in Portuguese)\n- Share token generated\n\n**Next step:** Validate OTP to complete the signature."
					},
					"response": []
				},
				{
					"name": "08 Validate Consignor OTP",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"otp_code\": \"123456\",\n  \"geolocation\": {\n    \"latitude\": 38.736946,\n    \"longitude\": -9.142685,\n    \"accuracy\": 10\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{consignor_signature_id}}/validate_otp",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{consignor_signature_id}}",
								"validate_otp"
							]
						},
						"description": "Validate OTP code sent to consignor's email.\n\n**For remote signatures (like this one):**\n- OTP validation automatically COMPLETES the signature\n- No separate /sign endpoint call needed\n- Signature status changes directly to 'completed'\n\n**What happens:**\n- OTP code verified (6-digit code from email)\n- Signature completed automatically\n- Document snapshot captured at this moment\n- Geolocation recorded as evidence\n\n**Response includes:**\n- completed: true\n- signed_at: timestamp\n- method: 'remote_otp'\n\n**Next step:** Request carrier signature (in_person type)."
					},
					"response": []
				},
				{
					"name": "09 Sign as Consignor",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"signature_image\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==\",\n  \"biometric_data\": {\n    \"strokes\": [\n      {\n        \"index\": 0,\n        \"points\": [\n          {\"x\": 10.5, \"y\": 20.3, \"time\": 0, \"pressure\": 0.5},\n          {\"x\": 12.8, \"y\": 22.1, \"time\": 10, \"pressure\": 0.6},\n          {\"x\": 15.2, \"y\": 24.5, \"time\": 20, \"pressure\": 0.65}\n        ],\n        \"duration\": 500,\n        \"point_count\": 45\n      }\n    ],\n    \"stroke_count\": 1,\n    \"total_points\": 45,\n    \"total_time\": 500,\n    \"average_speed\": 0.125,\n    \"average_pressure\": 0.62\n  },\n  \"geolocation\": {\n    \"latitude\": 38.736946,\n    \"longitude\": -9.142685,\n    \"accuracy\": 10\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{consignor_signature_id}}/sign",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{consignor_signature_id}}",
								"sign"
							]
						},
						"description": "**SKIP THIS STEP for remote signatures!**\n\nFor remote (OTP) signatures, the signature is automatically completed when you validate the OTP in step 8. This /sign endpoint is only needed for in_person signatures.\n\n**When is this needed?**\n- in_person: Required (biometric data + signature image)\n- remote: NOT needed (OTP validation completes it)\n- certificate: Required (digital signature data)\n- without_signature: NOT needed (completed on request)\n\n**Since we used remote signature for consignor, skip to step 10.**"
					},
					"response": []
				},
				{
					"name": "10 Request Carrier Signature",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"if (pm.response.code === 201) {",
									"    const response = pm.response.json();",
									"    pm.collectionVariables.set('carrier_signature_id', response.data.id);",
									"    pm.collectionVariables.set('carrier_share_token', response.data.share_token);",
									"    console.log('Carrier signature requested!');",
									"    console.log('Share Token:', response.data.share_token);",
									"    console.log('Signature ID:', response.data.id);",
									"}"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"signatory_role\": \"carrier\",\n  \"signatory_name\": \"John Driver\",\n  \"contact\": \"driver@mycompany.com\",\n  \"signature_type\": \"in_person\",\n  \"preferred_locale\": \"en\"\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures"
							]
						},
						"description": "Request signature from carrier (driver) using in_person biometric signature.\n\n**Signature type: in_person**\n- Driver signs directly on a device (tablet/phone)\n- Captures signature image + biometric data (strokes, pressure, timing)\n- eIDAS-compliant with full audit trail\n- Ideal when the signer is physically present\n\n**What happens:**\n- Signature request created with status 'pending'\n- NO OTP sent (in_person doesn't require email verification)\n- Ready for biometric signing immediately\n\n**Next step:** For in_person signatures you can skip OTP validation and go directly to the /sign endpoint, OR validate OTP first for additional security."
					},
					"response": []
				},
				{
					"name": "11 Validate Carrier OTP",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"otp_code\": \"123456\",\n  \"geolocation\": {\n    \"latitude\": 41.157944,\n    \"longitude\": -8.629105,\n    \"accuracy\": 10\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{carrier_signature_id}}/validate_otp",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{carrier_signature_id}}",
								"validate_otp"
							]
						},
						"description": "**OPTIONAL for in_person signatures!**\n\nFor in_person signatures, OTP validation is optional. You can skip directly to step 12 (/sign) if the driver is physically present and you trust their identity.\n\n**When to use OTP for in_person:**\n- Additional identity verification layer\n- Company policy requires dual-factor\n- Remote supervision of signing process\n\n**For this demo, skip to step 12 (Sign as Carrier).**"
					},
					"response": []
				},
				{
					"name": "12 Sign as Carrier",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"signature_image\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==\",\n  \"biometric_data\": {\n    \"strokes\": [\n      {\n        \"index\": 0,\n        \"points\": [\n          {\"x\": 10.5, \"y\": 20.3, \"time\": 0, \"pressure\": 0.5},\n          {\"x\": 12.8, \"y\": 22.1, \"time\": 10, \"pressure\": 0.6},\n          {\"x\": 15.2, \"y\": 24.5, \"time\": 20, \"pressure\": 0.65}\n        ],\n        \"duration\": 500,\n        \"point_count\": 45\n      }\n    ],\n    \"stroke_count\": 1,\n    \"total_points\": 45,\n    \"total_time\": 500,\n    \"average_speed\": 0.125,\n    \"average_pressure\": 0.62\n  },\n  \"geolocation\": {\n    \"latitude\": 41.157944,\n    \"longitude\": -8.629105,\n    \"accuracy\": 10\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{carrier_signature_id}}/sign",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{carrier_signature_id}}",
								"sign"
							]
						},
						"description": "Complete carrier signature with biometric data (in_person signature).\n\n**Required data for in_person:**\n- signature_image: Base64 PNG of the drawn signature\n- biometric_data: Stroke data including points, timing, and pressure\n- geolocation: GPS coordinates where signing occurred\n\n**Biometric validation:**\n- Minimum 1 stroke with 3+ points\n- Minimum 500ms signing duration (prevents auto-signing)\n- Confidence score calculated from stroke analysis\n\n**What happens:**\n- Biometric data analyzed for authenticity\n- eIDAS-compliant signature created\n- Document snapshot captured\n- If both carrier + consignor signed: Document auto-transitions to 'accepted'\n\n**Next step:** Request consignee signature at delivery (using without_signature type)."
					},
					"response": []
				},
				{
					"name": "13 Request Consignee Signature",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"if (pm.response.code === 201) {",
									"    const response = pm.response.json();",
									"    pm.collectionVariables.set('consignee_signature_id', response.data.id);",
									"    pm.collectionVariables.set('consignee_share_token', response.data.share_token);",
									"    console.log('Consignee signature requested!');",
									"    console.log('Share Token:', response.data.share_token);",
									"    console.log('Signature ID:', response.data.id);",
									"}"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"signatory_role\": \"consignee\",\n  \"signature_type\": \"without_signature\",\n  \"geolocation\": {\n    \"latitude\": 52.520008,\n    \"longitude\": 13.404954,\n    \"accuracy\": 10\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures"
							]
						},
						"description": "Register delivery acknowledgment when consignee cannot sign (without_signature).\n\n**Signature type: without_signature**\n- Used when the counterparty cannot provide a signature\n- Common scenarios: no one present at delivery, refusal to sign, unattended delivery\n- Only allowed for consignor and consignee roles (NOT carrier)\n\n**Required data:**\n- signatory_role: consignee (or consignor)\n- signature_type: without_signature\n- geolocation: GPS coordinates as evidence (required!)\n\n**Optional data:**\n- signatory_name: Name of the person (if known). Can be omitted when person is absent or refuses to identify.\n\n**NO contact needed** - no OTP is sent\n\n**What happens:**\n- Signature immediately completed with status 'completed'\n- Geolocation + timestamp recorded as evidence\n- Document can progress to 'delivered' status\n- Document CANNOT progress to 'completed' (requires proper signatures)\n\n**Important:** Documents with without_signature remain in 'delivered' state permanently. They cannot reach 'completed' status, which requires all three parties to provide actual signatures.\n\n**No further steps needed** - skip to step 18 (Download PDF)."
					},
					"response": []
				},
				{
					"name": "14 Validate Consignee OTP",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"otp_code\": \"123456\",\n  \"geolocation\": {\n    \"latitude\": 52.520008,\n    \"longitude\": 13.404954,\n    \"accuracy\": 10\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{consignee_signature_id}}/validate_otp",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{consignee_signature_id}}",
								"validate_otp"
							]
						},
						"description": "**SKIP THIS STEP for without_signature!**\n\nFor without_signature type used in step 13, the signature is automatically completed on request. No OTP validation needed.\n\n**When is OTP validation needed?**\n- remote: Required (OTP validates and completes)\n- in_person: Optional (additional security layer)\n- certificate: Required (verifies identity before cert signing)\n- without_signature: NOT needed (completed on request)\n\n**Since we used without_signature for consignee, skip to step 18 (Download PDF).**"
					},
					"response": []
				},
				{
					"name": "15 Sign as Consignee",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"signature_image\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==\",\n  \"biometric_data\": {\n    \"strokes\": [\n      {\n        \"index\": 0,\n        \"points\": [\n          {\"x\": 10.5, \"y\": 20.3, \"time\": 0, \"pressure\": 0.5},\n          {\"x\": 12.8, \"y\": 22.1, \"time\": 10, \"pressure\": 0.6},\n          {\"x\": 15.2, \"y\": 24.5, \"time\": 20, \"pressure\": 0.65}\n        ],\n        \"duration\": 500,\n        \"point_count\": 45\n      }\n    ],\n    \"stroke_count\": 1,\n    \"total_points\": 45,\n    \"total_time\": 500,\n    \"average_speed\": 0.125,\n    \"average_pressure\": 0.62\n  },\n  \"geolocation\": {\n    \"latitude\": 52.520008,\n    \"longitude\": 13.404954,\n    \"accuracy\": 10\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{consignee_signature_id}}/sign",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{consignee_signature_id}}",
								"sign"
							]
						},
						"description": "**SKIP THIS STEP for without_signature!**\n\nFor without_signature type used in step 13, the signature is automatically completed on request. No /sign endpoint call needed.\n\n**When is /sign endpoint needed?**\n- in_person: Required (biometric data + signature image)\n- certificate: Required (digital signature data)\n- remote: NOT needed (OTP validation completes it)\n- without_signature: NOT needed (completed on request)\n\n**Since we used without_signature for consignee, skip to step 18.**\n\n**Note about document status:**\nWith without_signature, the document will remain in 'delivered' status. It cannot transition to 'completed' because that requires all three parties to provide actual signatures."
					},
					"response": []
				},
				{
					"name": "16 Update Delivery Issue (After Consignee Signature)",
					"request": {
						"method": "PATCH",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"document\": {\n    \"timestamps\": {\n      \"delivery_started_at\": \"2025-01-22T14:00:00Z\",\n      \"delivery_completed_at\": \"2025-01-22T16:30:00Z\"\n    },\n    \"carrier_observations\": \"Waiting time at loading: 3h30. Consignor warehouse not ready on arrival at 08:00. Loading only started at 10:00. All 10 pallets verified and loaded by 11:30.\\n---\\nDelivery issue: 1 pallet (LOT-2025-DEMO-001-P07) found with water damage during unloading. Consignee refused to acknowledge damage in signature. Damage documented with photos (ref: DMG-20250122-001). Pallet segregated for inspection.\"\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}"
							]
						},
						"description": "**SCENARIO: Carrier adds delivery info after consignee acknowledgment**\n\n**What's happening:**\n- Driver completed delivery with without_signature (no one present to sign)\n- Unloading completed, but driver noticed 1 pallet with damage\n- Carrier documents the issue after the without_signature acknowledgment\n\n**Why document this?**\n- Even with without_signature, carrier can add observations\n- Creates audit trail of delivery conditions\n- Important for insurance/claims documentation\n- The system tracks when observations were added\n\n**Result:** The document shows:\n- Consignee entry has without_signature type with geolocation evidence\n- Carrier observations added with timestamp\n- Full transparency about delivery conditions\n\n**CMR Compliance:** Carrier's reservations are recorded with clear timestamps showing when they were documented."
					},
					"response": []
				},
				{
					"name": "17 Download PDF",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/pdf",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"pdf"
							]
						},
						"description": "Download the eCMR as PDF.\n\n**What you get:**\n- Professional bilingual PDF (carrier language + English)\n- All signatures displayed with their types\n- Geolocation and timestamps for each signature\n- QR code for verification\n\n**For without_signature entries (like our consignee):**\n- Displayed with orange warning: '[ NO SIGNATURE ]'\n- Shows 'Registered at:' timestamp prominently\n- Notes 'Acknowledged with geolocation and timestamp only'\n- GPS coordinates displayed as evidence\n\n**Document status:** 'delivered' (not 'completed' due to without_signature)"
					},
					"response": []
				},
				{
					"name": "18 Download PDF (Bilingual)",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/pdf?locale=de",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"pdf"
							],
							"query": [
								{
									"key": "locale",
									"value": "de",
									"description": "Secondary language for bilingual PDF (pt, en, es, de, fr, it)"
								}
							]
						},
						"description": "Download the eCMR as a bilingual PDF.\n\n**locale parameter:**\n- pt: Portuguese\n- en: English\n- es: Spanish\n- de: German\n- fr: French\n- it: Italian\n\n**What you get:**\n- Bilingual PDF with primary language + selected secondary language\n- Useful for international transport\n- All signatures included"
					},
					"response": []
				},
				{
					"name": "19 Download PDF (As Signed)",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/pdf/consignor",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"pdf",
								"consignor"
							],
							"query": [
								{
									"key": "locale",
									"value": "pt",
									"description": "Secondary language for bilingual PDF (pt, en, es, de, fr, it)",
									"disabled": true
								},
								{
									"key": "inline",
									"value": "true",
									"description": "Display inline in browser instead of download",
									"disabled": true
								}
							]
						},
						"description": "Download the eCMR PDF as it was at the moment a specific role signed.\n\n**URL Format:**\n- `/pdf/consignor` - Document as signed by the consignor (sender)\n- `/pdf/carrier` - Document as signed by the carrier (transporter)\n- `/pdf/consignee` - Document as signed by the consignee (receiver)\n\n**Purpose:**\nEssential for legal disputes and audits. Each signature captures a snapshot of the document at that exact moment. This endpoint generates a PDF showing exactly what that party saw and agreed to when they signed.\n\n**What you get:**\n- Yellow banner indicating 'DOCUMENT AS SIGNED'\n- Shows role, signatory name, and signature timestamp\n- Displays truncated SHA256 hash for integrity verification\n- Gray color scheme (document wasn't 'completed' at signature time)\n- Only signatures that existed at or before this signature moment\n\n**Query parameters:**\n- locale: Secondary language (pt, en, es, de, fr, it)\n- inline: Set to 'true' to display in browser instead of download\n\n**Error responses:**\n- 400 Bad Request: Invalid role (must be consignor, carrier, or consignee)\n- 404 Not Found: No completed signature found for the specified role"
					},
					"response": []
				}
			],
			"description": "Complete walkthrough from signup to completing your first eCMR document.\n\nFollow these requests in order:\n\n1. Create Account\n2. Verify Email & Get API Key\n3. Test API Key\n4. Create Your First eCMR\n5. Issue Document (Draft -> Issued)\n6. Request Consignor Signature\n7. Validate Consignor OTP\n8. Sign as Consignor\n9. Request Carrier Signature\n10. Validate Carrier OTP\n11. Sign as Carrier\n12. Accept by Carrier (Issued -> Accepted)\n13. Request Consignee Signature\n14. Deliver Cargo (Accepted -> Delivered)\n15. Validate Consignee OTP\n16. Sign as Consignee\n17. Complete eCMR (Delivered -> Completed)\n18. Download PDF\n19. Download PDF (Bilingual)\n20. Download PDF (As Signed)"
		},
		{
			"name": "2. Reseller API",
			"description": "For TMS providers and integrators who want to manage multiple client tenants.\n\nResellers can:\n- Create sub-tenants without email verification\n- Receive all webhooks in a centralized endpoint\n- Manage billing for all sub-tenants\n- Each sub-tenant gets its own API key for eCMR operations\n\n**Authentication:** Use X-Master-API-Key header with your reseller master key (rk_live_xxx or rk_test_xxx)",
			"item": [
				{
					"name": "01 Register as Reseller",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"if (pm.response.code === 201) {",
									"    const response = pm.response.json();",
									"    pm.collectionVariables.set('reseller_id', response.data.reseller_id);",
									"    pm.collectionVariables.set('reseller_signup_email', pm.request.body.raw ? JSON.parse(pm.request.body.raw).email : '');",
									"    console.log('Reseller account created! Reseller ID:', response.data.reseller_id);",
									"    console.log('Check your email for verification link');",
									"}"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"email\": \"reseller@tms-company.com\",\n  \"company_name\": \"TMS Integration Company\",\n  \"vat_number\": \"PT999888777\",\n  \"address\": \"Rua das Integrações, 100, 4000-000 Porto, Portugal\",\n  \"country_code\": \"PT\",\n  \"max_tenants\": 100\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/signup",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"signup"
							]
						},
						"description": "Register as an eCMR Reseller to manage multiple client tenants.\n\n**Required fields:**\n- email: Your company billing email\n- company_name: Your TMS/integration company name\n- vat_number: EU VAT number (validated)\n- address: Your company full address\n\n**Optional fields:**\n- country_code: Two-letter ISO country code\n- max_tenants: Maximum number of sub-tenants (default: 100)\n\n**What you get:**\n- Master API key (rk_live_xxx) for managing sub-tenants\n- Centralized webhook endpoint\n- Ability to create sub-tenants without email verification"
					},
					"response": []
				},
				{
					"name": "02 Verify Reseller Email",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"if (pm.response.code === 200) {",
									"    const response = pm.response.json();",
									"    pm.collectionVariables.set('master_api_key', response.data.master_api_key);",
									"    console.log('Reseller email verified!');",
									"    console.log('Master API Key:', response.data.master_api_key);",
									"    console.log('');",
									"    console.log('IMPORTANT: Save your master API key now!');",
									"    console.log('This is the ONLY time it will be shown in full.');",
									"}"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "GET",
						"header": [
							{
								"key": "Accept",
								"value": "application/json"
							}
						],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/signup/verify?token={{reseller_verification_token}}",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"signup",
								"verify"
							],
							"query": [
								{
									"key": "token",
									"value": "{{reseller_verification_token}}",
									"description": "Paste the token from your reseller verification email here"
								}
							]
						},
						"description": "Verify your reseller email address and receive your master API key.\n\n**What you get:**\n- Master API key (rk_live_xxx) - shown only once!\n- Account activated (status: 'active')\n- Ready to create sub-tenants"
					},
					"response": []
				},
				{
					"name": "03 Create Sub-Tenant",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"if (pm.response.code === 201) {",
									"    const response = pm.response.json();",
									"    pm.collectionVariables.set('created_tenant_id', response.data.tenant_id);",
									"    pm.collectionVariables.set('created_tenant_api_key', response.data.api_key);",
									"    console.log('Sub-tenant created!');",
									"    console.log('Tenant ID:', response.data.tenant_id);",
									"    console.log('API Key:', response.data.api_key);",
									"    console.log('');",
									"    console.log('The sub-tenant can now use this API key for eCMR operations.');",
									"}"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"auth": {
							"type": "apikey",
							"apikey": [
								{
									"key": "key",
									"value": "X-Master-API-Key",
									"type": "string"
								},
								{
									"key": "value",
									"value": "{{master_api_key}}",
									"type": "string"
								},
								{
									"key": "in",
									"value": "header",
									"type": "string"
								}
							]
						},
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"company_name\": \"Client Transport ABC\",\n  \"vat_number\": \"PT123456789\",\n  \"company_type\": \"carrier\",\n  \"address\": \"Rua do Cliente, 50, 1000-000 Lisboa, Portugal\",\n  \"email\": \"contact@client-abc.com\",\n  \"reseller_reference\": \"CLI-00123\"\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/tenants",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"tenants"
							]
						},
						"description": "Create a new sub-tenant for one of your clients. No email verification required!\n\n**Required fields:**\n- company_name: Client's company name\n- vat_number: Client's VAT number (validated)\n- company_type: 'consignor', 'consignee', or 'carrier'\n- address: Client's full address\n\n**Optional fields:**\n- email: Client's email (optional for managed tenants)\n- reseller_reference: Your internal reference ID for this client\n\n**Response includes:**\n- tenant_id: The sub-tenant's public ID\n- api_key: The sub-tenant's API key for eCMR operations\n\n**Important:** The API key is returned immediately. The sub-tenant can start using the eCMR API right away."
					},
					"response": []
				},
				{
					"name": "04 List Sub-Tenants",
					"request": {
						"auth": {
							"type": "apikey",
							"apikey": [
								{
									"key": "key",
									"value": "X-Master-API-Key",
									"type": "string"
								},
								{
									"key": "value",
									"value": "{{master_api_key}}",
									"type": "string"
								},
								{
									"key": "in",
									"value": "header",
									"type": "string"
								}
							]
						},
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/tenants?page=1&per_page=20",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"tenants"
							],
							"query": [
								{
									"key": "page",
									"value": "1"
								},
								{
									"key": "per_page",
									"value": "20"
								},
								{
									"key": "status",
									"value": "active",
									"disabled": true
								},
								{
									"key": "search",
									"value": "ABC",
									"disabled": true
								}
							]
						},
						"description": "List all sub-tenants managed by this reseller.\n\n**Optional filters:**\n- status: Filter by status ('active', 'suspended')\n- search: Search by company name\n- page: Page number (default: 1)\n- per_page: Items per page (default: 20)"
					},
					"response": []
				},
				{
					"name": "05 Get Sub-Tenant Details",
					"request": {
						"auth": {
							"type": "apikey",
							"apikey": [
								{
									"key": "key",
									"value": "X-Master-API-Key",
									"type": "string"
								},
								{
									"key": "value",
									"value": "{{master_api_key}}",
									"type": "string"
								},
								{
									"key": "in",
									"value": "header",
									"type": "string"
								}
							]
						},
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/tenants/{{created_tenant_id}}",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"tenants",
								"{{created_tenant_id}}"
							]
						},
						"description": "Get detailed information about a specific sub-tenant, including usage statistics."
					},
					"response": []
				},
				{
					"name": "06 Suspend Sub-Tenant",
					"request": {
						"auth": {
							"type": "apikey",
							"apikey": [
								{
									"key": "key",
									"value": "X-Master-API-Key",
									"type": "string"
								},
								{
									"key": "value",
									"value": "{{master_api_key}}",
									"type": "string"
								},
								{
									"key": "in",
									"value": "header",
									"type": "string"
								}
							]
						},
						"method": "POST",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/tenants/{{created_tenant_id}}/suspend",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"tenants",
								"{{created_tenant_id}}",
								"suspend"
							]
						},
						"description": "Suspend a sub-tenant. The tenant's API key will stop working immediately."
					},
					"response": []
				},
				{
					"name": "07 Activate Sub-Tenant",
					"request": {
						"auth": {
							"type": "apikey",
							"apikey": [
								{
									"key": "key",
									"value": "X-Master-API-Key",
									"type": "string"
								},
								{
									"key": "value",
									"value": "{{master_api_key}}",
									"type": "string"
								},
								{
									"key": "in",
									"value": "header",
									"type": "string"
								}
							]
						},
						"method": "POST",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/tenants/{{created_tenant_id}}/activate",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"tenants",
								"{{created_tenant_id}}",
								"activate"
							]
						},
						"description": "Reactivate a suspended sub-tenant."
					},
					"response": []
				},
				{
					"name": "08 Regenerate Sub-Tenant API Key",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"if (pm.response.code === 200) {",
									"    const response = pm.response.json();",
									"    pm.collectionVariables.set('created_tenant_api_key', response.data.api_key);",
									"    console.log('API key regenerated!');",
									"    console.log('New API Key:', response.data.api_key);",
									"}"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"auth": {
							"type": "apikey",
							"apikey": [
								{
									"key": "key",
									"value": "X-Master-API-Key",
									"type": "string"
								},
								{
									"key": "value",
									"value": "{{master_api_key}}",
									"type": "string"
								},
								{
									"key": "in",
									"value": "header",
									"type": "string"
								}
							]
						},
						"method": "POST",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/tenants/{{created_tenant_id}}/regenerate_api_key",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"tenants",
								"{{created_tenant_id}}",
								"regenerate_api_key"
							]
						},
						"description": "Regenerate the API key for a sub-tenant. The old key will be invalidated immediately."
					},
					"response": []
				},
				{
					"name": "09 Configure Reseller Webhook",
					"request": {
						"auth": {
							"type": "apikey",
							"apikey": [
								{
									"key": "key",
									"value": "X-Master-API-Key",
									"type": "string"
								},
								{
									"key": "value",
									"value": "{{master_api_key}}",
									"type": "string"
								},
								{
									"key": "in",
									"value": "header",
									"type": "string"
								}
							]
						},
						"method": "PUT",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"url\": \"https://your-tms.com/webhooks/ecmr\",\n  \"events\": [\n    \"issued\",\n    \"accepted\",\n    \"delivered\",\n    \"completed\",\n    \"cancelled\"\n  ]\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/webhook",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"webhook"
							]
						},
						"description": "Configure a centralized webhook endpoint to receive events from ALL sub-tenants.\n\n**Webhook payload includes:**\n- reseller_id: Your reseller ID\n- tenant_id: The sub-tenant's ID\n- reseller_reference: Your internal reference for the client\n- vat_number: The sub-tenant's VAT number\n- Event-specific data\n\nThis allows you to route events to the correct internal system based on your reference."
					},
					"response": []
				},
				{
					"name": "10 Test Reseller Webhook",
					"request": {
						"auth": {
							"type": "apikey",
							"apikey": [
								{
									"key": "key",
									"value": "X-Master-API-Key",
									"type": "string"
								},
								{
									"key": "value",
									"value": "{{master_api_key}}",
									"type": "string"
								},
								{
									"key": "in",
									"value": "header",
									"type": "string"
								}
							]
						},
						"method": "POST",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/webhook/test",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"webhook",
								"test"
							]
						},
						"description": "Send a test webhook to your configured endpoint to verify it's working correctly."
					},
					"response": []
				},
				{
					"name": "11 View Reseller Profile",
					"request": {
						"auth": {
							"type": "apikey",
							"apikey": [
								{
									"key": "key",
									"value": "X-Master-API-Key",
									"type": "string"
								},
								{
									"key": "value",
									"value": "{{master_api_key}}",
									"type": "string"
								},
								{
									"key": "in",
									"value": "header",
									"type": "string"
								}
							]
						},
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/profile",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"profile"
							]
						},
						"description": "Get your reseller profile including tenant limits and usage statistics."
					},
					"response": []
				},
				{
					"name": "12 View Aggregated Usage",
					"request": {
						"auth": {
							"type": "apikey",
							"apikey": [
								{
									"key": "key",
									"value": "X-Master-API-Key",
									"type": "string"
								},
								{
									"key": "value",
									"value": "{{master_api_key}}",
									"type": "string"
								},
								{
									"key": "in",
									"value": "header",
									"type": "string"
								}
							]
						},
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/billing/usage?start_date=2025-01-01&end_date=2025-01-31",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"billing",
								"usage"
							],
							"query": [
								{
									"key": "start_date",
									"value": "2025-01-01"
								},
								{
									"key": "end_date",
									"value": "2025-01-31"
								}
							]
						},
						"description": "Get aggregated usage statistics across all sub-tenants for billing purposes.\n\n**Query parameters:**\n- start_date: Period start (YYYY-MM-DD)\n- end_date: Period end (YYYY-MM-DD)\n\nDefaults to current month if not specified."
					},
					"response": []
				},
				{
					"name": "13 Brand the Reseller",
					"request": {
						"auth": {
							"type": "apikey",
							"apikey": [
								{
									"key": "key",
									"value": "X-Master-API-Key",
									"type": "string"
								},
								{
									"key": "value",
									"value": "{{master_api_key}}",
									"type": "string"
								},
								{
									"key": "in",
									"value": "header",
									"type": "string"
								}
							]
						},
						"method": "PUT",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"name\": \"Iberian TMS\",\n  \"logo\": \"iVBORw0KGgoAAAANSUhEUg...\",\n  \"logo_content_type\": \"image/png\",\n  \"metadata\": {\n    \"promotion\": {\n      \"name\": \"Iberian TMS\",\n      \"url\": \"https://iberian.example/ecmr\",\n      \"enabled\": true\n    }\n  }\n}"
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/profile",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"profile"
							]
						},
						"description": "Put your own mark on the documents of every tenant you manage. The logo is base64 (PNG or JPEG, under 1 MB) and replaces the platform logo on generated PDFs. The promotion block inside metadata drives the invitation shown to counterparties on documents and emails - set enabled to false to switch it off."
					},
					"response": []
				},
				{
					"name": "14 Sync Documents Across Sub-Tenants",
					"request": {
						"auth": {
							"type": "apikey",
							"apikey": [
								{
									"key": "key",
									"value": "X-Master-API-Key",
									"type": "string"
								},
								{
									"key": "value",
									"value": "{{master_api_key}}",
									"type": "string"
								},
								{
									"key": "in",
									"value": "header",
									"type": "string"
								}
							]
						},
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/documents/changes?updated_after=2025-01-01T00:00:00Z&per_page=200",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"documents",
								"changes"
							],
							"query": [
								{
									"key": "updated_after",
									"value": "2025-01-01T00:00:00Z"
								},
								{
									"key": "per_page",
									"value": "200"
								},
								{
									"key": "cursor",
									"value": "",
									"disabled": true
								}
							]
						},
						"description": "Every CMR and DeCA of every sub-tenant changed after a point, oldest change first, with the full detail (parties, transport, goods) and the `tenant_id` of each document. Keeps a reseller's copy in sync with one request instead of one per sub-tenant.\n\n**Query parameters:**\n- cursor: The `meta.next_cursor` of the previous answer (takes precedence)\n- updated_after: First poll only, an ISO 8601 instant\n- per_page: 1 to 500, default 200\n\nKeep asking with the new cursor while `meta.has_more` is true. When it is false you are up to date; store `meta.next_cursor` for the next poll."
					},
					"response": []
				}
			]
		},
		{
			"name": "3. DeCA (Espana)",
			"description": "Spain's **documento electronico de control administrativo**, mandatory for domestic transport (national and cabotage) from **5 October 2026** under the Ley 9/2025 and the Resolucion of 5 June 2026. International transport is excluded and stays under the e-CMR Protocol.\n\nFour steps, in order: create the DeCA, read it back, download the PDF under your API key, and finally fetch the public URL the QR encodes — the one that needs no credentials at all.\n\n**What a DeCA is, in one line:** the eCMR data without the states and without the signatures, plus a mandatory vehicle and the *cargador contractual*.\n",
			"item": [
				{
					"name": "01 Create DeCA",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"if (pm.response.code === 201) {",
									"    const response = pm.response.json();",
									"    pm.collectionVariables.set('deca_number', response.data.deca_number);",
									"    pm.collectionVariables.set('deca_download_url', response.data.deca.download_url);",
									"    pm.collectionVariables.set('deca_view_url', response.data.deca.view_url);",
									"    console.log('DeCA created!');",
									"    console.log('Number:', response.data.deca_number);",
									"    console.log('Public QR URL:', response.data.deca.download_url);",
									"    console.log('Page for the driver:', response.data.deca.view_url);",
									"    console.log('Expires at:', response.data.deca.expires_at);",
									"}"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"deca\": {\n    \"external_reference\": \"OT-2026-0042\",\n    \"contractual_shipper\": {\n      \"name\": \"Transitario Iberico SL\",\n      \"vat_number\": \"ESB12345678\",\n      \"address\": \"Calle del Cargador, 15\",\n      \"city\": \"Madrid\",\n      \"postal_code\": \"28001\",\n      \"country\": \"ES\"\n    },\n    \"consignor\": {\n      \"name\": \"Bodegas del Ebro SA\",\n      \"address\": \"Camino del Vino, 3\",\n      \"city\": \"Logrono\",\n      \"postal_code\": \"26001\",\n      \"country\": \"ES\"\n    },\n    \"consignee\": {\n      \"name\": \"Distribuciones Levante SL\",\n      \"address\": \"Poligono Sur, 12\",\n      \"city\": \"Valencia\",\n      \"postal_code\": \"46001\",\n      \"country\": \"ES\"\n    },\n    \"goods\": [\n      {\n        \"description\": \"Pales de bebidas\",\n        \"quantity\": 12,\n        \"packaging\": \"pallets\",\n        \"weight_kg\": 8400\n      }\n    ],\n    \"transport\": {\n      \"vehicle_registration\": \"1234 ABC\",\n      \"trailer_registration\": \"R1234BCD\",\n      \"articulated\": true,\n      \"special_authorization\": \"AEC-2026-00123\",\n      \"loading_date\": \"2026-10-06\",\n      \"loading_place_name\": \"Bodega Logrono\",\n      \"loading_place_address\": \"Camino del Vino, 3, 26001 Logrono\",\n      \"loading_place_country\": \"ES\",\n      \"delivery_date\": \"2026-10-07\",\n      \"delivery_place_name\": \"Almacen Valencia\",\n      \"delivery_place_address\": \"Poligono Sur, 12, 46001 Valencia\",\n      \"delivery_place_country\": \"ES\"\n    },\n    \"consignor_observations\": \"Carga paletizada. No apilar.\",\n    \"metadata\": {\n      \"demo\": true\n    }\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/deca",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"deca"
							]
						},
						"description": "Creates a Spanish **documento electronico de control administrativo** and generates its PDF straight away, before the service starts, as the Resolucion of 5 June 2026 requires.\n\n**A DeCA is not an eCMR.** It has no states and no signatures — *\"La firma de los documentos de control administrativo no es obligatoria\"*. What it does have is a mandatory vehicle registration, and a mandatory trailer registration when `transport.articulated` is `true`.\n\n**The two fields an eCMR does not carry:**\n- `contractual_shipper` — the *cargador contractual* of Orden FOM/2861/2012 art. 6 a): whoever contracts directly with the effective carrier. That may be a freight forwarder or a logistics operator, so it is not necessarily the consignor. Name, VAT number and address are all required. Omit the object and the consignor is used instead, in which case the consignor must carry those three fields.\n- `transport.special_authorization` — art. 6 e), for special transports only.\n\n**What comes back.** `data.deca.download_url` is the address encoded in the QR code printed on the PDF. It is public by design: opening it downloads the file directly, with no page, no login and no redirect, which is exactly what the Resolucion demands of a roadside check. It stops working seven days after the later of the loading and delivery dates.\n\n`data.deca.qr_code_base64` is the same QR as a PNG data URL, if you want to print it yourself.\n\n**Your tenant's own party is filled in automatically**, as with an eCMR. Sending its base fields is rejected.\n"
					},
					"response": []
				},
				{
					"name": "02 Get DeCA",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/deca/{{deca_number}}",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"deca",
								"{{deca_number}}"
							]
						},
						"description": "Reads back the DeCA created in step 01, including the `deca` block with the public download URL, the QR, the expiry and the PDF's size and SHA-256.\n\nNote that a DeCA never shows up under `/api/v1/ecmr/documents`: the two sets of endpoints are kept apart on purpose."
					},
					"response": []
				},
				{
					"name": "03 Download PDF",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/deca/{{deca_number}}/pdf",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"deca",
								"{{deca_number}}",
								"pdf"
							]
						},
						"description": "Downloads the stored file under your API key — the same bytes the public QR URL serves.\n\nThe PDF looks like an eCMR without the signature area: the CMR grid, the goods table, the VAT numbers the Orden requires, and a footer carrying the QR code, the public URL and the legal reference. Always a native PDF, always under the 5 MB the Resolucion allows.\n\nAdd `?view=true` to get it inline instead of as an attachment."
					},
					"response": []
				},
				{
					"name": "04 Public QR download (no API key)",
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{deca_download_url}}",
							"host": [
								"{{deca_download_url}}"
							]
						},
						"description": "This is the request the enforcement officer's phone makes when it reads the QR on the PDF.\n\n**It carries no API key**, and it must not: *\"no sera valida ninguna URL que dirija a una pagina web que requiera credenciales o autenticacion\"*. The response is the PDF itself — no HTML page, no redirect, no button.\n\nThe variable `deca_download_url` is set by step 01.\n\nOnce the access expires the same address answers `410 Gone` with an empty body. An address that never existed answers `404`. The access lasts a year past the transport, and `08` moves it further without changing the token."
					},
					"response": []
				},
				{
					"name": "05 Create DeCA (subcontracted)",
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"if (pm.response.code === 201) {",
									"    const response = pm.response.json();",
									"    console.log('Subcontracted DeCA created!');",
									"    console.log('Carrier:', response.data.carrier.name);",
									"    console.log('Contractual shipper:', response.data.contractual_shipper.name);",
									"}"
								],
								"type": "text/javascript"
							}
						}
					],
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"deca\": {\n    \"external_reference\": \"OT-2026-0043\",\n    \"subcontracted\": true,\n    \"consignor\": {\n      \"name\": \"Bodegas del Ebro SA\",\n      \"address\": \"Camino del Vino, 3\",\n      \"city\": \"Logrono\",\n      \"postal_code\": \"26001\",\n      \"country\": \"ES\"\n    },\n    \"carrier\": {\n      \"name\": \"Transportes Subcontratados SL\",\n      \"vat_number\": \"ESB99999999\",\n      \"address\": \"Poligono Norte, 4\",\n      \"city\": \"Zaragoza\",\n      \"postal_code\": \"50001\",\n      \"country\": \"ES\",\n      \"email\": \"trafico@subcontratados.test\"\n    },\n    \"consignee\": {\n      \"name\": \"Distribuciones Levante SL\",\n      \"address\": \"Poligono Sur, 12\",\n      \"city\": \"Valencia\",\n      \"postal_code\": \"46001\",\n      \"country\": \"ES\"\n    },\n    \"goods\": [\n      {\n        \"description\": \"Pales de bebidas\",\n        \"quantity\": 12,\n        \"packaging\": \"pallets\",\n        \"weight_kg\": 8400\n      }\n    ],\n    \"transport\": {\n      \"vehicle_registration\": \"5678 DEF\",\n      \"loading_date\": \"2026-10-06\",\n      \"loading_place_name\": \"Bodega Logrono\",\n      \"loading_place_address\": \"Camino del Vino, 3, 26001 Logrono\",\n      \"loading_place_country\": \"ES\",\n      \"delivery_date\": \"2026-10-07\",\n      \"delivery_place_name\": \"Almacen Valencia\",\n      \"delivery_place_address\": \"Poligono Sur, 12, 46001 Valencia\",\n      \"delivery_place_country\": \"ES\"\n    },\n    \"metadata\": {\n      \"demo\": true\n    }\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/deca",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"deca"
							]
						},
						"description": "Creates a DeCA for a trip you **subcontracted**.\n\nYour company is registered as `carrier`, so by default its own details fill the carrier box and sending them yourself is rejected. `subcontracted: true` says you are not the one driving: the `carrier` object is then taken exactly as sent, and your company fills the `contractual_shipper` instead — the *cargador contractual* of Orden FOM/2861/2012 art. 6 a), which is whoever contracted the transport.\n\nSend a `contractual_shipper` of your own and it wins; this example omits it to show the fallback.\n\nA subcontracted carrier needs a name, a VAT number (art. 6 b), an address, and an email or phone."
					},
					"response": []
				},
				{
					"name": "06 List DeCA",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/deca?page=1&per_page=20&search=acme",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"deca"
							],
							"query": [
								{
									"key": "page",
									"value": "1"
								},
								{
									"key": "per_page",
									"value": "20"
								},
								{
									"key": "search",
									"value": "acme",
									"disabled": true,
									"description": "Free text over the number, the external reference and the names of the parties"
								},
								{
									"key": "updated_after",
									"value": "2025-01-01T00:00:00Z",
									"disabled": true,
									"description": "Only DeCA changed after this instant"
								}
							]
						},
						"description": "Every DeCA of this tenant, newest first. The QR image is left out of the listing - ask for a single DeCA to get it."
					},
					"response": []
				},
				{
					"name": "07 Public page for the driver (no API key)",
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{deca_view_url}}",
							"host": [
								"{{deca_view_url}}"
							]
						},
						"description": "The same token as `04`, opened as a page instead of a file. **This is the link to send a driver** by SMS or email.\n\nIt answers HTML: the DeCA on screen, the QR code at a size that can be read off the phone, and the PDF openable in the browser (`/ver/pdf`, served inline). Hand a phone the download URL of `04` instead and it asks where to save a file — which is the wrong thing to be holding with an officer in front of you.\n\nThe QR drawn on this page still encodes the direct download of `04`, so enforcement scans the screen and receives the binary the Resolucion requires. The page carries no API key, is served in Spanish by default, and is kept out of search indexes.\n\nAn expired token answers `410` with a page that says so and who to ask for a new link, instead of an empty error."
					},
					"response": []
				},
				{
					"name": "08 Extend the public access",
					"request": {
						"method": "PATCH",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{}"
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/deca/{{deca_number}}/access",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"deca",
								"{{deca_number}}",
								"access"
							]
						},
						"description": "Moves the expiry of the public addresses forward **keeping the same token**, so the QR already printed on the PDF and already in a driver's hands goes on working. No PDF is regenerated.\n\nSend it with an empty body for another year from now, or with `{\"expires_at\": \"2027-12-31T23:59:59Z\"}` for a date of your own. An earlier date is ignored: the expiry only ever moves forward.\n\nUse it when a document is asked for after the access lapsed — an inspection, an audit, a claim."
					},
					"response": []
				}
			]
		},
		{
			"name": "Account Management",
			"item": [
				{
					"name": "Create Account",
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"email\": \"developer@mycompany.com\",\n  \"company_name\": \"My Transport Company\",\n  \"vat_number\": \"PT123456789\",\n  \"company_type\": \"carrier\",\n  \"address\": \"Rua do Transporte, 123, 4100-000 Porto, Portugal\",\n  \"country_code\": \"PT\"\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/signup",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"signup"
							]
						},
						"description": "Create a new eCMR API account. You'll receive a verification email with a link to activate your account and get your API key.\n\n**Required fields:**\n- email: Your company email\n- company_name: Your company name\n- vat_number: EU VAT number (validated)\n- company_type: Your role in eCMRs - 'consignor', 'consignee', or 'carrier'\n- address: Your company full address\n\n**Optional:**\n- country_code: Two-letter ISO country code (auto-detected from VAT if not provided)\n- rate_limit_per_minute: Custom rate limit (default: 60)\n\n**What happens:**\n1. Account created with status 'pending_verification'\n2. Verification email sent to your inbox\n3. You get a tenant_id\n4. Token expires in 24 hours\n\n**Rate limit:** 5 signups per IP per hour"
					},
					"response": []
				},
				{
					"name": "Verify Email",
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "GET",
						"header": [
							{
								"key": "Accept",
								"value": "application/json"
							}
						],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/signup/verify?token=YOUR_VERIFICATION_TOKEN",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"signup",
								"verify"
							],
							"query": [
								{
									"key": "token",
									"value": "YOUR_VERIFICATION_TOKEN",
									"description": "Token from verification email"
								}
							]
						},
						"description": "Verify your email address and activate your account.\n\n**How to get the token:**\n1. Check your email inbox\n2. Open the verification email from ecmr@40pes.pt\n3. Click the link or copy the token from the URL\n\n**What you get:**\n- Your API key (shown only once!)\n- Account activated (status: 'active')\n- Rate limit: 60 requests/minute (default)\n- Ready to use the API\n\n**Browser access:**\nIf you click the link in your browser, you'll see a nice landing page with your API key and quick start instructions.\n\n**API access:**\nIf you use Accept: application/json header, you'll get JSON response with your API key."
					},
					"response": []
				},
				{
					"name": "Resend Verification Email",
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"email\": \"developer@mycompany.com\"\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/signup/resend_verification",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"signup",
								"resend_verification"
							]
						},
						"description": "Resend the verification email if:\n- You didn't receive the original email\n- The verification link expired (24h expiration)\n- You deleted the email accidentally\n\n**What happens:**\n- New verification token generated\n- New email sent\n- Old token invalidated\n- New token expires in 24 hours"
					},
					"response": []
				}
			],
			"description": "Signup, verification, and account management (no authentication required)"
		},
		{
			"name": "API Status",
			"item": [
				{
					"name": "Health Check",
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/health",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"health"
							]
						},
						"description": "Check API health status. Returns service health, database connectivity, and response times. No authentication required."
					},
					"response": []
				},
				{
					"name": "API Version",
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/version",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"version"
							]
						},
						"description": "Get API version information including supported versions and deprecations. No authentication required."
					},
					"response": []
				}
			],
			"description": "Health checks and API version information"
		},
		{
			"name": "Documents",
			"item": [
				{
					"name": "List Documents",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents?page=1&per_page=20&search=acme",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents"
							],
							"query": [
								{
									"key": "page",
									"value": "1",
									"description": "Page number (default: 1)"
								},
								{
									"key": "per_page",
									"value": "20",
									"description": "Items per page (default: 20)"
								},
								{
									"key": "status",
									"value": "issued",
									"description": "Filter by status: draft, issued, accepted, delivered, completed, cancelled",
									"disabled": true
								},
								{
									"key": "external_reference",
									"value": "REF-001",
									"description": "Filter by external reference",
									"disabled": true
								},
								{
									"key": "created_after",
									"value": "2025-01-01",
									"description": "Filter documents created after this date",
									"disabled": true
								},
								{
									"key": "created_before",
									"value": "2025-12-31",
									"description": "Filter documents created before this date",
									"disabled": true
								},
								{
									"key": "updated_after",
									"value": "2025-01-01T00:00:00Z",
									"description": "Only documents changed after this instant - to catch up on missed webhooks, with sort_by=updated_at&sort_order=asc",
									"disabled": true
								},
								{
									"key": "sort_by",
									"value": "created_at",
									"description": "Sort field: created_at, updated_at, cmr_number, status, issued_at, completed_at",
									"disabled": true
								},
								{
									"key": "sort_order",
									"value": "desc",
									"description": "Sort order: asc or desc",
									"disabled": true
								},
								{
									"key": "search",
									"value": "acme",
									"disabled": true,
									"description": "Free text over the number, the external reference and the names of the parties"
								}
							]
						},
						"description": "List all eCMR documents for the authenticated tenant with optional filtering and pagination."
					},
					"response": []
				},
				{
					"name": "Get Document",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}"
							]
						},
						"description": "Get detailed information about a specific eCMR document including all parties, goods, transport details, and signatures."
					},
					"response": []
				},
				{
					"name": "Create Document",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"document\": {\n    \"external_reference\": \"REF-001\",\n    \"carrier\": {\n      \"name\": \"40 PES Transport, Lda\",\n      \"address\": \"Rua do Transporte, 123, 4100-000 Porto, Portugal\",\n      \"vat_number\": \"PT123456789\",\n      \"country\": \"PT\",\n      \"phone\": \"+351910000001\",\n      \"email\": \"carrier@40pes.pt\"\n    },\n    \"consignor\": {\n      \"name\": \"Empresa Expedidora, Lda\",\n      \"address\": \"Av. da Expedicao, 456, 1000-000 Lisboa, Portugal\",\n      \"country\": \"PT\",\n      \"vat_number\": \"PT987654321\",\n      \"email\": \"expedidor@exemplo.pt\",\n      \"phone\": \"+351912345678\",\n      \"contact_person\": \"Joao Expedidor\"\n    },\n    \"consignee\": {\n      \"name\": \"Receiver GmbH\",\n      \"address\": \"Empfangerstrasse 789, 10115 Berlin, Germany\",\n      \"country\": \"DE\",\n      \"vat_number\": \"DE123456789\",\n      \"email\": \"receiver@example.de\",\n      \"phone\": \"+49301234567\",\n      \"contact_person\": \"Hans Muller\"\n    },\n    \"goods\": [\n      {\n        \"description\": \"Pecas metalicas para industria automovel\",\n        \"quantity\": 50,\n        \"packaging\": \"pallets\",\n        \"weight_kg\": 12500,\n        \"volume_m3\": 60,\n        \"marks_and_numbers\": \"LOT-2025-001\",\n        \"statistical_number\": \"7326.90\",\n        \"dangerous_goods\": false\n      }\n    ],\n    \"transport\": {\n      \"vehicle_registration\": \"12-AB-34\",\n      \"trailer_registration\": \"56-CD-78\",\n      \"driver_name\": \"Manuel Silva\",\n      \"driver_license\": \"P-1234567\",\n      \"driver_phone\": \"+351935794288\",\n      \"loading_place_name\": \"Armazém Central\",\n      \"loading_place_address\": \"Lisboa, Portugal\",\n      \"delivery_place_name\": \"Centro Logístico\",\n      \"delivery_place_address\": \"Berlin, Germany\",\n      \"loading_date\": \"2025-01-20\",\n      \"delivery_date\": \"2025-01-22\",\n      \"successive_carriers\": []\n    },\n    \"consignor_observations\": \"Manter seco. Nao empilhar mais de 3 paletes. Handle with care.\",\n    \"instructions\": {\n      \"observations\": \"Carga lateral obrigatoria. Nao empilhar mais de 2 paletes.\",\n      \"payment_terms\": \"collect\",\n      \"special_agreements\": \"FOB Lisboa\",\n      \"cash_on_delivery\": null,\n      \"documents_attached\": [\"Invoice\", \"Packing List\"]\n    },\n    \"metadata\": {\n      \"internal_ref\": \"PO-2025-001\",\n      \"customer_id\": \"CUST-123\",\n      \"priority\": \"normal\"\n    }\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents"
							]
						},
						"description": "Create a new eCMR document. The document will be created in 'draft' status and must be issued to become active."
					},
					"response": []
				},
				{
					"name": "Update Document",
					"request": {
						"method": "PUT",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"document\": {\n    \"goods\": [\n      {\n        \"description\": \"Pecas metalicas para industria automovel - ATUALIZADO\",\n        \"quantity\": 50,\n        \"packaging\": \"pallets\",\n        \"weight_kg\": 12600,\n        \"volume_m3\": 62,\n        \"marks_and_numbers\": \"LOT-2025-001\"\n      }\n    ],\n    \"consignor_observations\": \"2 paletes com embalagem reforcada devido a fragilidade. Mercadoria verificada e conforme. Manter seco. Handle with extreme care.\",\n    \"metadata\": {\n      \"internal_ref\": \"PO-2025-001-REV1\",\n      \"updated_reason\": \"Weight correction after verification\"\n    }\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}"
							]
						},
						"description": "Update an existing eCMR document. Only include the fields you want to update.\n\n**consignor_observations** (Box 13): Sender can add observations about the goods until they sign. This is a top-level field."
					},
					"response": []
				},
				{
					"name": "Delete Document",
					"request": {
						"method": "DELETE",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}"
							]
						},
						"description": "Delete an eCMR document. Only draft documents can be deleted."
					},
					"response": []
				},
				{
					"name": "Search Documents",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/search?q=Berlin",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"search"
							],
							"query": [
								{
									"key": "q",
									"value": "Berlin",
									"description": "Search query (searches in CMR number, parties, goods description, etc.)"
								}
							]
						},
						"description": "Search for eCMR documents by keyword. Searches across CMR number, party names, goods description, etc."
					},
					"response": []
				},
				{
					"name": "Get Statistics",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/statistics",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"statistics"
							]
						},
						"description": "Get statistics about eCMR documents for the tenant including counts by status, this month's documents, etc."
					},
					"response": []
				},
				{
					"name": "Download PDF",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/pdf?inline=false",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"pdf"
							],
							"query": [
								{
									"key": "inline",
									"value": "false",
									"description": "Set to 'true' for inline display, 'false' for download attachment"
								},
								{
									"key": "locale",
									"value": "en",
									"description": "Secondary language for bilingual PDF (pt, en, es, de, fr, it)",
									"disabled": true
								}
							]
						},
						"description": "Download the eCMR document as a PDF file. Includes all document data, parties, goods, transport details, and signatures.\n\n**Query Parameters:**\n- inline: Set to 'true' for inline display, 'false' for download (default)\n- locale: Secondary language for bilingual PDF. Available: pt, en, es, de, fr, it"
					},
					"response": []
				},
				{
					"name": "Download PDF (Bilingual)",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/pdf?locale=de",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"pdf"
							],
							"query": [
								{
									"key": "locale",
									"value": "de",
									"description": "Secondary language for bilingual PDF (pt, en, es, de, fr, it)"
								}
							]
						},
						"description": "Download the eCMR document as a bilingual PDF. The PDF will include the primary language and the specified secondary language.\n\n**Available locales:**\n- pt: Portuguese\n- en: English\n- es: Spanish\n- de: German\n- fr: French\n- it: Italian\n\n**Use cases:**\n- International transport between countries with different languages\n- Authorities inspection in destination country\n- Customer documentation requirements"
					},
					"response": []
				},
				{
					"name": "Get Document History",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/history",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"history"
							]
						},
						"description": "Get the complete audit trail/history of all changes made to the document. Shows version history with timestamps and changes."
					},
					"response": []
				},
				{
					"name": "Check Compliance",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/compliance",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"compliance"
							]
						},
						"description": "Check the document's compliance with CMR Convention and eIDAS requirements. Returns detailed compliance status for each check."
					},
					"response": []
				},
				{
					"name": "Validate Document Data",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"document\": {\n    \"external_reference\": \"REF-001\",\n    \"carrier\": {\n      \"name\": \"40 PES Transport, Lda\",\n      \"vat\": \"PT123456789\"\n    },\n    \"consignor\": {\n      \"name\": \"Empresa Expedidora, Lda\"\n    },\n    \"consignee\": {\n      \"name\": \"Receiver GmbH\"\n    },\n    \"goods\": [\n      {\n        \"description\": \"Pecas metalicas\",\n        \"quantity\": 1,\n        \"packaging\": \"pallets\",\n        \"weight_kg\": 100\n      }\n    ],\n    \"transport\": {\n      \"vehicle_plate\": \"12-AB-34\"\n    }\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/validate",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"validate"
							]
						},
						"description": "Validate document data before creating. Returns validation errors and warnings without creating the document."
					},
					"response": []
				}
			],
			"description": "CRUD operations for eCMR documents"
		},
		{
			"name": "Error Codes Reference",
			"item": [
				{
					"name": "Authentication Errors (AUTH_*)",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/tenant/profile",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"tenant",
								"profile"
							]
						},
						"description": "## Authentication Error Codes\n\nThese errors occur during API authentication.\n\n### Tenant API Authentication\n\n| Code | HTTP Status | Description |\n|------|-------------|-------------|\n| `AUTH_NO_CREDENTIALS` | 401 | No API key or JWT token provided |\n| `AUTH_INVALID_API_KEY` | 401 | The API key is invalid or not found |\n| `AUTH_INVALID_JWT` | 401 | The JWT token is invalid |\n| `AUTH_JWT_DECODE_ERROR` | 401 | Failed to decode JWT token |\n| `AUTH_JWT_EXPIRED` | 401 | JWT token has expired |\n| `AUTH_TENANT_INACTIVE` | 401 | Tenant account is inactive |\n| `RATE_LIMIT_EXCEEDED` | 429 | Too many requests, rate limit exceeded |\n\n### Reseller API Authentication\n\n| Code | HTTP Status | Description |\n|------|-------------|-------------|\n| `AUTH_INVALID_MASTER_API_KEY` | 401 | The master API key is invalid or not found |\n| `AUTH_RESELLER_INACTIVE` | 401 | Reseller account is inactive |\n\n**Example Error Response:**\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"AUTH_INVALID_API_KEY\",\n    \"message\": \"Invalid API key\",\n    \"details\": []\n  }\n}\n```"
					},
					"response": []
				},
				{
					"name": "Document Errors (DOCUMENT_*)",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}"
							]
						},
						"description": "## Document Error Codes\n\nThese errors occur when working with eCMR documents.\n\n| Code | HTTP Status | Description |\n|------|-------------|-------------|\n| `DOCUMENT_NOT_FOUND` | 404 | Document with specified CMR number not found |\n| `DOCUMENT_NOT_EDITABLE` | 422 | Document cannot be edited (already issued) |\n| `DOCUMENT_NOT_DELETABLE` | 422 | Document cannot be deleted (already issued) |\n| `DOCUMENT_VALIDATION_FAILED` | 422 | Document data validation failed |\n\n**Example Error Response:**\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"DOCUMENT_NOT_EDITABLE\",\n    \"message\": \"Cannot update document after it has been issued\",\n    \"details\": []\n  }\n}\n```"
					},
					"response": []
				},
				{
					"name": "Lifecycle Errors (LIFECYCLE_*)",
					"request": {
						"method": "POST",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/lifecycle/issue",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"lifecycle",
								"issue"
							]
						},
						"description": "## Lifecycle Error Codes\n\nThese errors occur during document state transitions.\n\n| Code | HTTP Status | Description |\n|------|-------------|-------------|\n| `LIFECYCLE_INVALID_STATE` | 422 | Document is not in the expected state |\n| `LIFECYCLE_INVALID_TRANSITION` | 422 | State transition not allowed |\n| `LIFECYCLE_INVALID_DOCUMENT_STATE` | 422 | Cannot request signatures on a document that is not issued or accepted |\n| `LIFECYCLE_ISSUE_FAILED` | 422 | Failed to issue document |\n| `LIFECYCLE_ACCEPT_FAILED` | 422 | Failed to accept document |\n| `LIFECYCLE_DELIVER_FAILED` | 422 | Failed to deliver document |\n| `LIFECYCLE_COMPLETE_FAILED` | 422 | Failed to complete document |\n| `LIFECYCLE_CANCEL_FAILED` | 422 | Failed to cancel document |\n| `LIFECYCLE_CANNOT_CANCEL_COMPLETED` | 422 | Cannot cancel a completed document |\n| `LIFECYCLE_SIGNATURE_REQUIRED` | 422 | Required signature missing before transition |\n| `LIFECYCLE_SIGNATURES_INCOMPLETE` | 422 | Not all required signatures are complete |\n| `LIFECYCLE_ALREADY_SIGNED` | 422 | This role has already signed |\n| `LIFECYCLE_SIGNATURE_PENDING` | 422 | A signature request is already pending |\n| `LIFECYCLE_GOODS_INCOMPLETE` | 422 | Goods data is incomplete, cannot request signature |\n| `LIFECYCLE_OTP_FAILED` | 422 | Failed to send signature request OTP |\n\n**Example Error Response:**\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"LIFECYCLE_SIGNATURE_REQUIRED\",\n    \"message\": \"Carrier signature is required before this transition\",\n    \"details\": []\n  }\n}\n```"
					},
					"response": []
				},
				{
					"name": "Signature Errors (SIGNATURE_*)",
					"request": {
						"method": "POST",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{signature_id}}/sign",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{signature_id}}",
								"sign"
							]
						},
						"description": "## Signature Error Codes\n\nThese errors occur during signature operations.\n\n| Code | HTTP Status | Description |\n|------|-------------|-------------|\n| `SIGNATURE_NOT_FOUND` | 404 | Signature not found |\n| `SIGNATURE_ALREADY_COMPLETED` | 422 | Signature has already been completed |\n| `SIGNATURE_FAILED` | 422 | Signature failed and cannot be retried |\n| `SIGNATURE_EXPIRED` | 422 | Signature request has expired |\n| `SIGNATURE_CANCELLED` | 422 | Signature was cancelled |\n| `SIGNATURE_TYPE_INVALID` | 422 | Invalid signature type for this operation |\n| `SIGNATURE_TYPE_UNKNOWN` | 400 | Unknown signature type |\n| `SIGNATURE_OTP_REQUIRED` | 422 | OTP must be validated before signing |\n| `INVALID_ROLE_FOR_WITHOUT_SIGNATURE` | 422 | without_signature type only allowed for consignor or consignee |\n| `GEOLOCATION_REQUIRED` | 422 | Geolocation is required for without_signature type |\n\n**Example Error Response:**\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"SIGNATURE_ALREADY_COMPLETED\",\n    \"message\": \"Signature already completed\",\n    \"details\": []\n  }\n}\n```"
					},
					"response": []
				},
				{
					"name": "OTP Errors (OTP_*)",
					"request": {
						"method": "POST",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{signature_id}}/validate_otp",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{signature_id}}",
								"validate_otp"
							]
						},
						"description": "## OTP Error Codes\n\nThese errors occur during OTP (One-Time Password) validation.\n\n| Code | HTTP Status | Description |\n|------|-------------|-------------|\n| `OTP_EXPIRED` | 422 | OTP code has expired (15 min validity) |\n| `OTP_INVALID` | 422 | OTP code is incorrect |\n| `OTP_MAX_ATTEMPTS` | 422 | Maximum OTP attempts exceeded (3 attempts) |\n| `OTP_ALREADY_VALIDATED` | 422 | OTP has already been validated |\n| `OTP_BLOCKED` | 422 | Too many OTP attempts, signature is blocked |\n| `OTP_NO_CONTACT` | 422 | No email or phone to send OTP |\n| `OTP_EMAIL_FAILED` | 422 | Failed to send OTP via email |\n| `OTP_SMS_FAILED` | 422 | Failed to send OTP via SMS |\n\n**Example Error Response:**\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"OTP_EXPIRED\",\n    \"message\": \"OTP has expired. Please request a new one.\",\n    \"details\": []\n  }\n}\n```"
					},
					"response": []
				},
				{
					"name": "Biometrics Errors (BIOMETRICS_*)",
					"request": {
						"method": "POST",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{signature_id}}/sign",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{signature_id}}",
								"sign"
							]
						},
						"description": "## Biometrics Error Codes\n\nThese errors occur during biometric signature validation.\n\n| Code | HTTP Status | Description |\n|------|-------------|-------------|\n| `BIOMETRICS_INVALID` | 422 | Biometric data validation failed |\n| `BIOMETRICS_INVALID_FORMAT` | 422 | Invalid JSON format for biometric data |\n| `BIOMETRICS_MISSING_FIELDS` | 422 | Required biometric fields missing |\n| `BIOMETRICS_INVALID_STROKES` | 422 | Strokes array is invalid |\n| `BIOMETRICS_NO_STROKES` | 422 | No strokes provided |\n| `BIOMETRICS_STROKE_INVALID` | 422 | Stroke missing points array |\n| `BIOMETRICS_STROKE_TOO_SHORT` | 422 | Stroke has too few points |\n\n**Example Error Response:**\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"BIOMETRICS_STROKE_TOO_SHORT\",\n    \"message\": \"Stroke 0 has too few points\",\n    \"details\": []\n  }\n}\n```\n\n**Required Biometric Data Structure:**\n```json\n{\n  \"strokes\": [{\"points\": [{\"x\": 0, \"y\": 0, \"t\": 0}]}],\n  \"stroke_count\": 1,\n  \"total_time\": 1000,\n  \"average_speed\": 100,\n  \"average_pressure\": 0.5\n}\n```"
					},
					"response": []
				},
				{
					"name": "Share Errors (SHARE_*)",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/shares",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"shares"
							]
						},
						"description": "## Share Error Codes\n\nThese errors occur when working with document shares.\n\n| Code | HTTP Status | Description |\n|------|-------------|-------------|\n| `SHARE_NOT_FOUND` | 404 | Share link not found |\n| `SHARE_EXPIRED` | 410 | Share link has expired or been revoked |\n| `SHARE_CREATE_FAILED` | 422 | Failed to create share link |\n| `SMS_SEND_FAILED` | 422 | Failed to send driver SMS with share link |\n\n**Example Error Response:**\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"SHARE_EXPIRED\",\n    \"message\": \"Share link has expired or been revoked\",\n    \"details\": []\n  }\n}\n```"
					},
					"response": []
				},
				{
					"name": "Webhook Errors (WEBHOOK_*)",
					"request": {
						"method": "POST",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/tenant/test_webhook",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"tenant",
								"test_webhook"
							]
						},
						"description": "## Webhook Error Codes\n\nThese errors occur when configuring or testing webhooks.\n\n| Code | HTTP Status | Description |\n|------|-------------|-------------|\n| `WEBHOOK_NOT_CONFIGURED` | 400 | No webhook URL configured |\n| `WEBHOOK_UPDATE_FAILED` | 422 | Failed to update webhook configuration |\n| `WEBHOOK_UNREACHABLE` | 502 | Cannot reach webhook endpoint |\n| `WEBHOOK_INVALID_URL` | 400 | Invalid webhook URL format |\n| `WEBHOOK_SIGNATURE_ERROR` | 500 | Error generating webhook signature |\n| `WEBHOOK_SERIALIZATION_ERROR` | 500 | Error serializing webhook payload |\n\n**Example Error Response:**\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"WEBHOOK_UNREACHABLE\",\n    \"message\": \"Failed to reach webhook endpoint\",\n    \"details\": []\n  }\n}\n```"
					},
					"response": []
				},
				{
					"name": "Signup & Verification Errors",
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "POST",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/signup",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"signup"
							]
						},
						"description": "## Signup & Verification Error Codes\n\nThese errors occur during account registration and verification.\n\n| Code | HTTP Status | Description |\n|------|-------------|-------------|\n| `SIGNUP_FAILED` | 422 | Account registration failed |\n| `EMAIL_REQUIRED` | 400 | Email address is required |\n| `VERIFICATION_NOT_FOUND` | 404 | No pending verification for this email |\n| `VERIFICATION_TOKEN_REQUIRED` | 400 | Verification token is required |\n| `VERIFICATION_TOKEN_INVALID` | 404 | Verification token is invalid |\n| `VERIFICATION_TOKEN_EXPIRED` | 410 | Verification token has expired (24h) |\n| `VERIFICATION_FAILED` | 422 | Email verification failed |\n\n**Example Error Response:**\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"VERIFICATION_TOKEN_EXPIRED\",\n    \"message\": \"Verification token has expired\",\n    \"details\": []\n  }\n}\n```"
					},
					"response": []
				},
				{
					"name": "Reseller Errors (RESELLER_*, TENANT_*)",
					"request": {
						"auth": {
							"type": "apikey",
							"apikey": [
								{
									"key": "key",
									"value": "X-Master-API-Key",
									"type": "string"
								},
								{
									"key": "value",
									"value": "{{master_api_key}}",
									"type": "string"
								},
								{
									"key": "in",
									"value": "header",
									"type": "string"
								}
							]
						},
						"method": "POST",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/reseller/tenants",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"reseller",
								"tenants"
							]
						},
						"description": "## Reseller Error Codes\n\nThese errors occur when using the Reseller API.\n\n### Reseller Signup & Profile\n\n| Code | HTTP Status | Description |\n|------|-------------|-------------|\n| `RESELLER_SIGNUP_FAILED` | 422 | Reseller registration failed |\n| `RESELLER_UPDATE_FAILED` | 422 | Failed to update reseller profile |\n\n### Sub-Tenant Management\n\n| Code | HTTP Status | Description |\n|------|-------------|-------------|\n| `TENANT_CREATION_FAILED` | 422 | Failed to create sub-tenant |\n| `REGENERATE_KEY_FAILED` | 422 | Failed to regenerate sub-tenant API key |\n| `TENANT_ALREADY_SUSPENDED` | 422 | Tenant is already suspended |\n| `TENANT_ALREADY_ACTIVE` | 422 | Tenant is already active |\n\n**Example Error Response:**\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"TENANT_CREATION_FAILED\",\n    \"message\": \"A tenant with this VAT number already exists\",\n    \"details\": []\n  }\n}\n```"
					},
					"response": []
				},
				{
					"name": "Generic Errors",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents"
							]
						},
						"description": "## Generic Error Codes\n\nThese are general-purpose error codes used across the API.\n\n| Code | HTTP Status | Description |\n|------|-------------|-------------|\n| `VALIDATION_FAILED` | 422 | Data validation failed |\n| `RESOURCE_NOT_FOUND` | 404 | Requested resource not found |\n| `INVALID_ARGUMENT` | 400 | Invalid argument provided |\n| `INVALID_LOCALE` | 400 | Invalid locale code |\n| `INVALID_ROLE` | 400 | Invalid role specified (for PDF as-signed) |\n| `SEARCH_QUERY_REQUIRED` | 400 | Search query parameter required |\n| `INTERNAL_ERROR` | 500 | Internal server error |\n| `TENANT_UPDATE_FAILED` | 422 | Failed to update tenant profile |\n| `PDF_GENERATION_FAILED` | 422 | Failed to generate PDF |\n| `PDF_INVALID_DATA` | 422 | Invalid data for PDF generation |\n| `CERTIFICATE_ERROR` | 422 | Digital certificate error |\n\n**Example Error Response:**\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"VALIDATION_FAILED\",\n    \"message\": \"Validation failed\",\n    \"details\": [\"Carrier name is required\", \"Goods description is required\"]\n  }\n}\n```\n\n**Handling Error Codes in Your Application:**\n```javascript\nconst errorMessages = {\n  pt: {\n    OTP_EXPIRED: 'O código OTP expirou. Solicite um novo.',\n    OTP_INVALID: 'Código OTP inválido.',\n    SIGNATURE_ALREADY_COMPLETED: 'Esta assinatura já foi completada.',\n    // ... more translations\n  },\n  en: {\n    OTP_EXPIRED: 'OTP code has expired. Please request a new one.',\n    OTP_INVALID: 'Invalid OTP code.',\n    SIGNATURE_ALREADY_COMPLETED: 'This signature has already been completed.',\n    // ... more translations\n  }\n};\n\nfunction handleApiError(response) {\n  const code = response.error?.code;\n  const locale = getUserLocale();\n  return errorMessages[locale]?.[code] || response.error.message;\n}\n```"
					},
					"response": []
				}
			],
			"description": "Complete reference of all error codes returned by the API.\n\nAll error responses follow this structure:\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ERROR_CODE\",\n    \"message\": \"Human-readable message in English\",\n    \"details\": []\n  },\n  \"meta\": {\n    \"timestamp\": \"2025-01-15T12:00:00Z\"\n  }\n}\n```\n\n**Using Error Codes for i18n:**\nThe `code` field is machine-readable and stable. Use it to map to localized error messages in your application instead of displaying the English `message` to end users.\n\n**Error Categories:**\n- AUTH_* - Authentication errors (tenant and reseller)\n- DOCUMENT_* - Document errors\n- LIFECYCLE_* - State transition errors\n- SIGNATURE_* - Signature errors\n- OTP_* - OTP validation errors\n- BIOMETRICS_* - Biometric data errors\n- SHARE_* - Document sharing errors\n- WEBHOOK_* - Webhook configuration errors\n- VERIFICATION_* - Email verification errors\n- RESELLER_* - Reseller API errors\n- TENANT_* - Sub-tenant management errors\n- Generic codes - General errors"
		},
		{
			"name": "Lifecycle",
			"item": [
				{
					"name": "Get Lifecycle Status",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/lifecycle/status",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"lifecycle",
								"status"
							]
						},
						"description": "Get the current lifecycle status of the document including next required signature, completion status, and all timestamps."
					},
					"response": []
				},
				{
					"name": "Issue Document (Draft -> Issued)",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"location\": \"Lisboa, Portugal\"\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/lifecycle/issue",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"lifecycle",
								"issue"
							]
						},
						"description": "Issue a draft document. This transitions the document from 'draft' to 'issued' status.\n\n**Parameters:**\n- location: Where the document was issued"
					},
					"response": []
				},
				{
					"name": "Cancel Document",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"reason\": \"Encomenda cancelada pelo cliente. Order cancelled by customer due to production delay.\"\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/lifecycle/cancel",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"lifecycle",
								"cancel"
							]
						},
						"description": "Cancel the document. Only non-completed documents can be cancelled. Provide a reason for the cancellation."
					},
					"response": []
				}
			],
			"description": "Document lifecycle management.\n\n**Automatic Transitions (based on signatures):**\n- issued → accepted: When Carrier signs AND Consignor has signature request\n- accepted → delivered: When Consignee signature is requested AND Carrier has signed\n- delivered → completed: When ALL 3 signatures are completed\n\n**Manual Endpoints:**\n- POST /lifecycle/issue: Emit document (draft → issued)\n- POST /lifecycle/cancel: Cancel document\n- GET /lifecycle/status: Get current status"
		},
		{
			"name": "Shares",
			"item": [
				{
					"name": "List All Shares",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/shares",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"shares"
							]
						},
						"description": "List all shares for a document including active, expired, and revoked shares.\n\n**Response:**\n```json\n{\n  \"success\": true,\n  \"data\": [\n    {\n      \"token\": \"abc123...\",\n      \"type\": \"carrier\",\n      \"url\": \"https://app.40pes.pt/ecmr/view/abc123...\",\n      \"recipient_email\": null,\n      \"expires_at\": \"2026-02-27T15:30:00Z\",\n      \"access_count\": 5,\n      \"last_accessed_at\": \"2026-01-28T10:15:00Z\",\n      \"revoked\": false,\n      \"active\": true,\n      \"created_at\": \"2026-01-28T09:00:00Z\"\n    }\n  ]\n}\n```"
					},
					"response": []
				},
				{
					"name": "List Active Shares",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/shares/active",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"shares",
								"active"
							]
						},
						"description": "List only active shares (non-expired, non-revoked) for a document.\n\nUseful for checking which share links are currently valid."
					},
					"response": []
				},
				{
					"name": "Get Share Details",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/shares/{{share_token}}",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"shares",
								"{{share_token}}"
							]
						},
						"description": "Get detailed information about a specific share.\n\n**URL Parameters:**\n- `share_token`: The token returned when the share was created\n\n**Response includes:**\n- `url`: The public URL to access the document\n- `access_count`: Number of times the link was accessed\n- `last_accessed_at`: When the link was last used\n- `active`: Whether the share is currently valid"
					},
					"response": []
				},
				{
					"name": "Create Share (Carrier Access)",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"share\": {\n    \"permissions\": \"carrier\"\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/shares",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"shares"
							]
						},
						"description": "## Create Share with Carrier Permissions\n\nCreates a share URL that allows the user to act as the **Carrier**.\n\n**Use Case:** TMS needs to provide a URL to an operator who can assign/replace the driver in the field.\n\n**Carrier Capabilities:**\n- View document details\n- Assign or replace driver\n- Request signatures from other parties\n- Add transport observations\n\n---\n\n**Request Body:**\n```json\n{\n  \"share\": {\n    \"permissions\": \"carrier\"\n  }\n}\n```\n\n**Response:**\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"token\": \"Kj3mN9pQ...\",\n    \"type\": \"carrier\",\n    \"url\": \"https://app.40pes.pt/ecmr/view/Kj3mN9pQ...\",\n    \"expires_at\": \"2026-02-27T15:30:00Z\",\n    \"active\": true\n  }\n}\n```\n\nThe `url` field contains the link to share with the operator."
					},
					"response": []
				},
				{
					"name": "Create Share (View Only)",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"share\": {\n    \"permissions\": \"view\"\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/shares",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"shares"
							]
						},
						"description": "## Create Share with View-Only Permissions\n\nCreates a share URL that allows **read-only** access to the document.\n\n**Use Case:** Share document status with clients, partners, or stakeholders who only need to track the shipment.\n\n**View-Only Capabilities:**\n- View document details and status\n- See signature progress\n- Download PDF (when completed)\n\n**Cannot:**\n- Edit any information\n- Request signatures\n- Assign drivers\n\n---\n\n**Request Body:**\n```json\n{\n  \"share\": {\n    \"permissions\": \"view\"\n  }\n}\n```\n\n**Response:**\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"token\": \"Xy9zAbc...\",\n    \"type\": \"view\",\n    \"url\": \"https://app.40pes.pt/ecmr/view/Xy9zAbc...\",\n    \"expires_at\": \"2026-02-27T15:30:00Z\",\n    \"active\": true\n  }\n}\n```"
					},
					"response": []
				},
				{
					"name": "Create Share (With Email Notification)",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"share\": {\n    \"permissions\": \"view\",\n    \"recipient_email\": \"cliente@empresa.com\",\n    \"expires_at\": \"2026-03-01T23:59:59Z\"\n  },\n  \"send_notification\": true\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/shares",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"shares"
							]
						},
						"description": "## Create Share with Email Notification\n\nCreates a share and automatically sends an email to the recipient with the access link.\n\n**Use Case:** Automatically notify a client or partner about document access.\n\n---\n\n**Request Body:**\n```json\n{\n  \"share\": {\n    \"permissions\": \"view\",\n    \"recipient_email\": \"cliente@empresa.com\",\n    \"expires_at\": \"2026-03-01T23:59:59Z\"\n  },\n  \"send_notification\": true\n}\n```\n\n**Parameters:**\n\n| Field | Required | Description |\n|-------|----------|-------------|\n| `permissions` | No | Access level (default: `view`) |\n| `recipient_email` | No | Email for tracking purposes |\n| `expires_at` | No | Expiration date (default: 30 days) |\n| `send_notification` | No | Send email automatically (default: false) |\n\n**Available Permissions:**\n\n| Value | Can View | Can Edit | Can Assign Driver |\n|-------|----------|----------|-------------------|\n| `view` | Yes | No | No |\n| `carrier` | Yes | Yes* | Yes |\n| `consignor` | Yes | Yes* | No |\n| `consignee` | Yes | Yes* | No |\n| `driver` | Yes | No | No |\n\n*Edit depends on document state"
					},
					"response": []
				},
				{
					"name": "Generate QR Code",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"expires_in\": 48\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/shares/generate_qr",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"shares",
								"generate_qr"
							]
						},
						"description": "## Generate QR Code for Document\n\nGenerates a QR code that can be scanned to access the document. Useful for printing on physical documents or displaying on screens.\n\n**Use Case:** Print QR code on CMR paper copy, display on warehouse screens, or embed in mobile apps.\n\n---\n\n**Request Body:**\n```json\n{\n  \"expires_in\": 48\n}\n```\n\n**Parameters:**\n\n| Field | Required | Description |\n|-------|----------|-------------|\n| `expires_in` | No | Hours until expiration (default: 24) |\n\n---\n\n**Response:**\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"share\": {\n      \"token\": \"Qr1Code...\",\n      \"type\": \"viewer\",\n      \"url\": \"https://app.40pes.pt/ecmr/view/Qr1Code...\",\n      \"expires_at\": \"2026-01-30T15:30:00Z\"\n    },\n    \"qr_code_url\": \"https://app.40pes.pt/ecmr/view/Qr1Code...\",\n    \"qr_code_svg\": \"<svg xmlns=...\",\n    \"qr_code_base64\": \"data:image/png;base64,iVBORw0...\"\n  }\n}\n```\n\n**Response Fields:**\n- `qr_code_url`: URL encoded in the QR code\n- `qr_code_svg`: SVG string for embedding in HTML\n- `qr_code_base64`: PNG image as data URL for direct use in `<img>` tags"
					},
					"response": []
				},
				{
					"name": "Revoke Share",
					"request": {
						"method": "POST",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/shares/{{share_token}}/revoke",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"shares",
								"{{share_token}}",
								"revoke"
							]
						},
						"description": "## Revoke a Share\n\nInvalidates an active share. After revocation, the share link will no longer work.\n\n**Use Cases:**\n- Security breach: revoke access immediately\n- Employee left: revoke their access links\n- Wrong recipient: revoke and create new share\n\n---\n\n**URL Parameters:**\n- `share_token`: Token of the share to revoke\n\n**Response:**\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"message\": \"Share revoked successfully\",\n    \"share_token\": \"abc123...\",\n    \"revoked_at\": \"2026-01-28T16:00:00Z\"\n  }\n}\n```\n\n**Note:** Revocation is permanent. To restore access, create a new share."
					},
					"response": []
				}
			],
			"description": "## Document Sharing API\n\nManage access to eCMR documents through shareable URLs.\n\n### Key Concepts\n\n**Shares** are access tokens that create URLs for viewing or interacting with documents. Each share has:\n- **Permissions**: What the user can do (view, carrier, consignor, etc.)\n- **Expiration**: When the link stops working\n- **Token**: Unique identifier in the URL\n\n### Permission Levels\n\n| Permission | Description | Typical Use |\n|------------|-------------|-------------|\n| `view` | Read-only access | Clients tracking shipment |\n| `carrier` | Full carrier access | Operators managing transport |\n| `consignor` | Consignor access | Sender tracking |\n| `consignee` | Consignee access | Receiver tracking |\n| `driver` | Driver view | Driver reference |\n\n### Common TMS Integration Patterns\n\n1. **Backoffice Operator Access**: Create share with `permissions: carrier` to allow driver assignment\n2. **Client Status Tracking**: Create share with `permissions: view` for read-only access\n3. **QR Code for Documents**: Use `generate_qr` endpoint for printable codes"
		},
		{
			"name": "Signatures",
			"item": [
				{
					"name": "List Signatures",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures"
							]
						},
						"description": "List all signatures for a document including their status, signatory information, and signing timestamps."
					},
					"response": []
				},
				{
					"name": "Get Signature Details",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{signature_id}}",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{signature_id}}"
							]
						},
						"description": "Get detailed information about a specific signature."
					},
					"response": []
				},
				{
					"name": "Request Signature",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"signatory_role\": \"consignee\",\n  \"signatory_name\": \"Hans Muller\",\n  \"contact\": \"hans.muller@example.de\",\n  \"signature_type\": \"in_person\",\n  \"preferred_locale\": \"de\"\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures"
							]
						},
						"description": "Create a signature request for a party. This will generate a share link and optionally send an OTP.\n\n**Parameters:**\n- signatory_role: consignor, carrier, or consignee (required)\n- signatory_name: Name of the person signing (required, except for without_signature)\n- contact: Email or phone for OTP (required for remote)\n- signature_type: in_person, remote, certificate, platform, or without_signature\n- geolocation: Required for without_signature type (latitude, longitude)\n- preferred_locale: Language for emails (pt, en, es, de, fr, it)\n\n**Signature Types:**\n- in_person: Physical signature with OTP validation\n- remote: OTP-based signature without physical presence\n- certificate: Digital certificate signature (eIDAS)\n- platform: System-generated signature\n- without_signature: For cases where counterparty cannot sign (absent/refused). Only for consignor/consignee. Requires geolocation. signatory_name is optional (person may be absent or refuse to identify). Document cannot be completed with this type.\n\n**Response includes:**\n- Signature ID\n- Share token for signing link\n- Share expiration time"
					},
					"response": []
				},
				{
					"name": "Validate OTP",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"otp_code\": \"123456\",\n  \"geolocation\": {\n    \"latitude\": 52.520008,\n    \"longitude\": 13.404954,\n    \"accuracy\": 10\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{signature_id}}/validate_otp",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{signature_id}}",
								"validate_otp"
							]
						},
						"description": "Validate the OTP code sent to the signatory. This is required before signing (unless using device_shared mode).\n\n**Parameters:**\n- otp_code: 6-digit code sent to email/phone (required)\n- geolocation: Location data (optional but recommended)"
					},
					"response": []
				},
				{
					"name": "Sign Document (In Person)",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"signature_image\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==\",\n  \"biometric_data\": {\n    \"strokes\": [\n      {\n        \"index\": 0,\n        \"points\": [\n          {\"x\": 10.5, \"y\": 20.3, \"time\": 0, \"pressure\": 0.5},\n          {\"x\": 12.8, \"y\": 22.1, \"time\": 10, \"pressure\": 0.6},\n          {\"x\": 15.2, \"y\": 24.5, \"time\": 20, \"pressure\": 0.65},\n          {\"x\": 18.1, \"y\": 26.2, \"time\": 30, \"pressure\": 0.7},\n          {\"x\": 21.3, \"y\": 27.8, \"time\": 40, \"pressure\": 0.68}\n        ],\n        \"duration\": 500,\n        \"point_count\": 45\n      },\n      {\n        \"index\": 1,\n        \"points\": [\n          {\"x\": 30.0, \"y\": 15.0, \"time\": 600, \"pressure\": 0.55},\n          {\"x\": 32.5, \"y\": 18.2, \"time\": 610, \"pressure\": 0.6},\n          {\"x\": 35.1, \"y\": 21.4, \"time\": 620, \"pressure\": 0.62}\n        ],\n        \"duration\": 400,\n        \"point_count\": 38\n      }\n    ],\n    \"stroke_count\": 2,\n    \"total_points\": 83,\n    \"total_time\": 1020,\n    \"average_speed\": 0.125,\n    \"average_pressure\": 0.62\n  },\n  \"geolocation\": {\n    \"latitude\": 52.520008,\n    \"longitude\": 13.404954,\n    \"accuracy\": 10\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{signature_id}}/sign",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{signature_id}}",
								"sign"
							]
						},
						"description": "Complete the signature with biometric data. This creates an eIDAS-compliant Advanced Electronic Signature (AdES).\n\n**Required for in_person signatures:**\n- signature_image: Base64-encoded PNG/JPEG of the signature\n- biometric_data: Stroke data with pressure, speed, timing\n\n**Optional:**\n- geolocation: Location data\n- signer_info: Additional signer information\n\n**Prerequisites:** OTP must be validated first."
					},
					"response": []
				},
				{
					"name": "Sign Document (Certificate)",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"signed_data_hash\": \"a1b2c3d4e5f6...\",\n  \"digital_signature\": \"MEUCIQD...\",\n  \"certificate_fingerprint\": \"SHA256:AB:CD:EF:...\"\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{signature_id}}/sign",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{signature_id}}",
								"sign"
							]
						},
						"description": "Complete signature using a digital certificate (Qualified Electronic Signature).\n\n**Required for certificate signatures:**\n- signed_data_hash: Hash of the document data\n- digital_signature: Cryptographic signature\n- certificate_fingerprint: Certificate identifier"
					},
					"response": []
				},
				{
					"name": "Cancel Signature",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"reason\": \"Dados incorrectos. Nova assinatura sera solicitada. Incorrect data - new signature will be requested.\"\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{signature_id}}/cancel",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{signature_id}}",
								"cancel"
							]
						},
						"description": "Cancel a pending signature request."
					},
					"response": []
				},
				{
					"name": "Resend OTP",
					"request": {
						"method": "POST",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/documents/{{cmr_number}}/signatures/{{signature_id}}/resend_otp",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"documents",
								"{{cmr_number}}",
								"signatures",
								"{{signature_id}}",
								"resend_otp"
							]
						},
						"description": "Resend the OTP code to the signatory's email/phone. Use this when the previous OTP expired or wasn't received."
					},
					"response": []
				},
				{
					"name": "Get Signature by Share Token",
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/signatures/by_share/{{share_token}}",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"signatures",
								"by_share",
								"{{share_token}}"
							]
						},
						"description": "View signature details using a public share token. This endpoint does NOT require API authentication - it uses the share token for access.\n\n**Use cases:**\n- Third party signing via link\n- Viewing document summary before signing"
					},
					"response": []
				}
			],
			"description": "Electronic signature management for eCMR documents"
		},
		{
			"name": "Tenant",
			"item": [
				{
					"name": "Get Profile",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/tenant/profile",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"tenant",
								"profile"
							]
						},
						"description": "Get your tenant profile including configuration, rate limits, and settings."
					},
					"response": []
				},
				{
					"name": "Update Profile",
					"request": {
						"method": "PUT",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"name\": \"My Company Updated\",\n  \"company_type\": \"carrier\",\n  \"address\": \"Rua do Transporte, 456, 4100-000 Porto, Portugal\",\n  \"country_code\": \"PT\",\n  \"metadata\": {\n    \"department\": \"Logistics\",\n    \"integration_version\": \"2.0\"\n  }\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/tenant/profile",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"tenant",
								"profile"
							]
						},
						"description": "Update your tenant profile information.\n\n**Updatable fields:**\n- name: Your company name\n- company_type: Your role in eCMRs - 'consignor', 'consignee', or 'carrier'. When set, your company data will be pre-filled in eCMRs for that role.\n- address: Your company full address (street, postal code, city, country)\n- country_code: Two-letter ISO country code (e.g., 'PT', 'DE', 'ES')\n- metadata: Custom metadata object for your integration"
					},
					"response": []
				},
				{
					"name": "Get Usage Statistics",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/tenant/usage",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"tenant",
								"usage"
							]
						},
						"description": "Get your tenant's usage statistics including document counts, signature statistics, and API rate limits."
					},
					"response": []
				},
				{
					"name": "Get Webhook Configuration",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/tenant/webhook",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"tenant",
								"webhook"
							]
						},
						"description": "Get your current webhook configuration including URL and subscribed events."
					},
					"response": []
				},
				{
					"name": "Update Webhook",
					"request": {
						"method": "PUT",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"url\": \"https://yourserver.com/webhooks/ecmr\",\n  \"events\": [\n    \"issued\",\n    \"accepted\",\n    \"delivered\",\n    \"completed\",\n    \"cancelled\"\n  ]\n}",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/tenant/webhook",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"tenant",
								"webhook"
							]
						},
						"description": "Configure your webhook endpoint. The system will send POST requests to your URL when subscribed events occur. Returns the webhook secret for signature verification."
					},
					"response": []
				},
				{
					"name": "Test Webhook",
					"request": {
						"method": "POST",
						"header": [],
						"url": {
							"raw": "{{base_url}}/api/v1/ecmr/tenant/test_webhook",
							"host": [
								"{{base_url}}"
							],
							"path": [
								"api",
								"v1",
								"ecmr",
								"tenant",
								"test_webhook"
							]
						},
						"description": "Send a test webhook to your configured endpoint. Use this to verify your webhook receiver is working correctly."
					},
					"response": []
				}
			],
			"description": "Tenant profile, usage statistics, and webhook configuration"
		}
	],
	"auth": {
		"type": "apikey",
		"apikey": [
			{
				"key": "key",
				"value": "X-API-Key",
				"type": "string"
			},
			{
				"key": "value",
				"value": "{{api_key}}",
				"type": "string"
			},
			{
				"key": "in",
				"value": "header",
				"type": "string"
			}
		]
	},
	"event": [
		{
			"listen": "prerequest",
			"script": {
				"type": "text/javascript",
				"packages": {},
				"requests": {},
				"exec": [
					""
				]
			}
		},
		{
			"listen": "test",
			"script": {
				"type": "text/javascript",
				"packages": {},
				"requests": {},
				"exec": [
					""
				]
			}
		}
	],
	"variable": [
		{
			"key": "base_url",
			"value": "https://ecmr.40pes.pt"
		},
		{
			"key": "api_key",
			"value": ""
		},
		{
			"key": "tenant_id",
			"value": ""
		},
		{
			"key": "verification_token",
			"value": ""
		},
		{
			"key": "cmr_number",
			"value": ""
		},
		{
			"key": "signature_id",
			"value": ""
		},
		{
			"key": "consignor_signature_id",
			"value": ""
		},
		{
			"key": "carrier_signature_id",
			"value": ""
		},
		{
			"key": "consignee_signature_id",
			"value": ""
		},
		{
			"key": "share_token",
			"value": ""
		},
		{
			"key": "consignor_share_token",
			"value": ""
		},
		{
			"key": "carrier_share_token",
			"value": ""
		},
		{
			"key": "consignee_share_token",
			"value": ""
		},
		{
			"key": "signup_email",
			"value": ""
		},
		{
			"key": "master_api_key",
			"value": "",
			"description": "Reseller master API key (rk_live_xxx or rk_test_xxx)"
		},
		{
			"key": "reseller_id",
			"value": "",
			"description": "Reseller public ID (rsl_xxx)"
		},
		{
			"key": "reseller_verification_token",
			"value": "",
			"description": "Token from reseller verification email"
		},
		{
			"key": "reseller_signup_email",
			"value": ""
		},
		{
			"key": "created_tenant_id",
			"value": "",
			"description": "ID of the last sub-tenant created by reseller"
		},
		{
			"key": "created_tenant_api_key",
			"value": "",
			"description": "API key of the last sub-tenant created by reseller"
		},
		{
			"key": "deca_number",
			"value": "",
			"type": "string"
		},
		{
			"key": "deca_download_url",
			"value": "",
			"type": "string"
		}
	]
}
