Skip to content

Environnement en développement actif : vous pouvez remarquer des changements ou des fonctionnalités incomplètes.

JAOT

Models

A model in JAOT is a model project: a first-class, versioned entity your organization owns. You create it blank, from a template, by importing a file, or by forking a marketplace model — then edit its draft, commit versions, and solve it.

The marketplace is a facet of the same entity: publishing a project attaches a public listing to it. There is no separate "catalog model" object to activate — using a marketplace model means forking it into your own project.

Two endpoint families cover this:

  • /api/v2/models/catalog — the public marketplace catalog (browse, detail, input schema). No authentication required.
  • /api/v2/projects — your organization's model projects (create, draft, versions, datasets, solve, publish). Authenticated.

GET /api/v2/models/catalog

Browse the public marketplace. This endpoint is public and does not require authentication.

Query Parameters

ParameterTypeDefaultDescription
categorystring--Filter by category (e.g. "logistics", "finance", "combinatorial")
searchstring--Search in model name, display name, and description
is_officialboolean--Only official (JAOT-published) models
min_ratingnumber--Minimum average rating (0--5)
sort_bystringpopularpopular, newest, or rating
pageinteger1Page number
page_sizeinteger20Items per page (max 100)

Response

FieldTypeDescription
itemsarrayList of published listings
items[].idstringModel ID — also the project ID you pass to from-marketplace
items[].namestringMachine-readable model name
items[].display_namestringHuman-readable display name
items[].descriptionstringModel description
items[].categorystringModel category
items[].tagsarraySearchable tags
items[].avg_ratingnumberAverage review rating
items[].is_officialbooleanPublished by JAOT
totalintegerTotal matching models
pageintegerCurrent page number
page_sizeintegerPage size

Examples

import httpx

API_URL = "https://jaot.io/api/v2"

# List all logistics models (no auth needed)
response = httpx.get(
    f"{API_URL}/models/catalog",
    params={"category": "logistics", "page_size": 10},
)
data = response.json()
for model in data["items"]:
    print(f"{model['display_name']}: {model['description']}")

GET /api/v2/models/catalog/{model_id}

Get the full detail of a published marketplace model (description, sections, rating, adoption counters). Public.

GET /api/v2/models/catalog/{model_id}/schema

Get the input schema and example input of a generator-backed (official) model — the fields you can customize when forking it with user_input. Public; published models only.


POST /api/v2/projects/from-marketplace/{model_id}

Fork a published marketplace model into a new model project of your organization. This is the API equivalent of the Use in studio button, and the only "use a marketplace model" path.

  • A generator-backed (official) model is materialized from your optional user_input — or from its example input if you send none.
  • A static community model copies the version its author pinned when publishing.

Either way you get a fresh, fully editable project — born with a v1 commit — that you can modify, version, and solve like any other.

Authentication: Requires API key or JWT token.

Request Body (optional)

FieldTypeRequiredDescription
user_inputobjectNoCustom input for a generator-backed model (see its /schema). Ignored for static models.

Examples

import httpx

response = httpx.post(
    "https://jaot.io/api/v2/projects/from-marketplace/b9c1d2e3-abcd-1234-5678-f0a1b2c3d4e5",
    headers={"Authorization": "Bearer ok_live_your_key_here"},
    json={"user_input": {"capacity": 80}},  # optional
)
project = response.json()
print(f"Forked into project: {project['id']}")

Errors

HTTP CodeDescription
404Model not found or not published
422The generator could not build a model from this input

POST /api/v2/projects

Create a new blank model project.

Authentication: Requires API key or JWT token.

Request Body

FieldTypeRequiredDescription
namestringYesProject name
descriptionstringNoProject description
workspace_idstringNoWorkspace to attach the project to

Returns 201 with the project (id prefixed mp_).

POST /api/v2/projects/from-template/{template_id}

Seed a project from one of the curated templates — the one-click "Use template" path. The template is materialized from its example input and the project is born with a v1 commit.


GET /api/v2/projects

List your organization's model projects (newest-updated first). The list is org-wide; pass mine=true to narrow it to your own.

