{
  "info": {
    "name": "RoofAngle Integration API v4",
    "_postman_id": "ra-v4-collection",
    "description": "Postman collection for the v4 RoofAngle Integration API (platform integrations).\n\n## Quick start\n\n1. Import this collection AND `RoofAngle.postman_environment.json` from the same folder.\n2. Open the environment, fill in `base_url`, `client_id`, `client_secret`.\n3. Run `Auth > POST Get Access Token` once. It writes `access_token` to the environment automatically. Every other request reuses it.\n\nSandbox base URL: `https://sandboxclient-api.roofangle.com`\n\nAll requests scope automatically to your integration client (your token's `integrationClientId` claim). You will never see another tenant's data.\n\n### v4 vs v1\n- v4 is designed for platform-style integrations. The order create endpoint does NOT go through Stripe Checkout. It expects payment to be handled by the calling platform.\n- File response shape, status strings, and webhooks are identical to v1.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    { "key": "api_version", "value": "v4" }
  ],
  "auth": {
    "type": "bearer",
    "bearer": [
      { "key": "token", "value": "{{access_token}}", "type": "string" }
    ]
  },
  "item": [
    {
      "name": "Auth",
      "item": [
        {
          "name": "POST Get Access Token",
          "request": {
            "auth": { "type": "noauth" },
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"clientId\": \"{{client_id}}\",\n  \"clientSecret\": \"{{client_secret}}\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/v4/auth/token",
              "host": ["{{base_url}}"],
              "path": ["v4", "auth", "token"]
            },
            "description": "Exchange clientId + clientSecret for a JWT access token. Token expires in 1 hour."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200 OK', function () { pm.response.to.have.status(200); });",
                  "var json = pm.response.json();",
                  "if (json && json.accessToken) {",
                  "    pm.environment.set('access_token', json.accessToken);",
                  "    console.log('access_token saved to environment');",
                  "}"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Services",
      "item": [
        {
          "name": "GET List Services (custom pricing for your tenant)",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/v4/services",
              "host": ["{{base_url}}"],
              "path": ["v4", "services"]
            },
            "description": "Returns the services your business group can order, including per-tenant pricing and SLA (deliveryTimeHours)."
          }
        },
        {
          "name": "POST Calculate Price",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"serviceId\": \"REPLACE_WITH_SERVICE_ID\",\n  \"propertyType\": \"Residential\",\n  \"rushOrder\": false,\n  \"pdfRequired\": true\n}"
            },
            "url": {
              "raw": "{{base_url}}/v4/services/calculate-price",
              "host": ["{{base_url}}"],
              "path": ["v4", "services", "calculate-price"]
            }
          }
        }
      ]
    },
    {
      "name": "Orders",
      "item": [
        {
          "name": "GET List Orders",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/v4/orders?page=1&pageSize=50",
              "host": ["{{base_url}}"],
              "path": ["v4", "orders"],
              "query": [
                { "key": "page", "value": "1" },
                { "key": "pageSize", "value": "50" },
                { "key": "status", "value": "InProgress", "disabled": true },
                { "key": "from", "value": "2026-01-01", "disabled": true },
                { "key": "to", "value": "2026-12-31", "disabled": true }
              ]
            }
          }
        },
        {
          "name": "GET Order by ID",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/v4/orders/REPLACE_WITH_ORDER_ID",
              "host": ["{{base_url}}"],
              "path": ["v4", "orders", "REPLACE_WITH_ORDER_ID"]
            },
            "description": "Returns the order including status and deliveredFiles[] when the order is Completed."
          }
        },
        {
          "name": "GET Orders by Customer Email",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/v4/orders/by-customer?email=customer@example.com",
              "host": ["{{base_url}}"],
              "path": ["v4", "orders", "by-customer"],
              "query": [
                { "key": "email", "value": "customer@example.com" }
              ]
            }
          }
        },
        {
          "name": "POST Create Order",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"customerName\": \"John Smith\",\n  \"customerEmail\": \"john.smith@example.com\",\n  \"address\": \"123 Main Street, Austin, TX 78701\",\n  \"latitude\": 30.2672,\n  \"longitude\": -97.7431,\n  \"propertyType\": \"Residential\",\n  \"serviceId\": \"REPLACE_WITH_SERVICE_ID\",\n  \"rushOrder\": false,\n  \"pdfRequired\": true,\n  \"additionalInstructions\": \"Please include detached garage.\",\n  \"insuredName\": \"Jane Doe\",\n  \"claimNumber\": \"CLM-2026-001234\",\n  \"notes\": \"Internal reference: contract #4421\",\n  \"referenceImageUrl\": \"https://example.com/property-photos/123-main.jpg\"\n}"
            },
            "url": {
              "raw": "{{base_url}}/v4/orders",
              "host": ["{{base_url}}"],
              "path": ["v4", "orders"]
            }
          }
        },
        {
          "name": "POST Create Bulk Orders (up to 50)",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"customerName\": \"Example Roofing\",\n  \"customerEmail\": \"ops@example.com\",\n  \"orders\": [\n    {\n      \"address\": \"123 Main Street, Austin, TX 78701\",\n      \"latitude\": 30.2672,\n      \"longitude\": -97.7431,\n      \"propertyType\": \"Residential\",\n      \"serviceId\": \"REPLACE_WITH_SERVICE_ID\",\n      \"rushOrder\": false,\n      \"pdfRequired\": true,\n      \"additionalInstructions\": \"Please include detached garage.\",\n      \"insuredName\": \"Jane Doe\",\n      \"claimNumber\": \"CLM-2026-001234\",\n      \"notes\": \"Internal reference: contract #4421\",\n      \"referenceImageUrl\": \"https://example.com/property-photos/123-main.jpg\"\n    },\n    {\n      \"address\": \"456 Oak Avenue, Austin, TX 78702\",\n      \"latitude\": 30.2729,\n      \"longitude\": -97.7558,\n      \"propertyType\": \"Commercial\",\n      \"serviceId\": \"REPLACE_WITH_SERVICE_ID\",\n      \"rushOrder\": true,\n      \"pdfRequired\": true,\n      \"additionalInstructions\": \"Two-building property, measure both roofs.\",\n      \"claimNumber\": \"CLM-2026-001235\",\n      \"referenceImageUrl\": \"https://example.com/property-photos/456-oak.jpg\"\n    }\n  ]\n}"
            },
            "url": {
              "raw": "{{base_url}}/v4/orders/bulk",
              "host": ["{{base_url}}"],
              "path": ["v4", "orders", "bulk"]
            }
          }
        }
      ]
    },
    {
      "name": "Files",
      "item": [
        {
          "name": "GET Order Files (list)",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/v4/orders/REPLACE_WITH_ORDER_ID/files",
              "host": ["{{base_url}}"],
              "path": ["v4", "orders", "REPLACE_WITH_ORDER_ID", "files"]
            },
            "description": "Returns `files[]` with id, fileName, fileSize, fileType, uploadedAt, url. The `url` is a pre-signed S3 link valid for ~15 minutes."
          }
        },
        {
          "name": "GET Download Single File",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/v4/files/REPLACE_WITH_FILE_ID/download",
              "host": ["{{base_url}}"],
              "path": ["v4", "files", "REPLACE_WITH_FILE_ID", "download"]
            }
          }
        },
        {
          "name": "GET Download All Files (ZIP)",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/v4/orders/REPLACE_WITH_ORDER_ID/files/download-all",
              "host": ["{{base_url}}"],
              "path": ["v4", "orders", "REPLACE_WITH_ORDER_ID", "files", "download-all"]
            }
          }
        },
        {
          "name": "GET Download via Token (anonymous, from webhook payload)",
          "request": {
            "auth": { "type": "noauth" },
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/v4/files/download?token=REPLACE_WITH_TOKEN",
              "host": ["{{base_url}}"],
              "path": ["v4", "files", "download"],
              "query": [
                { "key": "token", "value": "REPLACE_WITH_TOKEN" }
              ]
            },
            "description": "Anonymous download via the one-time token that arrives in the `order.delivered` webhook. Token is valid 24h. Returns 410 Gone if the order was cancelled after the token was issued."
          }
        }
      ]
    },
    {
      "name": "Endpoint Discovery",
      "item": [
        {
          "name": "GET Available Endpoints (your permissions)",
          "request": {
            "method": "GET",
            "url": {
              "raw": "{{base_url}}/v4/endpoints",
              "host": ["{{base_url}}"],
              "path": ["v4", "endpoints"]
            }
          }
        }
      ]
    }
  ]
}
