Skip to content

Umgebung in aktiver Entwicklung: Es können Änderungen oder unvollständige Funktionen auftreten.

JAOT

Solve

The Solve endpoints are the core of the JAOT API. Send any optimization problem as JSON and receive the optimal solution. JAOT supports linear programming (LP), integer programming (IP), mixed-integer programming (MIP), and binary problems.

POST /api/v2/solve

Solve an optimization problem synchronously. The request blocks until the solver finishes or the time limit is reached.

Authentication: Requires API key or JWT token.

Request Body

FieldTypeRequiredDescription
namestringNoA descriptive name for the problem
objectiveobjectYesObjective function definition
objective.sensestringYes"minimize" or "maximize"
objective.expressionstringYesMathematical expression (e.g. "50*chairs + 40*tables")
variablesarrayYesList of decision variable definitions
variables[].namestringYesVariable name used in expressions
variables[].typestringYes"continuous", "integer", or "binary"
variables[].lower_boundnumberNoLower bound (default: 0)
variables[].upper_boundnumberNoUpper bound (default: infinity)
constraintsarrayYesList of constraint definitions
constraints[].namestringNoDescriptive constraint name
constraints[].expressionstringYesConstraint expression (e.g. "2*chairs + 3*tables <= 240")
optionsobjectNoSolver options
options.time_limit_secondsnumberNoMaximum solve time in seconds, 1–86400 (default: 300). Long solves should use the async endpoint.
options.gap_tolerancenumberNoOptimality gap tolerance (default: 0.0001)
solver_namestringNoSolver override: "scip", "highs", "cbc", "glpk", "hexaly", or "auto" (default: platform default). See what auto picks.
warm_startobjectNoWarm-start a solve from a previous execution ({"execution_id": "exe_..."})
templatestringNoTemplate name for template-based solving
inputobjectNoInput data for template-based solving

Response

FieldTypeDescription
statusstringSolve status (see table below)
objective_valuenumberOptimal objective function value
variablesarrayVariable names and their optimal values
solutionobjectVariable-value map for quick access
solve_time_secondsnumberWall-clock solve time
gapnumberOptimality gap (0.0 = proven optimal)
iterationsnumberSolver iterations performed
nodesnumberBranch-and-bound nodes explored

Status values:

StatusMeaning
optimalGlobally optimal solution found
feasibleSolution found, but optimality not proven
infeasibleNo feasible solution exists
unboundedObjective is unbounded
time_limitTime limit reached; best solution (if any) returned
errorSolver exception

Examples

import httpx

API_URL = "https://jaot.io/api/v2"
headers = {"Authorization": "Bearer ok_live_your_key_here"}

response = httpx.post(f"{API_URL}/solve", headers=headers, json={
    "name": "furniture_production",
    "variables": [
        {"name": "chairs", "type": "integer", "lower_bound": 0, "upper_bound": 100},
        {"name": "tables", "type": "integer", "lower_bound": 0, "upper_bound": 80}
    ],
    "objective": {"sense": "maximize", "expression": "50*chairs + 40*tables"},
    "constraints": [
        {"name": "assembly_hours", "expression": "2*chairs + 3*tables <= 240"},
        {"name": "finishing_hours", "expression": "4*chairs + 2*tables <= 200"}
    ],
    "options": {"time_limit_seconds": 30}
})

result = response.json()
print(f"Status: {result['status']}")
print(f"Revenue: ${result['objective_value']}")
print(f"Chairs: {result['solution']['chairs']}, Tables: {result['solution']['tables']}")

Response

{
  "status": "optimal",
  "objective_value": 3500.0,
  "variables": [
    {"name": "chairs", "value": 30, "type": "integer"},
    {"name": "tables", "value": 60, "type": "integer"}
  ],
  "solution": {"chairs": 30, "tables": 60},
  "solve_time_seconds": 0.045,
  "gap": 0.0,
  "iterations": 12,
  "nodes": 1
}

Errors

HTTP CodeErrorDescription
400bad_requestInvalid problem definition (undefined variable, invalid bounds)
401unauthorizedMissing or invalid API key
429rate_limitedToo many requests

POST /api/v2/solve/async

Start an asynchronous solve. Returns immediately with a task ID. Use polling or WebSocket to retrieve the result when it completes.

Authentication: Requires API key or JWT token.

Request Body

Same as POST /api/v2/solve.

Response

FieldTypeDescription
task_idstringUnique task identifier for polling
statusstringAlways "pending" on creation
messagestringConfirmation message
ws_urlstringWebSocket URL for real-time updates
poll_urlstringPolling URL for status checks

Examples

import httpx

API_URL = "https://jaot.io/api/v2"
headers = {"Authorization": "Bearer ok_live_your_key_here"}

# For large problems, use async mode
response = httpx.post(f"{API_URL}/solve/async", headers=headers, json={
    "name": "warehouse_routing",
    "variables": [
        {"name": f"route_{i}", "type": "binary"}
        for i in range(500)
    ],
    "objective": {
        "sense": "minimize",
        "expression": " + ".join(f"{cost}*route_{i}" for i, cost in enumerate(costs))
    },
    "constraints": warehouse_constraints,
    "options": {"time_limit_seconds": 120}
})

task = response.json()
print(f"Task ID: {task['task_id']}, Status: {task['status']}")

Response

