Skip to main content
Reference for /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 status queued. Request schema (TrainingJobCreate). This body is the L4 GSM8K example as a raw request: GRPO with LoRA on a single L4-24GB.
Required: 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=grpo or grpo_harness → reward required (reward_source or reward_sources). grpo_agentic is being rebuilt and sft_video_gen is not available: both are a 400.
  • method=sft_text or dpo, and job_type=custom_script → reward fields must be omitted (these learn from the dataset, or own their loop)
  • custom_script is a job_type, not a method: "method": "custom_script" is a 400 (Unknown training method). job_type must be managed or custom_script; anything else is a 400.
  • environments is 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 at from_volume. A dataset_id whose dataset was connected with datasets.from_volume trains: the file is downloaded to the training machine at job start. See Volumes.
  • checkpoint_destination other than {"type": "veri"} (another type, a uri, or credentials) is a 400; see Checkpoint destination schema.
  • hyperparameters.load_in_4bit: true without lora_rank is a 400 (QLoRA trains adapters on a frozen 4-bit base).
  • hyperparameters.lora_rank of 0 or below is a 400: set a positive rank (e.g. 16) for LoRA, or omit it (or send null) for a full fine-tune.
  • job_type=custom_script with provider: "vast" is a 400: custom scripts run on aws and digitalocean; 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_type must be one the provider offers for training: L4-24GB, A10G-24GB, A100-80GB, H100-80GB on aws; see GPU config schema
  • gpu.gpu_count must be a node shape the provider sells for that gpu_type (on aws: 1 or 4 for L4-24GB/A10G-24GB, 8 for A100-80GB/H100-80GB)
Errors: coded errors carry a JSON body {"error": {"code": "...", "message": "..."}}; uncoded ones carry {"error": {"message": "..."}}.
  • 400 Bad Request: the job was rejected (invalid reward source, unknown or unavailable method or job_type, unsupported or unpriced GPU shape, provider or region, invalid or wrongly typed hyperparameters, an unsupported field (environments, volume, or a checkpoint_destination other 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_id doesn’t exist in your workspace.
  • 422 Unprocessable Entity: the body is not valid JSON for this schema (for example a missing output_name). This body is plain text, not JSON.
  • 429 Too Many Requests: code CONCURRENT_JOB_LIMIT when 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. Code RATE_LIMITED (with a Retry-After header and retry_after_seconds) when the API key exceeds its request rate.
  • 503 Service Unavailable: code billing_unavailable when the billing account could not be checked; retry after the Retry-After header (retry_after_seconds in the body).
Response (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 by status (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.
Response is 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:
A job is live while its 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 than max_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 bounds
  • max_points: cap result size, default 500, max 5000
Response:
Every step the trainer logs is stored: the worker batches the steps logged since its last status report (it reports every 5 seconds) and sends them all, so the series has no gaps. 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 has id, 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) or asc (oldest first)
  • after: an event id. Only events created after that event are returned. An id that is not an event of this job is a 400.
  • limit: 1 to 500, default 50
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:
Response:
The SDK pages with 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 are info. 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 the 200 body is at most about 10 seconds behind, and once the job has ended it is complete.
  • 206 Partial Content before the first upload: the latest lines the worker reported, ending in a [partial ...] marker line, or only the boot log, ending in [provisioning ...].
  • 404 with the plain-text body [no logs available yet] for a live job with no log yet.
  • 404 with the JSON error code log_never_uploaded for a finished job whose log was never uploaded.
Use /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, for custom_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 with status: "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 (launching or uploading)
If a deployment or a running export still references the checkpoint, you get 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 after status=completed (400 before that).
A checkpoint is a directory: download every entry of 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:
LoRA and QLoRA are supported on GRPO: see GRPO algorithm. The job object echoes 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:
This method is being rebuilt and is not currently available. For environment-based RL today, use a custom script.

Multi-GPU fields (method=sft_text, dpo)

Added to SFTTextHyperparameters 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

Training shapes by provider (any other 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

Checkpoints are always stored in Veri storage, so {"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-job callback_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 state
  • POST /v1/training_jobs/{id}/presigned-upload: worker requests a presigned S3 PUT URL for checkpoint files
  • POST /v1/training_jobs/{id}/logs/presigned-upload: worker requests a presigned S3 PUT URL for the full stdout
These are documented for completeness; the worker SDK handles them.

Datasets

dataset_id setup before submitting.

Reward functions

Writing and attaching reward_source.

Billing

Billing states and balance checks.

Quickstart

Full SDK + CLI walkthrough.