{
  "info": {
    "name": "LabNote API v1",
    "description": "REST API for LabNote ELN/QMS. Authenticate with a per-org API key (Bearer). Docs: https://labnote-light.com/api-docs",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    { "key": "base_url", "value": "https://vilasdqkwlszlulteqrb.supabase.co/functions/v1/api-v1" },
    { "key": "api_key", "value": "lk_live_REPLACE_ME" },
    { "key": "inbound_url", "value": "https://vilasdqkwlszlulteqrb.supabase.co/functions/v1/inbound-webhook/your-endpoint-slug" },
    { "key": "inbound_secret", "value": "whsec_REPLACE_ME" }
  ],
  "auth": {
    "type": "bearer",
    "bearer": [{ "key": "token", "value": "{{api_key}}", "type": "string" }]
  },
  "item": [
    {
      "name": "Service",
      "item": [
        {
          "name": "Health (no auth)",
          "request": { "auth": { "type": "noauth" }, "method": "GET", "url": "{{base_url}}/v1/health" }
        },
        {
          "name": "Ping (identity + resource registry)",
          "request": { "method": "GET", "url": "{{base_url}}/v1/ping" }
        }
      ]
    },
    {
      "name": "Experiments",
      "item": [
        { "name": "List experiments", "request": { "method": "GET", "url": "{{base_url}}/v1/experiments?limit=20" } },
        { "name": "Get experiment", "request": { "method": "GET", "url": "{{base_url}}/v1/experiments/:id" } },
        {
          "name": "Create experiment",
          "request": {
            "method": "POST",
            "url": "{{base_url}}/v1/experiments",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "{{$guid}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"title\": \"Run 42\",\n  \"description\": \"Created via API\",\n  \"status\": \"draft\"\n}" }
          }
        },
        {
          "name": "Update experiment",
          "request": {
            "method": "PATCH",
            "url": "{{base_url}}/v1/experiments/:id",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"status\": \"in_review\"\n}" }
          }
        },
        { "name": "Delete experiment", "request": { "method": "DELETE", "url": "{{base_url}}/v1/experiments/:id" } }
      ]
    },
    {
      "name": "Projects",
      "item": [
        { "name": "List projects", "request": { "method": "GET", "url": "{{base_url}}/v1/projects?limit=20" } },
        { "name": "Get project", "request": { "method": "GET", "url": "{{base_url}}/v1/projects/:id" } },
        {
          "name": "Create project",
          "request": {
            "method": "POST",
            "url": "{{base_url}}/v1/projects",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "{{$guid}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"name\": \"Project Alpha\",\n  \"status\": \"active\"\n}" }
          }
        }
      ]
    },
    {
      "name": "Samples",
      "item": [
        { "name": "List samples", "request": { "method": "GET", "url": "{{base_url}}/v1/samples?limit=20" } },
        { "name": "Get sample", "request": { "method": "GET", "url": "{{base_url}}/v1/samples/:id" } },
        {
          "name": "Create sample",
          "request": {
            "method": "POST",
            "url": "{{base_url}}/v1/samples",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "{{$guid}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"name\": \"S-test-001\",\n  \"type\": \"liquid\",\n  \"status\": \"active\"\n}" }
          }
        }
      ]
    },
    {
      "name": "Equipment",
      "item": [
        { "name": "List equipment", "request": { "method": "GET", "url": "{{base_url}}/v1/equipment?limit=20" } },
        { "name": "Get equipment", "request": { "method": "GET", "url": "{{base_url}}/v1/equipment/:id" } },
        { "name": "List usage sessions", "request": { "method": "GET", "url": "{{base_url}}/v1/equipment/:id/usage?limit=100" } },
        { "name": "List maintenance logs", "request": { "method": "GET", "url": "{{base_url}}/v1/equipment/:id/maintenance?limit=100" } },
        { "name": "List calibrations", "request": { "method": "GET", "url": "{{base_url}}/v1/equipment/:id/calibrations?limit=100" } },
        { "name": "List qualifications", "request": { "method": "GET", "url": "{{base_url}}/v1/equipment/:id/qualifications?limit=100" } },
        {
          "name": "Create calibration",
          "request": {
            "method": "POST",
            "url": "{{base_url}}/v1/equipment/:id/calibrations",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "{{$guid}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"calibration_date\": \"2026-08-01\",\n  \"due_date\": \"2027-08-01\",\n  \"result\": \"pass\"\n}" }
          }
        },
        {
          "name": "Update sub-resource record",
          "request": {
            "method": "PATCH",
            "url": "{{base_url}}/v1/equipment/:id/calibrations/:subId",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"notes\": \"Certificate archived\"\n}" }
          }
        },
        { "name": "Get remote access", "request": { "method": "GET", "url": "{{base_url}}/v1/equipment/:id/remote" } },
        {
          "name": "Upsert remote access",
          "request": {
            "method": "POST",
            "url": "{{base_url}}/v1/equipment/:id/remote",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"url\": \"https://device.local/ui\",\n  \"label\": \"Web UI\",\n  \"protocol\": \"https\",\n  \"status\": \"online\",\n  \"requires_vpn\": false\n}" }
          }
        },
        {
          "name": "Patch remote access",
          "request": {
            "method": "PATCH",
            "url": "{{base_url}}/v1/equipment/:id/remote",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"status\": \"maintenance\"\n}" }
          }
        },
        { "name": "Delete remote access", "request": { "method": "DELETE", "url": "{{base_url}}/v1/equipment/:id/remote" } }
      ]
    },
    {
      "name": "Reagents & stock",
      "item": [
        { "name": "List reagent catalog", "request": { "method": "GET", "url": "{{base_url}}/v1/reagents?limit=20" } },
        { "name": "Get reagent", "request": { "method": "GET", "url": "{{base_url}}/v1/reagents/:id" } },
        { "name": "List stock items", "request": { "method": "GET", "url": "{{base_url}}/v1/stock-items?limit=20" } },
        { "name": "Get stock item", "request": { "method": "GET", "url": "{{base_url}}/v1/stock-items/:id" } },
        {
          "name": "Create stock movement",
          "request": {
            "method": "POST",
            "url": "{{base_url}}/v1/stock-movements",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "{{$guid}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"stock_item_id\": \"...\",\n  \"movement_type\": \"consumption\",\n  \"quantity_delta\": -5,\n  \"reason\": \"API withdrawal\"\n}" }
          }
        },
        { "name": "List inventories", "request": { "method": "GET", "url": "{{base_url}}/v1/inventories?limit=20" } }
      ]
    },
    {
      "name": "Instruments",
      "item": [
        { "name": "List devices", "request": { "method": "GET", "url": "{{base_url}}/v1/devices" } },
        {
          "name": "Register device (upsert)",
          "request": {
            "method": "POST",
            "url": "{{base_url}}/v1/devices",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"external_device_id\": \"UVVIS-01\",\n  \"vendor\": \"synefex\",\n  \"model\": \"UV-1900\",\n  \"device_type\": \"uvvis\"\n}" }
          }
        },
        { "name": "Delete device", "request": { "method": "DELETE", "url": "{{base_url}}/v1/devices/:id" } },
        { "name": "List results", "request": { "method": "GET", "url": "{{base_url}}/v1/results?limit=50" } },
        { "name": "Get result", "request": { "method": "GET", "url": "{{base_url}}/v1/results/:id" } },
        { "name": "Get result chart data", "request": { "method": "GET", "url": "{{base_url}}/v1/results/:id/chart" } },
        {
          "name": "Ingest result (JSON)",
          "request": {
            "method": "POST",
            "url": "{{base_url}}/v1/results",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-Instrument-Vendor", "value": "generic" },
              { "key": "Idempotency-Key", "value": "{{$guid}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"device_id\": \"UVVIS-01\",\n  \"sample_id\": \"...\",\n  \"method\": \"A280\",\n  \"unit_x\": \"nm\",\n  \"unit_y\": \"AU\",\n  \"data_points\": [{ \"x\": 260, \"y\": 0.42 }, { \"x\": 280, \"y\": 0.81 }]\n}" }
          }
        },
        {
          "name": "Upload raw file (CSV / JCAMP-DX / mzML)",
          "request": {
            "method": "POST",
            "url": "{{base_url}}/v1/results/upload",
            "body": {
              "mode": "formdata",
              "formdata": [
                { "key": "file", "type": "file", "src": [] },
                { "key": "device_id", "value": "UVVIS-01", "type": "text" },
                { "key": "sample_id", "value": "", "type": "text" },
                { "key": "method", "value": "A280", "type": "text" }
              ]
            }
          }
        }
      ]
    },
    {
      "name": "Notifications",
      "item": [
        { "name": "List own notifications", "request": { "method": "GET", "url": "{{base_url}}/v1/notifications?limit=50" } },
        {
          "name": "Push notification",
          "request": {
            "method": "POST",
            "url": "{{base_url}}/v1/notifications",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "{{$guid}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"title\": \"Instrument finished\",\n  \"body\": \"Run 42 completed\",\n  \"role\": \"researcher\"\n}" }
          }
        }
      ]
    },
    {
      "name": "Audit & jobs",
      "item": [
        { "name": "Export audit trail", "request": { "method": "GET", "url": "{{base_url}}/v1/audit?from=2026-08-01T00:00:00Z&to=2026-08-31T23:59:59Z&limit=200" } },
        { "name": "List jobs", "request": { "method": "GET", "url": "{{base_url}}/v1/jobs?limit=20" } },
        {
          "name": "Enqueue job",
          "request": {
            "method": "POST",
            "url": "{{base_url}}/v1/jobs",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "{{$guid}}" }
            ],
            "body": { "mode": "raw", "raw": "{\n  \"job_type\": \"report_export\",\n  \"payload\": { \"report_id\": \"...\" }\n}" }
          }
        },
        { "name": "Get job", "request": { "method": "GET", "url": "{{base_url}}/v1/jobs/:id" } }
      ]
    },
    {
      "name": "Inbound webhook (HMAC required — see pre-request script)",
      "request": {
        "auth": { "type": "noauth" },
        "method": "POST",
        "url": "{{inbound_url}}",
        "header": [
          { "key": "Content-Type", "value": "application/json" },
          { "key": "X-Labnote-Timestamp", "value": "{{ts}}" },
          { "key": "X-Labnote-Signature", "value": "t={{ts}},v1={{sig}}" },
          { "key": "Idempotency-Key", "value": "{{$guid}}" }
        ],
        "body": {
          "mode": "raw",
          "raw": "{\n  \"event_type\": \"instrument.measurement_recorded\",\n  \"occurred_at\": \"2026-06-19T20:15:00Z\",\n  \"external_id\": \"HPLC-A-001#run-1\",\n  \"payload\": { \"instrument_serial\": \"HPLC-A-001\", \"results\": [] }\n}"
        }
      },
      "event": [
        {
          "listen": "prerequest",
          "script": {
            "type": "text/javascript",
            "exec": [
              "const ts = Math.floor(Date.now() / 1000).toString();",
              "const body = pm.request.body.raw;",
              "const secret = pm.collectionVariables.get('inbound_secret');",
              "const sig = CryptoJS.HmacSHA256(ts + '.' + body, secret).toString(CryptoJS.enc.Hex);",
              "pm.variables.set('ts', ts);",
              "pm.variables.set('sig', sig);"
            ]
          }
        }
      ]
    }
  ]
}
