{
  "name": "HappyHorse 1.0 - Sheets to S3 short-video pipeline",
  "nodes": [
    {
      "parameters": {},
      "id": "1d8a0c4a-8b3a-4f01-a0d3-c1f5d2e87a01",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -840,
        200
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "REPLACE_WITH_YOUR_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "prompts",
          "mode": "name"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "status",
              "lookupValue": "queued"
            }
          ]
        },
        "options": {
          "outputFormatting": "default"
        }
      },
      "id": "2a91b5d7-1e6c-4b22-9d4a-7c9e3f4b22b1",
      "name": "Sheets - Read queued prompts",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        -620,
        200
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.atlascloud.ai/api/v1/model/generateVideo",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"alibaba/happyhorse-1.0/text-to-video\",\n  \"prompt\": $json[\"prompt\"],\n  \"resolution\": \"1080p\",\n  \"duration\": 5,\n  \"aspect_ratio\": \"16:9\"\n}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          },
          "timeout": 30000
        }
      },
      "id": "3c82d6f3-9a4b-4d33-b1e7-2f5a8b9d6c11",
      "name": "Atlas Cloud - Submit job",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -380,
        200
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "notes": "Atlas Cloud header credential = `Authorization: Bearer ${ATLAS_CLOUD_API_KEY}`. Same base URL works for HappyHorse / Seedance / Veo \u2014 only model id changes."
    },
    {
      "parameters": {
        "amount": 5,
        "unit": "seconds"
      },
      "id": "4d9c8a72-b1f2-44e8-bce6-3a1d7e9c4f22",
      "name": "Wait 5s (poll cadence)",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        -160,
        200
      ],
      "notes": "Cut from 30s to 5s after I noticed HappyHorse finishes in ~10s avg. Seedance was 30-60s, polling at 30s was fine. HappyHorse left time on the table."
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://api.atlascloud.ai/api/v1/model/prediction/{{ $('Atlas Cloud - Submit job').item.json.id }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {
          "timeout": 15000
        }
      },
      "id": "5e7a4b89-3c5d-49a1-b2c8-4d8e9f0a1b33",
      "name": "Atlas Cloud - Poll prediction",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        60,
        200
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "8c4d3e2f-1a0b-4c5d-6e7f-8a9b0c1d2e44",
              "leftValue": "={{ $json.status }}",
              "rightValue": "succeeded",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "6f8b9c0d-2e3f-4a5b-9c6d-7e8f9a0b1c44",
      "name": "If done?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        280,
        200
      ],
      "notes": "If status != succeeded -> loop back to Wait. Increase max-iterations on the wait if you hit p99 outliers."
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ $json.output[0].url }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "id": "7a9c0d1e-3f4a-5b6c-9d7e-8f9a0b1c2d55",
      "name": "Download MP4",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        520,
        100
      ],
      "notes": "1080p MP4 lands clean \u2014 no re-encode needed before S3 upload."
    },
    {
      "parameters": {
        "operation": "upload",
        "bucketName": "REPLACE_WITH_S3_BUCKET",
        "fileName": "=happyhorse/{{ $('Sheets - Read queued prompts').item.json.row_number }}-{{ $('Sheets - Read queued prompts').item.json.prompt_id }}.mp4",
        "binaryPropertyName": "data",
        "additionalFields": {
          "acl": "publicRead",
          "storageClass": "STANDARD"
        }
      },
      "id": "8b0d1e2f-4a5b-6c7d-8e9f-0a1b2c3d4e66",
      "name": "S3 - Upload MP4",
      "type": "n8n-nodes-base.awsS3",
      "typeVersion": 2,
      "position": [
        740,
        100
      ],
      "credentials": {
        "aws": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "REPLACE_WITH_YOUR_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "prompts",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "prompt_id": "={{ $('Sheets - Read queued prompts').item.json.prompt_id }}",
            "status": "done",
            "video_url": "={{ $('S3 - Upload MP4').item.json.Location }}",
            "model": "alibaba/happyhorse-1.0",
            "duration_s": "={{ $('Atlas Cloud - Poll prediction').item.json.usage.duration_seconds }}"
          },
          "matchingColumns": [
            "prompt_id"
          ]
        }
      },
      "id": "9c1e2f3a-5b6c-7d8e-9f0a-1b2c3d4e5f77",
      "name": "Sheets - Mark done",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        960,
        100
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Sheets - Read queued prompts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets - Read queued prompts": {
      "main": [
        [
          {
            "node": "Atlas Cloud - Submit job",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Atlas Cloud - Submit job": {
      "main": [
        [
          {
            "node": "Wait 5s (poll cadence)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 5s (poll cadence)": {
      "main": [
        [
          {
            "node": "Atlas Cloud - Poll prediction",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Atlas Cloud - Poll prediction": {
      "main": [
        [
          {
            "node": "If done?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If done?": {
      "main": [
        [
          {
            "node": "Download MP4",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 5s (poll cadence)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download MP4": {
      "main": [
        [
          {
            "node": "S3 - Upload MP4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "S3 - Upload MP4": {
      "main": [
        [
          {
            "node": "Sheets - Mark done",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "1.0.0",
  "meta": {
    "templateCredsSetupCompleted": false,
    "description": "Reads queued prompts from a Google Sheet, generates 1080p video via Atlas Cloud's HappyHorse 1.0 endpoint, polls until succeeded, uploads MP4 to S3, writes back to Sheets. Same shape works for Seedance / Veo by changing the model id on the Atlas Cloud Submit node."
  }
}