Skip to main content
Send labeled examples, get back a text classifier. Create a job, poll until it finishes, then classify text against it. A small Reflex trains in about 30 seconds. See the Reflexes overview for what a Reflex is. Jobs use the OpenAI fine-tuning API, so the official SDKs work unchanged, with two differences:
  • Inline training data. Pass training_data in the body. No Files API, no training_file.
  • Fully managed. No hyperparameters. Train from scratch, or warm-start from any custom or default reflex.

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=.
1

Get an API key

Grab one from the dashboard.
2

Create a training job

Send labeled examples. No data? Use generate or label_data instead.
3

Wait for training

Poll until status is succeeded. A small Reflex takes about 30 seconds.
4

Classify text

Predict against fine_tuned_model (your suffix, or the job id if you gave none).

Create a Job

Starts a training job. Provide exactly one input: training_data, generate, or label_data (see Input modes).

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 reads validating_files during this phase, then moves to running. Poll as usual.
1. training_data: labeled rows you supply.
2. generate: no data; synthesize it from a description.
3. label_data: your unlabeled text, sorted into your classes.

Continual training

Set model to an existing classifier’s name to start a job from its 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 model for a cold start. For a warm start, model accepts two kinds of name, resolved owned-first:
  • A model you trained. Its suffix (the fine_tuned_model name) or job id. The latest succeeded version 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, incomplete-thought, user-frustrated, user-joy, or health-emergency. Starts from Morph’s pre-trained classifier for that task (see the overview). An owned model of the same name shadows the default.
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.
The job’s model reflects what it trained from — the warm-start reflex, or the from-scratch base when you omit model. 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 model_incompatible; retrain it once from scratch and it becomes a valid base. A model that has not finished training yet returns model_not_ready.

Retrieve a Job

Poll until status is succeeded, failed, or cancelled.

List Jobs

Returns the key’s jobs, newest first.

Cancel a Job

Stops a queued or running job. status becomes cancelled.

Training Events

Returns the lifecycle 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

Pass webhook_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: The body is a thin event envelope — it carries only the job id, mirroring OpenAI. Fetch the job to read the result:

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:
Use the raw request body — re-serializing the JSON changes the bytes and breaks the signature. Acknowledge with a 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

Classifies text against a trained model. A Morph endpoint, not an OpenAI method, so call it with a plain POST. The model must be ready, else 409 (model_not_ready).
See The response for the full field reference and how single_label vs multi_label scoring decides the winning class. One billing note specific to this endpoint: prefill_tokens is the tokenized input length, charged once per request regardless of how many models run. The SDK flattens this for you: predict returns the winning label/confidence and selected class alongside the full classes array (allScores), plus mode, completionId, and inferenceTimeMs. Pass completionId to tag the call (sent as the X-Completion-Id header) so the prediction is correlatable in your logs.

Classify against multiple models

Run several classifiers over the same text in one request — morph.reflex.predictMany({ models, text }) in the SDK, or a models array on the raw endpoint. They share a single prefill, so the input is tokenized once: cheaper and faster than a call per model. You get back { predictions, inferenceTimeMs, prefillTokens }, one entry per model. An entry that fails at inference carries an error instead of a classification; an unknown model name still fails the whole request with model_not_found.
To classify many different texts in one job, use the batch API instead.

Delete a Job

Deletes the job and its trained model.

Delete a Model

Deletes a model by name (the fine_tuned_model value or job id). Same effect as deleting the job; OpenAI Models-API parity.

Reference

OpenAI-shaped: { "error": { "message", "type", "param", "code" } }.

Reflexes overview

What a Reflex is, the default classifiers, and realtime /predict.

Batch classification

Classify up to 300 rows inline, or 10,000 offline. Sync and async batch APIs.