DocsIntegrations
curl
Every call of the round trip from a shell: upload a file, create a job from JSON, wait for it and download the outputs.
What you build#
A shell script that uploads a local file, creates a job from a JSON body, waits for the job to end
and saves its outputs. It is the shortest way to see every call and every response; the same calls
are what the other guides make from their servers. It uses plain HTTP with curl and reads the
responses with jq.
The whole project: videxo-curl.zip, or browse it in the examples repository (opens in a new tab).
A shell cannot receive webhooks. To verify them, follow a server guide: Express, Next.js or SvelteKit.
Before you start#
- An API key from the dashboard's API keys page, in
VIDEXO_API_KEY. curlandjq.
Install#
Nothing to install beyond curl and jq. Unpack the project and make the script executable:
chmod +x videxo.shCopy failed. The text is selected; copy it with your keyboard.
Create a job#
The JSON bodies sit in requests/: one MP4 through POST /v1/transcode, and an MP4, a poster and
a GIF preview in one job through POST /v1/jobs. The script puts the upload in as the input with
jq and sends an Idempotency-Key, so running the same call again returns
the same job:
# The body from requests/, with the upload as its input. The Idempotency-Key makes a retry safe: the
# same key answers the same job, never a second one.
if [ "$kind" = jobs ]; then
body=$(jq --arg input "$input" '.inputs.talk = $input' "$here/requests/jobs.json")
path=/v1/jobs
else
body=$(jq --arg input "$input" '.input = $input' "$here/requests/transcode.json")
path=/v1/transcode
fi
job=$(call "$api$path" -H 'Content-Type: application/json' -H "Idempotency-Key: $kind-$(jq -r .id <<< "$upload")" -d "$body")
id=$(jq -r .id <<< "$job")Copy failed. The text is selected; copy it with your keyboard.
Every call goes through call, which prints an error response's problem details:
its status, code, detail and the field errors.
# Every error answer is problem details (application/problem+json): print its code and detail.
call() {
local body status
body=$(mktemp)
# A request that gets no answer stops the script too (set -e does not reach into $(...)).
status=$(curl -sS -o "$body" -w '%{http_code}' -H "$auth" "$@") || exit 1
if [ "$status" -ge 400 ]; then
jq -r '"\(.status) \(.code): \(.detail)", (.errors[]? | " \(.pointer // .parameter // .header): \(.detail)")' "$body" >&2
exit 1
fi
cat "$body"
}Copy failed. The text is selected; copy it with your keyboard.
Then the script reads the job every 2 seconds until it ends:
while state=$(jq -r .state <<< "$job"); [ "$state" = queued ] || [ "$state" = running ]; do
echo "$id: $state"
sleep 2
job=$(call "$api/v1/jobs/$id")
done
echo "$id: $state $(jq -r '.error // empty | "\(.code): \(.message)"' <<< "$job")"Copy failed. The text is selected; copy it with your keyboard.
Receive the webhook#
Not from a shell: a webhook needs a public https server. Pass "webhook_url" in the body to have
the job call yours when it ends, and see Webhooks for how to verify it.
Upload a file#
POST /v1/uploads with the exact size returns a one-time upload_url; one PUT of the bytes,
with no API key, completes it. The upload's input_url, videxo://uploads/<id>, is the job's
input:
name=$(basename "$file" | tr -c 'A-Za-z0-9_.\n-' '-')
size=$(wc -c < "$file" | tr -d ' ')
upload=$(call "$api/v1/uploads" -H 'Content-Type: application/json' \
-d "{\"filename\":\"$name\",\"size_bytes\":$size,\"content_type\":\"application/octet-stream\"}")
# The upload URL takes one PUT and needs no API key.
curl -sS --fail-with-body -X PUT "$(jq -r .upload_url <<< "$upload")" \
-H 'Content-Type: application/octet-stream' --data-binary "@$file" > /dev/null
input=$(jq -r .input_url <<< "$upload")Copy failed. The text is selected; copy it with your keyboard.
See Files for the size limit and how long an upload lasts.
Download the outputs#
GET /v1/jobs/{id}/outputs/{alias} returns a redirect to a fresh signed link; curl -L follows
it:
# Each output through GET /v1/jobs/{id}/outputs/{alias}: it redirects to a fresh signed link, which
# needs no API key (curl does not send the Authorization header to another host).
for alias in $(jq -r '.outputs[].alias' <<< "$job"); do
filename=$(jq -r --arg alias "$alias" '.outputs[] | select(.alias == $alias) | .filename' <<< "$job")
curl -sS --fail-with-body -L -H "$auth" "$api/v1/jobs/$id/outputs/$alias" -o "$filename"
echo "saved $filename"
doneCopy failed. The text is selected; copy it with your keyboard.
A job's outputs[].download_url is also a signed link, valid until download_url_expires_at.
Run it#
export VIDEXO_API_KEY=... # your key
./videxo.sh talk.mp4 # one MP4
./videxo.sh talk.mp4 jobs # MP4, poster and GIF preview in one jobCopy failed. The text is selected; copy it with your keyboard.
Next steps#
- Operations and Presets: what else a JSON job can make.
- Quickstart: the same calls one at a time.