Jobs
Manage your videos, ask questions, and build applications with the Platform API. Explore the operations below for request details, responses, and examples.
No matching documentation. Try a different search.
Jobs
Standing questions the platform answers for you. A job runs on each new video when it finishes processing, on a daily schedule over a window of videos, or by hand, and records a result you can read, print or deliver to your notification channels.
GET/api/v1/projects/:projectId/jobsList jobs
List jobs
Each job carries run_count and platform_tokens_total, the tokens its runs have spent. Add ?include=archived to include archived jobs.
POST/api/v1/projects/:projectId/jobsCreate a job
Create a job
name and prompt are required. Needs a role that can change jobs. Returns 201 with the job.
trigger: auto_ops_complete (each video, once it finishes processing — the default), schedule or manual.
run_scope: per_video (default) or project — one run over a window of videos. Project jobs run on schedule or manual, and a scheduled job needs schedule: { at, timezone?, days? }. The timezone defaults to the project's site timezone.
output_contract: free (default), grade, findings or report (needs report_type_id — see Report types).
audience: recipient (default, written for someone outside the team) or team.
delivery: { channel_ids, full_report? } — the notification channels that receive each result.
scope: { sources?, include_uploads?, include_live? } — which videos the job applies to.
curl -X POST https://platform.endlessai.com/api/v1/projects/PROJECT_ID/jobs \
-H "X-Api-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Opening checklist",
"prompt": "Was the shop open by 09:00 with the shutters fully up?",
"trigger": "auto_ops_complete",
"output_contract": "grade",
"delivery": { "channel_ids": ["CHANNEL_ID"] }
}'import requests
resp = requests.post(
f"{BASE}/projects/{PROJECT_ID}/jobs",
headers={"X-Api-Key": API_KEY, "Content-Type": "application/json"},
json={
"name": "Opening checklist",
"prompt": "Was the shop open by 09:00 with the shutters fully up?",
"trigger": "auto_ops_complete",
"output_contract": "grade",
"delivery": {"channel_ids": ["CHANNEL_ID"]},
},
)
job = resp.json()["data"]
print(job["id"])const res = await fetch(
`${BASE}/projects/${projectId}/jobs`,
{
method: "POST",
headers: { "X-Api-Key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
name: "Opening checklist",
prompt: "Was the shop open by 09:00 with the shutters fully up?",
trigger: "auto_ops_complete",
output_contract: "grade",
delivery: { channel_ids: ["CHANNEL_ID"] }
})
}
);
const { data: job } = await res.json();GET/api/v1/projects/:projectId/jobs/:jobIdGet a job
Get a job
Send the request to https://platform.endlessai.com/api/v1/projects/:projectId/jobs/:jobId using your project key or user token.
PATCH/api/v1/projects/:projectId/jobs/:jobIdUpdate a job
Update a job
Send only the fields to change. Set enabled to false to pause a job. run_scope cannot be changed after creation.
DELETE/api/v1/projects/:projectId/jobs/:jobIdDelete a job
Delete a job
A job that already has results is archived instead, and its results are kept. The response says which: { "deleted": true } or { "archived": true, "job": { ... } }.
POST/api/v1/projects/:projectId/jobs/:jobId/runRun a job now
Run a job now
Needs the right to run jobs; a run spends the project's tokens. For a per-video job send video_id; for a project job send an optional from / to window (ISO 8601). Returns 202 with the result_id to poll. Add ?wait=1 to wait up to 30 seconds for the finished result.
curl -X POST https://platform.endlessai.com/api/v1/projects/PROJECT_ID/jobs/JOB_ID/run \
-H "X-Api-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "video_id": "VIDEO_ID" }'import time
import requests
headers = {"X-Api-Key": API_KEY}
run = requests.post(
f"{BASE}/projects/{PROJECT_ID}/jobs/{JOB_ID}/run",
headers=headers,
json={"video_id": VIDEO_ID},
).json()["data"]
result_id = run["result_id"]
while True:
result = requests.get(
f"{BASE}/projects/{PROJECT_ID}/job-results/{result_id}",
headers=headers,
).json()["data"]
if result["status"] != "running":
break
time.sleep(10)
print(result["status"], result["verdict"])const res = await fetch(
`${BASE}/projects/${projectId}/jobs/${jobId}/run`,
{
method: "POST",
headers: { "X-Api-Key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ video_id: videoId })
}
);
const { data: run } = await res.json();
console.log(run.result_id);{
"data": {
"accepted": true,
"job_id": "JOB_ID",
"video_id": "VIDEO_ID",
"result_id": "RESULT_ID",
"result": { "id": "RESULT_ID", "status": "running" }
},
"meta": {}
}GET/api/v1/projects/:projectId/job-resultsList job results
List job results
Newest first. Query params: job_id, video_id, status, limit (default 50, max 200). status is one of running, completed, not_applicable, failed, budget_stopped or skipped_scope.
GET/api/v1/projects/:projectId/job-results/:resultIdGet a job result
Get a job result
{
"data": {
"id": "RESULT_ID",
"job_id": "JOB_ID",
"job_name": "Opening checklist",
"video_id": "VIDEO_ID",
"status": "completed",
"verdict": "Pass — shutters up at 08:52",
"body": "The shop opened at 08:52. Both shutters were fully raised...",
"created_at": "2026-10-01T06:05:12.000Z"
},
"meta": {}
}GET/api/v1/projects/:projectId/job-results/:resultId/viewView a job result as a printable page
View a job result as a printable page
Returns an HTML page with the result and its evidence frames, ready to print or save as PDF.