{
  "task_id": "b7a3c9e2-1234-5678-abcd-ef0123456789",
  "status": "pending",
  "message": "Task queued for processing",
  "ws_url": "/api/v2/ws/executions/b7a3c9e2-1234-5678-abcd-ef0123456789",
  "poll_url": "/api/v2/solve/async/b7a3c9e2-1234-5678-abcd-ef0123456789"
}

Tip: Use async mode for problems with more than 1,000 variables or long time limits. Async solves run on dedicated workers and support real-time progress updates via WebSocket.

Polling for Results

Use GET /api/v2/solve/async/{task_id} to check the status of an async solve.

Pending:

{"task_id": "b7a3c9e2-...", "status": "pending", "message": "Task is waiting to be processed"}

Running:

{"task_id": "b7a3c9e2-...", "status": "running", "progress": 0.45, "objective_value": 1234.56}

Completed:

{"task_id": "b7a3c9e2-...", "status": "completed", "result": {"status": "optimal", "objective_value": 3500.0, "..."}}

To retrieve async execution results, see the Executions page.


POST /api/v2/solve/validate

Validate a problem without solving it. Useful for checking problem definitions before committing.

Authentication: Requires API key or JWT token.

Request Body

Same as POST /api/v2/solve.

Response (Valid Problem)

{
  "valid": true,
  "num_variables": 5,
  "num_constraints": 8,
  "variable_types": {
    "continuous": 2,
    "integer": 3,
    "binary": 0
  }
}

Response (Invalid Problem)

{
  "valid": false,
  "errors": ["Objective references undefined variables: {'unknown_var'}"]
}

POST /api/v2/solve/async/{task_id}/cancel

Cancel a running async task.

Authentication: Requires API key or JWT token.

Response

{"task_id": "b7a3c9e2-...", "cancelled": true, "message": "Task cancellation requested"}

If the task already completed:

{"task_id": "b7a3c9e2-...", "cancelled": false, "message": "Task already completed, cannot cancel"}

GET /api/v2/solvers/available

List the solvers this server can run, and what each one can deliver. Use it to pick a value for solver_name — and to know what to expect back from that choice.

Authentication: Requires API key or JWT token.

Response

{
  "solvers": [
    {
      "name": "scip",
      "available": true,
      "description": "Academic MIP solver",
      "version": "10.0",
      "comparable": true,
      "capabilities": {
        "sensitivity": true,
        "warm_start": true,
        "quadratic": true,
        "progress": true
      }
    },
    {
      "name": "hexaly",
      "available": false,
      "reason": "maintenance",
      "retry_after": null,
      "description": "Commercial solver for quadratic / non-convex problems",
      "comparable": false,
      "not_comparable_reason": "not_available",
      "capabilities": {
        "sensitivity": false,
        "warm_start": true,
        "quadratic": true,
        "progress": false
      }
    }
  ]
}
CapabilityWhat it means for your response
sensitivityThe solve returns sensitivity (shadow prices, reduced costs). When false, that field is absent — it is not an error.
warm_startA re-solve can be seeded from a previous solution. Affects speed, not results.
quadraticModels containing quadratic terms can run. A solver without it rejects them explicitly rather than solving a linear relaxation.
progressThe solve streams per-incumbent progress events. When false, you get the result only when it finishes.

Notes:

  • available: false means the solver is registered but temporarily unusable (reason explains why). Its capabilities still describe the solver, so past executions that used it can still be interpreted.
  • capabilities may be absent on an entry. That means "not known", not "supports nothing" — treat it as no information rather than as a denial.
  • version is the solver's own version string. Absent when the solver will not say. Record it next to any timing you keep: seconds measured against CBC 2.10.12 explain nothing about 2.11.
  • comparable says whether the solver can take part in a solver comparison on this server. It is a property of the server, not of any one model: Hexaly needs its own image and licence, and the comparison worker runs the base image so that one machine times every column. not_comparable_reason rides only on a false. Whether a solver can express a particular model is decided per comparison, against that problem's class.
  • Multi-objective is not listed: it is available for every solver. Solvers without native support get it through scalarization, so a per-solver flag would be misleading.
  • solver_name: "auto" is always accepted and is not listed here; the server picks the effective solver per problem and reports it back as solver_used, with auto_route_reason saying why.

What auto picks

The model isauto runsauto_route_reason
Linear, all continuous (LP)HiGHSlp_routed_to_highs
Anything with quadratic termsHexalyquadratic_routed_to_hexaly
Quadratic, Hexaly worker downSCIPhexaly_unavailable_fallback (plus a warning)
Anything else (MIP, mixed)SCIPmilp_routed_to_scip

CBC and GLPK are substitutes, not candidates. auto never picks them while the solver its rule prefers is installed. It reaches them only on a server built without that solver, and then it says so: auto_route_reason comes back preferred_solver_not_installed and the response carries a warning naming the solver that actually ran.

They sit behind SCIP and HiGHS for two reasons that have nothing to do with speed. Neither computes shadow prices or reduced costs, so a caller who chose no solver would silently lose the sensitivity analysis. And GLPK is single-threaded: on a 60-lot burn-in plan (1,342 binaries) HiGHS and CBC finished in 1.5 seconds, SCIP in 12, and GLPK ran a full 60-second limit without finding any feasible answer.

A substitute is never given a model it cannot express, so a quadratic never reaches CBC or GLPK whatever else is missing.

To pick CBC or GLPK, name one — and use the solver comparer to find out which one is right for your model, with numbers, instead of guessing.