{
  "name": "Rendex — Self-Healing Render (retry transient, fall back on permanent)",
  "flow": [
    {
      "id": 1,
      "module": "gateway:CustomWebHook",
      "version": 1,
      "parameters": {
        "hook": 1000401,
        "maxResults": 1
      },
      "mapper": {},
      "metadata": {
        "designer": {
          "x": 0,
          "y": 0,
          "name": "Trigger"
        },
        "notes": "Send a JSON body like {\"title\":\"Weekly report\"}. Swap for any trigger that feeds a render."
      }
    },
    {
      "id": 2,
      "module": "http:ActionSendData",
      "version": 3,
      "parameters": {
        "handleErrors": true,
        "useNewZLibDeCompress": true
      },
      "mapper": {
        "url": "https://api.rendex.dev/v1/screenshot",
        "method": "post",
        "headers": [
          {
            "name": "Authorization",
            "value": "Bearer rdx_YOUR_API_KEY"
          }
        ],
        "qs": [],
        "bodyType": "raw",
        "contentType": "application/json",
        "data": "{\n  \"html\": \"<div style='padding:56px;font-family:system-ui'><h1 style='color:#ea580c'>{{title}}</h1></div>\",\n  \"data\": {\n    \"title\": \"{{1.title}}\"\n  },\n  \"format\": \"png\"\n}",
        "serializeUrl": false,
        "shareCookies": false,
        "parseResponse": false,
        "followRedirect": true,
        "useQuerystring": false,
        "followAllRedirects": false,
        "timeout": "",
        "ca": "",
        "rejectUnauthorized": true,
        "gzip": true,
        "useMtls": false
      },
      "metadata": {
        "designer": {
          "x": 300,
          "y": 0,
          "name": "Rendex — Render (protected)"
        },
        "restore": {
          "expect": {
            "method": { "mode": "chose", "label": "POST" },
            "bodyType": { "label": "Raw" },
            "contentType": { "label": "JSON (application/json)" }
          }
        },
        "notes": "'handleErrors' is ON so a non-2xx response routes to the error handler below instead of failing the run. The body is a normal single-source render — the resilience is all in the onerror route. Replace rdx_YOUR_API_KEY."
      },
      "onerror": [
        {
          "id": 3,
          "module": "builtin:BasicRouter",
          "version": 1,
          "mapper": null,
          "metadata": {
            "designer": {
              "x": 300,
              "y": 300,
              "name": "Error handler — branch on status"
            }
          },
          "routes": [
            {
              "flow": [
                {
                  "id": 4,
                  "module": "builtin:Break",
                  "version": 1,
                  "parameters": {
                    "count": 3,
                    "interval": 15
                  },
                  "filter": {
                    "name": "transient — retry",
                    "conditions": [
                      [
                        { "a": "{{2.statusCode}}", "o": "numeric:greaterorequal", "b": "500" },
                        { "a": "{{2.statusCode}}", "o": "text:equal", "b": "429" }
                      ]
                    ]
                  },
                  "mapper": {},
                  "metadata": {
                    "designer": {
                      "x": 600,
                      "y": 180,
                      "name": "Break — retry 3× / 15s"
                    },
                    "notes": "Transient errors (429, 500/502/503) retry the render up to 3 times, 15s apart. On a 429, Rendex returns a Retry-After header — raise 'interval' to that value so you back off exactly as asked. The OR filter row means 5xx OR 429."
                  }
                }
              ]
            },
            {
              "flow": [
                {
                  "id": 5,
                  "module": "builtin:Resume",
                  "version": 1,
                  "parameters": {},
                  "filter": {
                    "name": "permanent — fall back",
                    "conditions": [
                      [
                        { "a": "{{2.statusCode}}", "o": "numeric:less", "b": "500" }
                      ]
                    ]
                  },
                  "mapper": {
                    "output": "{\n  \"url\": \"https://cdn.example.com/placeholder.png\"\n}"
                  },
                  "metadata": {
                    "designer": {
                      "x": 600,
                      "y": 420,
                      "name": "Resume — fallback placeholder"
                    },
                    "notes": "Permanent errors (400 'one source', 401 bad key, 403 plan) can't heal by retrying, so Resume continues the flow with a fallback value — a placeholder URL here. Downstream reads this instead of dead-lettering. Point 'output' at your own placeholder or a review-queue flag."
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ],
  "metadata": {
    "instant": false,
    "version": 1,
    "scenario": {
      "roundtrips": 1,
      "maxErrors": 3,
      "autoCommit": true,
      "autoCommitTriggerLast": true,
      "sequential": false,
      "confidential": false,
      "dataloss": false,
      "dlq": false,
      "freshVariables": false
    },
    "designer": {
      "orphans": []
    },
    "zone": "us1.make.com"
  }
}
