---
title: "Deploy a model to a project"
method: POST
path: "/projects/{project_id}/deployments"
tags: ["deployments"]
---

# Deploy a model to a project

`POST /projects/{project_id}/deployments`

Activate a model as the project's active model and record the swap.

Exactly one of ``training_job_id`` (a fine-tuned checkpoint) or
``base_model`` (a stock HuggingFace base model) must be supplied in the
request body; this is enforced by the ``DeploymentCreate`` schema.

Args:
    project_id: ID of the project to update.
    request: Deployment target (training job or base model) and optional
        reason.
    auth: Authenticated user.

Returns:
    DeploymentResponse with the new history record.

Raises:
    HTTPException: 400 if the supplied base model is not in the catalog;
        403 if the user does not have access to the project; 404 if the
        project or training job is not found; 409 if the training job
        exists but is not deployable, or a seed-supported job has no
        scored second-seed sibling.

## Path parameters

- `project_id` string, uuid, required

## Request body

- DeploymentCreate — Request body for deploying a model to a project. Exactly one of ``training_job_id`` or ``base_model`` must be supplied. Attributes: training_job_id: UUID of a completed training job to activate. base_model: HuggingFace base model ID to activate (e.g. ``fastino/gliner2-base-v1``). Used when rolling back from a fine-tuned checkpoint to the original base model. reason: Optional human-readable reason for the swap. project_id: Project ID -- used only by the deprecated /felix/deployments endpoint. For new clients, supply project_id in the URL path instead. selection_evaluation_run_id: Evaluation Suite run this promotion was decided on. recipe_fingerprint: Digest of the promoted candidate's recipe. The two evidence fields are the caller's declared basis for the swap, not a gate: they are recorded, never checked, and omitting them cannot stop a promotion. Only the agent knows which of several evaluations it weighed, so the alternative -- Brain guessing -- would record a fact nobody asserted. Provenance (which Experiment, which plan revision) is deliberately absent here: the service resolves that from the caller's own run key so it cannot be misstated. See ``services.deployments.promotion_evidence``.
  - `training_job_id` string, nullable — UUID of the training job to deploy
  - `base_model` string, nullable — HuggingFace base model ID to deploy (e.g. 'fastino/gliner2-base-v1'). Mutually exclusive with training_job_id.
  - `reason` string, nullable — Optional reason for this deployment
  - `project_id` string, nullable — [Deprecated] Project ID. Provide in the URL path (/projects/{project_id}/deployments) instead.
  - `selection_evaluation_run_id` string, nullable — UUID of the Evaluation Suite run this promotion was decided on. Recorded as evidence; ignored unless it names a run on this project.
  - `recipe_fingerprint` string, nullable — Digest of the promoted candidate's training recipe. Recorded as evidence, never checked.

## Response `200`

Successful Response

- DeploymentResponse — A single deployment history record. A deployment targets exactly one of ``training_job_id`` or ``base_model``: the trained adapter that was activated, or the base catalog model that was activated when no adapter is in use. Attributes: id: Unique deployment record ID. project_id: The project whose active model was changed. training_job_id: Training job that was deployed (training-job shape). base_model: HuggingFace base model ID that was deployed (base-model shape). deployed_by: User ID who triggered the deployment. reason: Optional reason for the swap. deployed_at: When the swap occurred. experiment_id: Experiment whose agent promoted, when one did. finetune_plan_id: Plan revision that authorised the promotion. selection_evaluation_run_id: Evaluation Suite run the promotion was decided on. recipe_fingerprint: Digest of the promoted candidate's recipe. The four evidence fields are null for a promotion nobody recorded evidence for -- a human pressing promote, or the post-training auto-deploy. They are returned so the basis for a swap is readable after the fact rather than only at the moment it happens.
  - `id` string, required
  - `project_id` string, required
  - `training_job_id` string, nullable
  - `base_model` string, nullable
  - `deployed_by` string, required
  - `reason` string, nullable, required
  - `deployed_at` string, date-time, required
  - `experiment_id` string, nullable
  - `finetune_plan_id` string, nullable
  - `selection_evaluation_run_id` string, nullable
  - `recipe_fingerprint` string, nullable

## Other responses

- `422` — Validation Error

## Changes

- **2026-09-24** `1cffaad2a921` — 1 warning, 7 info
  - removed the optional property `detail` from the response with the `422` status
  - added the new optional request property `recipe_fingerprint`
  - added the new optional request property `selection_evaluation_run_id`
  - added the optional property `experiment_id` to the response with the `200` status
  - …4 more

[Change history](https://skmtc.dev/pioneer/apis/brain-api/changes/projects/:project_id/deployments/post.md)

---

[API](https://skmtc.dev/pioneer/apis/brain-api.md) · [All operations](https://skmtc.dev/pioneer/apis/brain-api/llms.txt) · [OpenAPI document](https://skmtc.dev/pioneer/apis/brain-api/revisions/1cffaad2a921?raw)
