/v1/training_jobs/*. All endpoints require Bearer token auth (Authorization: Bearer vk_...) unless flagged worker-only.
Endpoints
Worker-only endpoints (used by the runner agent, authenticated via per-job
callback_token): POST /status, POST /presigned-upload, POST /logs/presigned-upload. Documented in the worker-callbacks appendix at the bottom.
POST /v1/training_jobs
Create a job. Returns immediately with statusqueued.
Request schema (TrainingJobCreate). This body is the L4 GSM8K example as a raw request: GRPO with LoRA on a single L4-24GB.
base_model, dataset_id, output_name, gpu (a custom_script job needs script instead of base_model and dataset_id). Required for method=grpo and grpo_harness: reward_source (or reward_sources).
Request fields:
dataset_id also accepts dataset stream references: a stream’s name or id with @latest or @snap-N (for example acme-feedback@latest). The job resolves and stores the pinned snapshot as dataset_snapshot_id in its response, so later appends never change what the job trains on.
Reward fields: reward_source is the Python source text of a TRL-signature reward function. For multiple weighted rewards, pass reward_sources (a list) plus optional reward_weights (same length). Limits: at most 8 sources, 256 KiB each, and each source must define a function; violations are HTTP 400 at submit. See Reward functions.
Validation rules:
method=grpoorgrpo_harness→ reward required (reward_sourceorreward_sources).grpo_agenticis being rebuilt andsft_video_genis not available: both are a 400.method=sft_textordpo, andjob_type=custom_script→ reward fields must be omitted (these learn from the dataset, or own their loop)custom_scriptis ajob_type, not amethod:"method": "custom_script"is a 400 (Unknown training method).job_typemust bemanagedorcustom_script; anything else is a 400.environmentsis not supported: a non-empty value is a 400 (no available method reads it).- A job-level
volume(a whole-volume read-only mount at/mnt/<name>) is coming soon: until it is enabled, it is a 400 whose message points atfrom_volume. Adataset_idwhose dataset was connected withdatasets.from_volumetrains: the file is downloaded to the training machine at job start. See Volumes. checkpoint_destinationother than{"type": "veri"}(anothertype, auri, orcredentials) is a 400; see Checkpoint destination schema.hyperparameters.load_in_4bit: truewithoutlora_rankis a 400 (QLoRA trains adapters on a frozen 4-bit base).hyperparameters.lora_rankof0or below is a 400: set a positive rank (e.g.16) for LoRA, or omit it (or sendnull) for a full fine-tune.job_type=custom_scriptwithprovider: "vast"is a 400: custom scripts run onawsanddigitalocean; Vast runs managed methods only.- A hyperparameter value of the wrong type (for example
"lora_rank": "8"or"packing": "yes") is a 400 that names the key. gpu.gpu_typemust be one the provider offers for training:L4-24GB,A10G-24GB,A100-80GB,H100-80GBonaws; see GPU config schemagpu.gpu_countmust be a node shape the provider sells for thatgpu_type(onaws:1or4forL4-24GB/A10G-24GB,8forA100-80GB/H100-80GB)
{"error": {"code": "...", "message": "..."}}; uncoded ones carry {"error": {"message": "..."}}.
400 Bad Request: the job was rejected (invalid reward source, unknown or unavailable method orjob_type, unsupported or unpriced GPU shape, provider or region, invalid or wrongly typed hyperparameters, an unsupported field (environments,volume, or acheckpoint_destinationother than Veri storage), or a dataset the method can’t use: a row format it can’t train on, or one that reads from a volume). The message names the problem.402 Payment Required: no billing account is linked to the workspace yet (see Billing).404 Not Found:dataset_iddoesn’t exist in your workspace.422 Unprocessable Entity: the body is not valid JSON for this schema (for example a missingoutput_name). This body is plain text, not JSON.429 Too Many Requests: codeCONCURRENT_JOB_LIMITwhen the workspace already has its maximum of in-flight jobs (4 by default; failed, completed and cancelled jobs don’t count). Wait for one to finish or cancel one, then resubmit. CodeRATE_LIMITED(with aRetry-Afterheader andretry_after_seconds) when the API key exceeds its request rate.503 Service Unavailable: codebilling_unavailablewhen the billing account could not be checked; retry after theRetry-Afterheader (retry_after_secondsin the body).
TrainingJobResponse, 201). hyperparameters echoes every field of the method’s schema (see the note under GRPOHyperparameters about echoed defaults):
GET /v1/training_jobs
List your jobs, newest first. Filter bystatus (queued / provisioning / configuring / running / completed / failed / cancelled) or method (grpo / grpo_harness / sft_text / dpo / custom_script). limit is 1 to 100 (default 20); pass the last job’s id as after for the next page. An unknown status is a 400.
PaginatedList[TrainingJobResponse]: {"object": "list", "data": [...], "has_more": false}.
GET /v1/training_jobs/{id}
Single job. Same shape as the create response, with the lifecycle timestamps, progress and cost filled in as the job moves. A running job, trimmed to those fields:status is queued, provisioning, configuring or running (these count toward the in-flight job limit); completed, failed and cancelled are final. configuring is the worker setting up the machine (dependencies, data, model); the setup events time each stage.
Lifecycle and cost fields:
Timestamps of jobs created before a field existed stay
null.
Error and refund. error is null unless the job failed: {"code": "...", "message": "...", "attribution": "customer"}. code is one of the failure codes and attribution says who the failure belongs to (customer, platform or pending; absent on jobs that failed before it existed). A platform-attributed failure also carries refund: {"status": "credited", "amount_usd": 1.84}, with status one of approved, credited, pending or denied. refund is omitted otherwise.
GET /v1/training_jobs/{id}/metrics
Sampled training metrics for charting. Server-side stride downsampling if you ask for more thanmax_points. Metric names are the trainer’s own log keys (for GRPO: loss, reward, reward_std, entropy, completions/mean_length, and kl when kl_coef is above 0; see Reading the metrics).
Query params:
keys: comma-separated metric names (e.g.loss,reward,reward_std). Omit for all available.from_step,to_step: inclusive boundsmax_points: cap result size, default500, max5000
max_points thins it on read.
Step timing and system keys
Next to the trainer’s own keys, the worker writes these per step. All are numbers; a key is absent when its value is unknown.{i} is the GPU’s index on its machine, from 0. The time/ keys come from the managed methods’ trainers: time/gen_s and time/reward_s are GRPO only, and time/weight_sync_s is grpo_harness only. The sys/ values are the latest sample from a sampler that reads the machine every 15 seconds, so neighboring steps can carry the same sample; GPU values come from NVIDIA’s NVML, so hosts without it (AMD GPUs) have no sys/gpu keys.
GET /v1/training_jobs/{id}/events
The job’s lifecycle events: the launch, each setup stage, checkpoints, warnings, the outcome and billing. Each event hasid, object ("training_job.event"), job_id, level (info / warning / error), event_type, message, metadata (an object or null), request_id, and created_at.
Query params:
order:desc(newest first, the default) orasc(oldest first)after: an eventid. Only events created after that event are returned. An id that is not an event of this job is a400.limit: 1 to 500, default50
has_more is true when more events match than the page holds. To read the whole history, or to poll a live job for new events, page forward with order=asc and the last id you have as after:
client.training_jobs.events(job_id, after=..., order="asc", limit=500) and the CLI with veri jobs events.
Event types
Setup, roughly in the order a job goes through it. All areinfo. waiting_for_provider_gpu, job_submitted, job_configuring and job_running come from Veri; the others come from the worker on the training machine.
Training and outcome:
Billing, after the job ends:
Unknown
event_type values can appear as Veri adds events: treat the list as open.
GET /v1/training_jobs/{id}/logs
Streams the job’s log as server-sent events (text/event-stream): each data: event is one log line, starting with the instance’s boot log and then the worker’s output as it arrives. The stream ends with a final [Job completed], [Job failed], or [Job cancelled] event. A job that has not reached a GPU yet returns 400 (Job has not been submitted yet).
For the full log after a job completes, use /logs/full instead.
GET /v1/training_jobs/{id}/logs/full
Returns the instance’s boot log followed by the worker’s stdout, as plain text. The worker uploads its log every 10 seconds while it runs and once more when it exits, so during a run the200 body is at most about 10 seconds behind, and once the job has ended it is complete.
206 Partial Contentbefore the first upload: the latest lines the worker reported, ending in a[partial ...]marker line, or only the boot log, ending in[provisioning ...].404with the plain-text body[no logs available yet]for a live job with no log yet.404with the JSON error codelog_never_uploadedfor a finished job whose log was never uploaded.
/logs to follow a live job and /logs/full to read a finished one.
GET /v1/training_jobs/{id}/files
The files a job was submitted with: its inline reward sources and, forcustom_script and grpo_harness jobs, the code archive, the dependency file and the entrypoint. The script’s env is never returned. 404 for a job outside your workspace.
The SDK returns the list with
client.training_jobs.files(job_id).
POST /v1/training_jobs/{id}/cancel
Cancel a non-terminal job. Cancellation is synchronous: Veri stops provisioning, terminates the instance, and returns the job withstatus: "cancelled". The worker gets no chance to flush, so anything not yet uploaded is lost. Cancelling a job that is already completed, failed, or cancelled is a 400.
DELETE /v1/training_jobs/{id}
Hard-delete the job row + cascading metrics + events + S3 artifacts (logs, status files, checkpoints). Returns{"id": "...", "object": "training_job", "deleted": true}.
Constraints:
- Job must be in a terminal state (
completed,failed,cancelled) - No deployment may reference this job (
source="training_job",source_job_id=this_id), whatever the deployment’s status, including stopped ones - No Hugging Face export of a model saved from this job may still be running (
launchingoruploading)
409 Conflict naming it: delete the deployment, or wait for the export to end, first.
GET /v1/training_jobs/{id}/model
Presigned S3 download URLs for the final HF-format checkpoint, one per file. Only available afterstatus=completed (400 before that).
files, keeping its path, to get something from_pretrained can load (the SDK’s job.download() does this). download_url points at the weights file alone. URLs expire in 1 hour; re-call to get fresh ones.
artifact_type says what files holds: adapter for a job trained with lora_rank (a PEFT adapter plus tokenizer, loaded on top of the base model), full for a full fine-tune. For a LoRA job, merged_files lists the merged weights (adapter merged into the base model) that Veri keeps next to the adapter and that a deployment of the job serves; download them the same way for a standalone checkpoint. The field is absent for full fine-tunes and for LoRA jobs trained before merged weights were kept.
Hyperparameter schemas
GRPOHyperparameters (method=grpo)
See GRPO algorithm for the full walkthrough. Fields, with the value a job uses when the field is omitted:kl_coef only when the job set it. It echoes max_prompt_length: 1024 for a job that omitted it; the trainer applies that only with use_unsloth.
AgenticGRPOHyperparameters (method=grpo_agentic)
Inherits all GRPO fields plus:Multi-GPU fields (method=sft_text, dpo)
Added toSFTTextHyperparameters and DPOHyperparameters for jobs with gpu_count above 1; see Multi-GPU training for semantics and validation. All optional; absent keys are not echoed back.
micro_batch_size, gradient_accumulation_steps, global_batch_size, gradient_checkpointing and precision are honoured on single-GPU jobs too; the sharding keys need gpu_count above 1.
GPU config schema
gpu_type or gpu_count is rejected at submit with a 400 that lists what the provider offers):
AMD
MI300X-192GB is not yet available for training. Run veri gpu list to see current availability and pricing for every shape.
Checkpoint destination schema
{"type": "veri"} (or omitting the field) is the only accepted value. Any other value is a 400 at submit: another type (such as s3 or gs), a uri, or credentials. The destination is not stored. Writing checkpoints directly to your own bucket is not supported yet. To get the weights out, download them (GET /v1/training_jobs/{id}/model, veri jobs download <job_id>, or job.download()), or push them to Hugging Face with hf_push.
Worker callbacks (appendix)
These exist for the runner agent and are NOT for users to call directly. Auth is per-jobcallback_token (a 32-byte random secret stored in the DB on job creation), not your API key.
POST /v1/training_jobs/{id}/status: worker reports progress, metrics, terminal statePOST /v1/training_jobs/{id}/presigned-upload: worker requests a presigned S3 PUT URL for checkpoint filesPOST /v1/training_jobs/{id}/logs/presigned-upload: worker requests a presigned S3 PUT URL for the full stdout
Related
Datasets
dataset_id setup before submitting.Reward functions
Writing and attaching
reward_source.Billing
Billing states and balance checks.
Quickstart
Full SDK + CLI walkthrough.

