Prompt for your coding agent
- Inline training data. Pass
training_datain the body. No Files API, notraining_file. - Fully managed. No hyperparameters. One base model,
morph-reflex-v1.
| Base model | morph-reflex-v1 (default, optional on create) |
| Minimums | 2 distinct labels, 5 examples per label |
| Concurrency | 3 jobs run at once per key; the rest queue |
Quick Start
Four steps: get a key, create a job, wait for it, classify text.training_data is a Morph extension. The OpenAI Python SDK rejects unknown arguments, so pass it through extra_body=.Get an API key
Grab one from the dashboard.
Create a training job
Send labeled examples. No data? Use
generate or label_data instead.Create a Job
training_data, generate, or label_data (see Input modes).
| Field | Type | Required | Description |
|---|---|---|---|
model | string | No | morph-reflex-v1 (default and only value). |
base_model | string | No | Start from an existing classifier’s weights instead of from scratch. A model you trained or a default Reflex name. Omit for a cold start. See Continual training. |
suffix | string | No | Names the served model. Becomes fine_tuned_model on success. |
labels | array | * | The classes. 2+ required for generate and label_data; inferred from training_data if omitted. |
webhook_url | string | No | An https URL to receive signed webhooks when the job reaches succeeded, failed, or cancelled. |
Input modes
Pick one. The training set, however it is produced, must have 2+ labels and 5+ examples per label, else the job fails.generate and label_data synthesize or label data through the OpenAI Batch API, so the job spends a few minutes on data before training. status stays running the whole time. Poll as usual.training_data: labeled rows you supply.
| Field | Type | Required | Description |
|---|---|---|---|
training_data | array | Yes | { "text": string, "label": string } rows. |
generate: no data; synthesize it from a description.
| Field | Type | Required | Description |
|---|---|---|---|
generate.description | string | Yes | What the classifier is for. |
generate.examples_per_label | integer | No | Examples to synthesize per label. Default 500, max 1000. |
labels | array | Yes | The classes to generate for. 2+. |
label_data: your unlabeled text, sorted into your classes.
| Field | Type | Required | Description |
|---|---|---|---|
label_data.texts | array | Yes | 10 to 20,000 unlabeled strings. |
label_data.description | string | No | Context for more accurate labeling. |
labels | array | Yes | The classes to sort into. 2+. |
Continual training
Passbase_model to start a job from an existing classifier’s weights instead of from scratch. The new model inherits what the checkpoint already learned, so it converges on fewer examples. Use it to grow a Reflex as you collect data, retrain a drifting classifier on fresh labels, or specialize one of Morph’s default Reflexes to your domain. Omit base_model for a cold start.
base_model takes two kinds of name, resolved owned-first:
- A model you trained. Its
suffix(thefine_tuned_modelname) or job id. The latestsucceededversion is pinned when the job is created, so retraining the source afterward never moves an in-flight job. - A default Reflex.
guardrail,jailbreak,difficulty,domain,ambiguity,stuck-in-a-loop,leaked-thinking, orincomplete-thought. Starts from Morph’s pre-trained classifier for that task (see the overview). An owned model of the same name shadows the default.
model, which selects the architecture (morph-reflex-v1). base_model selects the weights to warm-start from. The new job is independent: it gets its own id, trains on the data you send now, and never changes the model it started from.
base_model you sent. Everything else (poll, predict, manage) is unchanged.
The starting checkpoint must be on the current training stack. A model trained before the aLoRA migration returns
base_model_incompatible; retrain it once from scratch and it becomes a valid base. A model that has not finished training yet returns base_model_not_ready.Retrieve a Job
status is succeeded, failed, or cancelled.
List Jobs
| Query param | Type | Description |
|---|---|---|
limit | integer | Jobs per page. Default 20, max 100. |
after | string | Job id cursor. Returns jobs created before it. |
Cancel a Job
status becomes cancelled.
Training Events
running, succeeded, failed) interleaved with the loss curve, oldest first. type is metrics for a loss point and message for a lifecycle line. Add ?stream=true for a live Server-Sent Events stream.
Webhooks
Passwebhook_url when you create a job to get a signed POST the moment it finishes, instead of polling. Morph delivers a webhook for each terminal state:
Event type | Fired when |
|---|---|
fine_tuning.job.succeeded | The model trained and is ready to use. |
fine_tuning.job.failed | Training failed. Retrieve the job for the error. |
fine_tuning.job.cancelled | The job was cancelled. |
Verifying signatures
Deliveries are signed with the Standard Webhooks scheme — the same one OpenAI and Stripe use — so off-the-shelf verifiers work. Three headers travel with each request:| Header | Description |
|---|---|
webhook-id | Unique delivery id. Also your idempotency key — dedupe on it. |
webhook-timestamp | Unix seconds at delivery. Reject if more than 5 minutes from now. |
webhook-signature | v1,<base64 HMAC-SHA256> over {webhook-id}.{webhook-timestamp}.{body}, keyed by your signing secret. |
2xx quickly and do work asynchronously; failed deliveries are retried with backoff, and duplicates are possible, so make your handler idempotent on webhook-id.
Predict
POST. The model must be ready, else 409 (model_not_ready).
| Field | Type | Required | Description |
|---|---|---|---|
model | string | Yes | A fine_tuned_model name or job id. |
text | string | Yes | The text to classify. |
classes is one entry per label. mode is single_label (softmax, scores sum to 1, one selected) or multi_label (independent sigmoid scores, any number selected). The winning label is the selected class with the highest score.
Classify against multiple models
predict takes one model per request. To run several of your classifiers over the same text, make one call per model and fire them in parallel.
Delete a Job
Delete a Model
fine_tuned_model value or job id). Same effect as deleting the job; OpenAI Models-API parity.
Reference
Job object
Job object
| Field | Type | Description |
|---|---|---|
id | string | Job id, prefixed ftjob-. |
object | string | Always fine_tuning.job. |
model | string | Always morph-reflex-v1. |
base_model | string / null | The starting checkpoint supplied at creation, or null for a cold start. |
created_at | integer | Unix timestamp (seconds) at creation. |
finished_at | integer / null | Unix timestamp at terminal state, else null. |
fine_tuned_model | string / null | Served model name once succeeded. The suffix, or the job id if none. |
status | string | queued, running, succeeded, failed, or cancelled. |
labels | array | The label set used for training. |
trained_examples | integer | Number of training examples. |
result | object / null | { "accuracy", "f1_score" } when succeeded, else null. Each value may be null. |
error | object / null | { "code", "message", "param" } when failed, else null. |
suffix | string / null | The suffix supplied at creation, or null. |
Event object
Event object
| Field | Type | Description |
|---|---|---|
id | string | Event id, prefixed ftevent-. |
object | string | Always fine_tuning.job.event. |
created_at | integer | Unix timestamp (seconds). |
level | string | info, warn, or error. |
message | string | Human-readable message. |
type | string | metrics for per-step loss, message for the terminal event. |
data | object | { "epoch", "step", "train_loss" }. |
Errors
Errors
OpenAI-shaped:
{ "error": { "message", "type", "param", "code" } }.| Status | type | When |
|---|---|---|
401 | authentication_error | Invalid or missing API key. |
400 | invalid_request_error | Validation failed. param names the offending field. |
400 | invalid_request_error | base_model could not be used. code is base_model_not_found (no such owned model or default Reflex) or base_model_incompatible (trained on the legacy stack; retrain first). |
404 | invalid_request_error | Not found. code is job_not_found or model_not_found. |
409 | invalid_request_error | Not ready. code is model_not_ready, or base_model_not_ready when the starting checkpoint has no trained version yet. |