Blog/Build a media ingestion workflow with n8n
OverviewAll posts
Tutorial

Build a media ingestion workflow with n8n

Connect n8n to Tornado API with HTTP Request nodes, preserve the job ID, bound polling, and route completed media to your next processing step.

VTornado API team
Covered in this article
Configure HTTP Request nodes
Keep one job ID through polling
Route outcomes and recover interrupted work
7 min reading time
Published October 6, 2026
VProduct guides by Velys Software

You can connect Tornado API to n8n with ordinary HTTP Request nodes: submit one media URL, keep the returned job ID, wait, and check the result. The useful output is a delivered file that your next service can consume—not simply a successful submission request.

This recipe covers one video per workflow execution. It deliberately separates the submission node from the polling loop, so checking progress never creates another media job. It is a configuration guide with locally checked JavaScript, not an imported workflow tested against a live n8n instance.

Prepare a small, inspectable workflow

Start with a Manual Trigger and one source video that you are authorized to process. Use a single-video URL rather than a playlist or podcast-show URL: those can return a batch response and require a different workflow. The create-job reference describes those response shapes.

Build this path, using the node names below exactly where the expressions refer to them:

Manual Trigger → Create job → Remember job → Wait → Get status → Route result → Switch
                                            ↑                                  |
                                            └──────── poll output ──────────────┘

The other Switch outputs end in separate handling steps. No connection returns to Create job. During development, inspect each node's JSON output before connecting the next step. Re-executing the entire workflow from its trigger submits a new job.

Configure the two HTTP requests

In Create job, select POST and the URL https://api.tornadoapi.io/jobs. Use a generic Header Auth credential named for your Tornado account, with header name x-api-key and your key stored as the credential value. Do not paste the key into example code or exported workflow data.

Enable a JSON request body and supply the following object, replacing the placeholder with your single-video source URL:

{
  "url": "REPLACE_WITH_YOUR_SINGLE_VIDEO_URL",
  "format": "mp4"
}

For both HTTP nodes, choose JSON response format, leave Include Response Headers and Status disabled, and leave Never Error disabled. The code below expects a response body directly. A full-response wrapper would instead put that body under another property. See the HTTP Request node documentation.

Do not enable automatic retries on Create job without a submission-reconciliation strategy. If the connection breaks after acceptance, repeating the POST can submit another job. A known job ID allows you to continue through Get status instead.

Keep the ID outside the changing response body

Add a JavaScript Code node named Remember job, set to Run Once for All Items. This recipe requires exactly one input item. Paste:

const items = $input.all();
if (items.length !== 1) throw new Error('Expected one submission');
const id = items[0].json.job_id;
if (typeof id !== 'string' || !id.trim()) {
  throw new Error('No single job_id: inspect the submission response');
}
return [{ json: { job_id: id, deadlineMs: Date.now() + 30 * 60 * 1000 } }];

The 30-minute deadline is an example of your workflow's waiting policy, not a processing-time promise. Ending the wait does not cancel a job.

Set Wait to After Time Interval, with an example interval of one minute. Choose an interval appropriate to your account limits and workload. n8n's Wait documentation notes that waits shorter than 65 seconds remain in the running process instead of offloading execution data to the database. A short Wait node alone is therefore not a durable recovery plan.

Configure Get status as GET, using the same API credential. Set its URL to this expression:

{{ 'https://api.tornadoapi.io/jobs/' + encodeURIComponent($('Remember job').first().json.job_id) }}

The explicit reference to Remember job survives the replacement of the current item by each status response. Using .first() is intentional only because this workflow handles one job. Do not apply it to a list of videos: every item could then check the first video's ID. n8n documents previous-node output access separately from current input access.

Route the observed outcome

Add another JavaScript Code node, Route result, in Run Once for All Items mode:

const context = $('Remember job').first().json;
const result = $input.first().json;
const active = new Set(['Pending', 'Processing']);
const unsuccessful = new Set([
  'Failed', 'Warning', 'Skipped', 'Cancelled', 'CancelledByAdmin'
]);
let action = 'review';
if (result.id === context.job_id) {
  if (result.status === 'Completed') {
    action = typeof result.s3_url === 'string' && result.s3_url.trim()
      ? 'inspect_delivery' : 'check_storage';
  } else if (unsuccessful.has(result.status)) {
    action = 'handle_outcome';
  } else if (active.has(result.status)) {
    action = Date.now() < context.deadlineMs ? 'poll' : 'resume_later';
  }
}
return [{ json: { ...context, ...result, action } }];

The status names and delivery field follow the job-status contract. Unknown statuses or a mismatched ID go to review, rather than silently continuing forever.

In a Switch node, match {{ $json.action }} against the values below. Configure an explicit fallback to the review path for any unmatched value.

ActionNext step
pollReturn to Wait, then Get status
inspect_deliveryValidate the delivered file before invoking the consumer
check_storageInvestigate the missing delivery link
handle_outcomeRecord the final outcome and decide whether intervention is needed
resume_laterSave the job ID for later lookup; stop this waiting loop
reviewPreserve the response for investigation with sensitive fields restricted

An HTTP error stops before this router under the selected HTTP settings. Handle authentication and validation failures by correcting the request. For transient GET failures, add a bounded delayed retry policy; resume the same ID. Never convert a failed lookup into a fresh POST.

Make the handoff recoverable

For production use, persist the job ID alongside your application's media record immediately after submission. Add a separate recovery entry point that loads that record and starts status lookup without passing through Create job. n8n execution history is useful for debugging, but your application still needs to know whether downstream work was accepted and completed.

Treat the returned delivery URL as sensitive, potentially temporary access. Do not forward the Tornado API credential to the file host. Check the file's availability and the consumer's format requirements; S3 delivery validation explains why a completed media job is not sufficient evidence of a successful downstream handoff.

Both JavaScript snippets were exercised locally with synthetic n8n input adapters. The checks cover invalid submission input, active and terminal statuses, deadline expiry, unknown statuses, mismatched IDs and a missing delivery URL. They do not establish live n8n wiring, credential access, request latency or file delivery. Verify those boundaries on your own instance before scheduling larger workloads.

Start with the first media workflow to check your account and destination. Once this single-video path works, use the worker recovery pattern to plan restarts, or evaluate webhooks versus polling when your automation needs a notification-driven path.