Query Parameters

ParameterTypeDefaultDescription
statusstringactiveactive or archived
workspace_idstring--Filter by workspace
qstring--Search in name/description
minebooleanfalseOnly projects created by the current user
skipinteger0Pagination offset
limitinteger50Page size

GET /api/v2/projects/{project_id}

Get a single project: metadata, the mutable draft model, and the committed HEAD.

GET /api/v2/projects/{project_id}/stats

Live structural statistics for the draft: variable/constraint counts by type, density, detected problem class (LP, MIP, QP, ...), and the health score.

GET /api/v2/projects/{project_id}/executions

The project's solve runs (newest first). Supports status and limit (max 100). This is the same server-side history the workspace uses to re-attach a running solve.


PATCH /api/v2/projects/{project_id}

Update project metadata (name, description, status).

PUT /api/v2/projects/{project_id}/draft

Replace the draft model. This is the agent/programmatic authoring endpoint: send model_json (an OptimizationProblem), and optionally canvas_json or dsl_source (JModel).

Concurrency is optimistic: pass the draft lock version in the If-Match header (an integer); a stale value returns 409 Conflict so two writers never silently overwrite each other.

import httpx

headers = {"Authorization": "Bearer ok_live_your_key_here"}

project = httpx.get(
    "https://jaot.io/api/v2/projects/mp_abc123", headers=headers
).json()

response = httpx.put(
    "https://jaot.io/api/v2/projects/mp_abc123/draft",
    headers={**headers, "If-Match": str(project["draft_lock_version"])},
    json={"model_json": {
        "name": "production_plan",
        "variables": [{"name": "x", "type": "integer", "lower_bound": 0}],
        "constraints": [{"name": "cap", "expression": "x <= 10"}],
        "objective": {"sense": "maximize", "expression": "3*x"},
    }},
)
print(response.status_code)  # 200, or 409 if someone committed in between

DELETE /api/v2/projects/{project_id}

Archive the project (soft delete, reversible from the trash view). With ?permanent=true the project and its versions are hard-deleted — only allowed once the project is already archived (409 otherwise), so a permanent delete is always a deliberate two-step action.

An archived project is read-only. Every write to it answers 409 — the draft, commits, version restores, datasets, publish, rename and solve alike. Reads keep working, so the trash view can show what is in it. The one write that passes is the restore itself, PATCH {"status": "active"}; do that first and the rest opens up again.


POST /api/v2/projects/{project_id}/publish

Publish the project to the marketplace. Publishing attaches (or updates) the project's public listing and pins your latest committed version — the marketplace never serves your dirty draft. Requires at least one committed version (400 otherwise).

The request body carries the presentation: display_name, description, short_description, category, tags, and optional documentation sections. See Publishing Models for the full flow.


Versions, datasets, and solving

ConcernEndpointsDocs
Commit / list / diff / restore versionsPOST /projects/{id}/commit, GET /projects/{id}/versions, GET /projects/{id}/versions/{a}/diff/{b}, POST /projects/{id}/versions/{vid}/restoreVersions API
Datasets & scenariosGET/POST /projects/{id}/datasets, POST /projects/{id}/datasets/import, POST /projects/{id}/datasets/{did}/solveDatasets & Scenarios
Solve the draft or a versionPOST /projects/{id}/solve?version_id=&solver_name=&solution_filter=nonzeroSolve API
Execution resultsGET /api/v2/models/executions/all, GET /api/v2/models/executions/{id}Executions API

POST /projects/{id}/solve rides the same async pipeline as POST /solve: it waits briefly for the result and returns the classic OptimizationResult, or 202 with a task envelope (poll_url, ws_url) if the solve is still running. solution_filter=nonzero returns a compact solution that omits near-zero variables (variables_omitted reports the count); the stored execution always keeps the full solution.

Its options go in the query string, not the body. POST /solve takes solver_name inside the JSON problem; this endpoint takes it as a query parameter, because the model comes from the project and there is no request body at all. A body sent here is ignored without an error, so {"solver_name": "glpk"} silently gets you the default solver. Check solver_used in the response if you are not sure which one ran.