curl https://compute.prentis.ai/v1/accounts/my-account/supervisedFineTuningJobs \
-H "Authorization: Bearer $PRENTIS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: support-lora-v1" \
-d '{
"baseModel": "accounts/maas/models/your-base-model",
"inputDatasetVersion": "accounts/my-account/datasets/support-chats",
"config": {
"outputModelId": "support-lora-v1",
"epochs": 2,
"learningRate": "1e-4",
"loraRank": 16
}
}'import os
import requests
BASE = "https://compute.prentis.ai/v1/accounts/my-account"
HEADERS = {"Authorization": f"Bearer {os.environ['PRENTIS_API_KEY']}"}
job = requests.post(
f"{BASE}/supervisedFineTuningJobs",
headers={**HEADERS, "Idempotency-Key": "support-lora-v1"},
json={
"baseModel": "accounts/maas/models/your-base-model",
"inputDatasetVersion": "accounts/my-account/datasets/support-chats",
"config": {"outputModelId": "support-lora-v1", "epochs": 2, "learningRate": "1e-4"},
},
)
job.raise_for_status()
print(job.json()["name"], job.json()["state"])const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
baseModel: '<string>',
inputDatasetVersion: '<string>',
config: {
outputModelId: '<string>',
loraRank: 123,
learningRate: '<string>',
epochs: 123,
batchSize: 123,
maxContextLength: 123,
earlyStop: true,
evalAutoCarveoutBasisPoints: 4999,
lrSchedule: '<string>',
warmupSteps: 123,
dpoBeta: '<string>'
},
evaluationDatasetVersion: '<string>'
})
};
fetch('https://compute.prentis.ai/v1/accounts/{account}/supervisedFineTuningJobs', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://compute.prentis.ai/v1/accounts/{account}/supervisedFineTuningJobs",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'baseModel' => '<string>',
'inputDatasetVersion' => '<string>',
'config' => [
'outputModelId' => '<string>',
'loraRank' => 123,
'learningRate' => '<string>',
'epochs' => 123,
'batchSize' => 123,
'maxContextLength' => 123,
'earlyStop' => true,
'evalAutoCarveoutBasisPoints' => 4999,
'lrSchedule' => '<string>',
'warmupSteps' => 123,
'dpoBeta' => '<string>'
],
'evaluationDatasetVersion' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://compute.prentis.ai/v1/accounts/{account}/supervisedFineTuningJobs"
payload := strings.NewReader("{\n \"baseModel\": \"<string>\",\n \"inputDatasetVersion\": \"<string>\",\n \"config\": {\n \"outputModelId\": \"<string>\",\n \"loraRank\": 123,\n \"learningRate\": \"<string>\",\n \"epochs\": 123,\n \"batchSize\": 123,\n \"maxContextLength\": 123,\n \"earlyStop\": true,\n \"evalAutoCarveoutBasisPoints\": 4999,\n \"lrSchedule\": \"<string>\",\n \"warmupSteps\": 123,\n \"dpoBeta\": \"<string>\"\n },\n \"evaluationDatasetVersion\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://compute.prentis.ai/v1/accounts/{account}/supervisedFineTuningJobs")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"baseModel\": \"<string>\",\n \"inputDatasetVersion\": \"<string>\",\n \"config\": {\n \"outputModelId\": \"<string>\",\n \"loraRank\": 123,\n \"learningRate\": \"<string>\",\n \"epochs\": 123,\n \"batchSize\": 123,\n \"maxContextLength\": 123,\n \"earlyStop\": true,\n \"evalAutoCarveoutBasisPoints\": 4999,\n \"lrSchedule\": \"<string>\",\n \"warmupSteps\": 123,\n \"dpoBeta\": \"<string>\"\n },\n \"evaluationDatasetVersion\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://compute.prentis.ai/v1/accounts/{account}/supervisedFineTuningJobs")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"baseModel\": \"<string>\",\n \"inputDatasetVersion\": \"<string>\",\n \"config\": {\n \"outputModelId\": \"<string>\",\n \"loraRank\": 123,\n \"learningRate\": \"<string>\",\n \"epochs\": 123,\n \"batchSize\": 123,\n \"maxContextLength\": 123,\n \"earlyStop\": true,\n \"evalAutoCarveoutBasisPoints\": 4999,\n \"lrSchedule\": \"<string>\",\n \"warmupSteps\": 123,\n \"dpoBeta\": \"<string>\"\n },\n \"evaluationDatasetVersion\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"name": "accounts/my-account/trainingJobs/01K7AX3V9Q2M8T4R6Y0B1C5D7E",
"jobId": "01K7AX3V9Q2M8T4R6Y0B1C5D7E",
"jobType": "JOB_TYPE_SFT",
"state": "JOB_STATE_QUEUED",
"baseModel": "accounts/maas/models/your-base-model",
"inputDatasetVersion": "accounts/my-account/datasets/support-chats/versions/1",
"config": {
"outputModelId": "support-lora-v1",
"loraRank": 16,
"learningRate": 0.0001,
"epochs": 2
},
"estimatedCost": {
"amount": "1.93",
"currency": "USD"
},
"execution": {
"thirdParty": false
},
"createTime": "2026-10-10T08:00:00Z"
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}Create a supervised fine-tuning job
Start supervised fine-tuning: the model learns to answer the way the last assistant turn of each example does. The job is accepted in state JOB_STATE_QUEUED and runs in the background — a 201 means it has been accepted, not that it has started. If the job cannot be allowed to run (for example, your organization has not accepted the data processing addendum), it is refused here and none of your data leaves.
curl https://compute.prentis.ai/v1/accounts/my-account/supervisedFineTuningJobs \
-H "Authorization: Bearer $PRENTIS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: support-lora-v1" \
-d '{
"baseModel": "accounts/maas/models/your-base-model",
"inputDatasetVersion": "accounts/my-account/datasets/support-chats",
"config": {
"outputModelId": "support-lora-v1",
"epochs": 2,
"learningRate": "1e-4",
"loraRank": 16
}
}'import os
import requests
BASE = "https://compute.prentis.ai/v1/accounts/my-account"
HEADERS = {"Authorization": f"Bearer {os.environ['PRENTIS_API_KEY']}"}
job = requests.post(
f"{BASE}/supervisedFineTuningJobs",
headers={**HEADERS, "Idempotency-Key": "support-lora-v1"},
json={
"baseModel": "accounts/maas/models/your-base-model",
"inputDatasetVersion": "accounts/my-account/datasets/support-chats",
"config": {"outputModelId": "support-lora-v1", "epochs": 2, "learningRate": "1e-4"},
},
)
job.raise_for_status()
print(job.json()["name"], job.json()["state"])const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
baseModel: '<string>',
inputDatasetVersion: '<string>',
config: {
outputModelId: '<string>',
loraRank: 123,
learningRate: '<string>',
epochs: 123,
batchSize: 123,
maxContextLength: 123,
earlyStop: true,
evalAutoCarveoutBasisPoints: 4999,
lrSchedule: '<string>',
warmupSteps: 123,
dpoBeta: '<string>'
},
evaluationDatasetVersion: '<string>'
})
};
fetch('https://compute.prentis.ai/v1/accounts/{account}/supervisedFineTuningJobs', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://compute.prentis.ai/v1/accounts/{account}/supervisedFineTuningJobs",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'baseModel' => '<string>',
'inputDatasetVersion' => '<string>',
'config' => [
'outputModelId' => '<string>',
'loraRank' => 123,
'learningRate' => '<string>',
'epochs' => 123,
'batchSize' => 123,
'maxContextLength' => 123,
'earlyStop' => true,
'evalAutoCarveoutBasisPoints' => 4999,
'lrSchedule' => '<string>',
'warmupSteps' => 123,
'dpoBeta' => '<string>'
],
'evaluationDatasetVersion' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://compute.prentis.ai/v1/accounts/{account}/supervisedFineTuningJobs"
payload := strings.NewReader("{\n \"baseModel\": \"<string>\",\n \"inputDatasetVersion\": \"<string>\",\n \"config\": {\n \"outputModelId\": \"<string>\",\n \"loraRank\": 123,\n \"learningRate\": \"<string>\",\n \"epochs\": 123,\n \"batchSize\": 123,\n \"maxContextLength\": 123,\n \"earlyStop\": true,\n \"evalAutoCarveoutBasisPoints\": 4999,\n \"lrSchedule\": \"<string>\",\n \"warmupSteps\": 123,\n \"dpoBeta\": \"<string>\"\n },\n \"evaluationDatasetVersion\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://compute.prentis.ai/v1/accounts/{account}/supervisedFineTuningJobs")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"baseModel\": \"<string>\",\n \"inputDatasetVersion\": \"<string>\",\n \"config\": {\n \"outputModelId\": \"<string>\",\n \"loraRank\": 123,\n \"learningRate\": \"<string>\",\n \"epochs\": 123,\n \"batchSize\": 123,\n \"maxContextLength\": 123,\n \"earlyStop\": true,\n \"evalAutoCarveoutBasisPoints\": 4999,\n \"lrSchedule\": \"<string>\",\n \"warmupSteps\": 123,\n \"dpoBeta\": \"<string>\"\n },\n \"evaluationDatasetVersion\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://compute.prentis.ai/v1/accounts/{account}/supervisedFineTuningJobs")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"baseModel\": \"<string>\",\n \"inputDatasetVersion\": \"<string>\",\n \"config\": {\n \"outputModelId\": \"<string>\",\n \"loraRank\": 123,\n \"learningRate\": \"<string>\",\n \"epochs\": 123,\n \"batchSize\": 123,\n \"maxContextLength\": 123,\n \"earlyStop\": true,\n \"evalAutoCarveoutBasisPoints\": 4999,\n \"lrSchedule\": \"<string>\",\n \"warmupSteps\": 123,\n \"dpoBeta\": \"<string>\"\n },\n \"evaluationDatasetVersion\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"name": "accounts/my-account/trainingJobs/01K7AX3V9Q2M8T4R6Y0B1C5D7E",
"jobId": "01K7AX3V9Q2M8T4R6Y0B1C5D7E",
"jobType": "JOB_TYPE_SFT",
"state": "JOB_STATE_QUEUED",
"baseModel": "accounts/maas/models/your-base-model",
"inputDatasetVersion": "accounts/my-account/datasets/support-chats/versions/1",
"config": {
"outputModelId": "support-lora-v1",
"loraRank": 16,
"learningRate": 0.0001,
"epochs": 2
},
"estimatedCost": {
"amount": "1.93",
"currency": "USD"
},
"execution": {
"thirdParty": false
},
"createTime": "2026-10-10T08:00:00Z"
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}{
"error": {
"message": "<string>",
"type": "invalid_request_error",
"code": "INVALID_REQUEST",
"param": "<string>"
}
}What happens after the 201
The job is accepted, not started. It movesQUEUED → RUNNING → SUCCEEDED, and on success
outputModel names the new model in your account, which you serve on a dedicated deployment
(see Fine-tuning). Poll
Get a supervised fine-tuning job, or watch it
in the console’s Training page — it is the same job.
Send an Idempotency-Key and a retried request returns the job the first one created
instead of starting a second, billable run.Authorizations
Authorization: Bearer <your API key> -- the same key as inference. A key reaches the account it belongs to and no other.
Headers
Your own key for making a create safe to retry (up to 255 characters). Sending the same key again returns the job the first request created instead of starting a second one.
255Path Parameters
Your account id -- the one in the console's address bar (my-account for this key). It must be the account the key belongs to.
64Body
The model to fine-tune, as a full resource name (accounts/maas/models/your-base-model). Only base models offered for training are accepted -- the console's Training page lists them.
The model to fine-tune, as a full resource name (accounts/maas/models/your-base-model). Only base models offered for training are accepted -- the console's Training page lists them.
The model to fine-tune, as a full resource name (accounts/maas/models/your-base-model). Only base models offered for training are accepted -- the console's Training page lists them.
The model to fine-tune, as a full resource name (accounts/maas/models/your-base-model). Only base models offered for training are accepted -- the console's Training page lists them.
256The training data: a dataset (accounts/{account}/datasets/{dataset}) to use its latest version, or one version (…/versions/{n}) to pin it. The dataset must be READY.
The preference data: a dataset to use its latest version, or one version to pin it. Each row needs a prompt and two answers -- see the dataset format guide. The dataset must be READY.
The training data: a dataset (accounts/{account}/datasets/{dataset}) to use its latest version, or one version (…/versions/{n}) to pin it. The dataset must be READY.
The training data: a dataset (accounts/{account}/datasets/{dataset}) to use its latest version, or one version (…/versions/{n}) to pin it. The dataset must be READY.
256How to train. outputModelId is required: the id of the model the job will create, which must not already exist in your account. The rest are optional -- loraRank, learningRate (a string, e.g. "1e-4"), epochs (1-100), batchSize, maxContextLength (longer examples are skipped and counted, never truncated), earlyStop, evalAutoCarveoutBasisPoints (0-9999, in hundredths of a percent), lrSchedule (constant, cosine or linear_decay), warmupSteps, and dpoBeta (preference jobs only; a string, default "0.1"). Fixed once the job is created.
How to train. outputModelId is required: the id of the model the job will create, which must not already exist in your account. The rest are optional -- loraRank, learningRate (a string, e.g. "1e-4"), epochs (1-100), batchSize, maxContextLength (longer examples are skipped and counted, never truncated), earlyStop, evalAutoCarveoutBasisPoints (0-9999, in hundredths of a percent), lrSchedule (constant, cosine or linear_decay), warmupSteps, and dpoBeta (preference jobs only; a string, default "0.1"). Fixed once the job is created.
How to train. outputModelId is required: the id of the model the job will create, which must not already exist in your account. The rest are optional -- loraRank, learningRate (a string, e.g. "1e-4"), epochs (1-100), batchSize, maxContextLength (longer examples are skipped and counted, never truncated), earlyStop, evalAutoCarveoutBasisPoints (0-9999, in hundredths of a percent), lrSchedule (constant, cosine or linear_decay), warmupSteps, and dpoBeta (preference jobs only; a string, default "0.1"). Fixed once the job is created.
How to train. outputModelId is required: the id of the model the job will create, which must not already exist in your account. The rest are optional -- loraRank, learningRate (a string, e.g. "1e-4"), epochs (1-100), batchSize, maxContextLength (longer examples are skipped and counted, never truncated), earlyStop, evalAutoCarveoutBasisPoints (0-9999, in hundredths of a percent), lrSchedule (constant, cosine or linear_decay), warmupSteps, and dpoBeta (preference jobs only; a string, default "0.1"). Fixed once the job is created.
Show child attributes
Show child attributes
Accepted so that the body of a create can be sent unchanged, but not used: the estimate covers the training data only.
Optional held-out data to measure evaluation loss on, in the same form as inputDatasetVersion. Leave it empty and set config.evalAutoCarveoutBasisPoints to hold out a share of the training data instead, or leave both empty for no evaluation.
Accepted so that the body of a create can be sent unchanged, but not used: the estimate covers the training data only.
Optional held-out data to measure evaluation loss on, in the same form as inputDatasetVersion. Leave it empty and set config.evalAutoCarveoutBasisPoints to hold out a share of the training data instead, or leave both empty for no evaluation.
256Response
Created.
JOB_TYPE_SFT, JOB_TYPE_DPO JOB_STATE_QUEUED, JOB_STATE_RUNNING, JOB_STATE_CHECKPOINTING, JOB_STATE_FINALIZING, JOB_STATE_SUCCEEDED, JOB_STATE_FAILED, JOB_STATE_CANCELLED, JOB_STATE_EXPIRED Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes