ULTINEO

AI runs

A run executes an app: you supply the app's input fields, the API queues the work, and the model fills in the answer. Runs are asynchronous: creating one returns at once with the status new, and you read the result back once it has finished.

Endpoints

MethodPathScope
POST/v1/ai/run/createreasoning:write
GET/v1/ai/run/listreasoning:read
GET/v1/ai/run/detail/<run_id>reasoning:read

<run_id> is a run identifier, prefixed runi_.

Note — The path segment is run but the scope is reasoning:read and reasoning:write, and the object is a reasoning. A key scoped run:* cannot call these endpoints. Use reasoning:*, or *.

The reasoning object

FieldTypeDescription
idstringThe run identifier, prefixed runi_.
objectstringAlways reasoning.
app_idstringThe app that was run.
modelobjectThe model the run used, or null.
dataobjectThe inputs the run was launched with, keyed by field name.
instructionobjectThe instructions that were sent, keyed by instruction identifier.
responseobjectThe model's answer, keyed by instruction identifier. Empty until the run finishes.
tokensobjectToken counts as input, output and total, or null when not measured.
statusstringSee the statuses below.
durationnumberHow long the run took, in seconds to two decimals, or null.
started_atintegerWhen the run was picked up, or null.
finished_atintegerWhen the run completed, or null.
created_atintegerWhen the run was queued, in Unix seconds.
updated_atintegerLast change, in Unix seconds.

The model object names the model and its exact version:

FieldTypeDescription
idstringThe model, prefixed modi_.
model_librarystringThe exact version the run used, prefixed mlyi_.
namestringThe version's name, such as qwen3:4b.
parameterintegerThe version's size, in billions of parameters.

Statuses

StatusMeaning
newQueued, not yet picked up.
pullBeing prepared.
processingRunning against the model.
finishedComplete. response is filled in.
failedEnded in an error.

Poll GET /v1/ai/run/detail/<run_id> until the status is finished or failed. Both are final; every other status changes on its own.

Launch a run

POST /v1/ai/run/create names an app and supplies its inputs.

ParameterTypeRequiredDescription
app_idstringYesThe app to run. Also accepted as app or app_ident.
dataobjectYesOne entry per input field, keyed by the field's name as returned by AI apps.
curl
curl -X POST https://api.ultineo.com/v1/ai/run/create \
  -H "Authorization: Bearer sk-project-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "app_id": "mapp_0ceacb46e5b3a99e91b6",
    "data": { "message": "My parcel has not arrived and the tracking has not moved for a week." }
  }'
application/json
{
  "code": 200,
  "title": "OK",
  "response": {
    "id": "runi_1b7f4e0c96a2d8531fca",
    "object": "reasoning",
    "data": { "message": "My parcel has not arrived and the tracking has not moved for a week." },
    "instruction": { "1": "Classify the message as low, normal or urgent." },
    "status": "new",
    "created_at": 1789210200
  }
}

Keep the returned id and poll it. The creation response carries no response field: nothing has run yet.

You can run your own apps, and any activated public app of another project whose identifier you know. An app that is not activated, has no usable model, or is private to another project is refused with 422 and the general message.

CodeMessageCause
402Your reasoning quota is exhausted. Please wait until it resets or upgrade your plan.The token allowance of the cycle is used up.
400Missing required field: app_id.No app was named.
404App not found.The app does not exist or was deleted.
422Missing required field: <name>.A required input field was absent or empty.
422The parameter '<key>' is not recognized.data holds a key that is not one of the app's fields.

The quota is checked before anything else, so a project with no allowance left cannot queue work. Read the remaining allowance from AI quota and token usage.

Tip — A field can also be keyed by its madi_ identifier or by its position, counting from 1. Keying by name is the clearest and is what the app's schema gives you.

Send a document or an image

A field whose type is document or image (see AI apps) does not take text: it takes a file already stored in your project's file library. Send the file's identifier, prefixed file_, or its delivery URL. The API does not upload files itself.

CodeMessageCause
422Field '<name>' expects an uploaded file: send its cdn file id (file_<20 hex>) or its delivery URL.The value was ordinary text.
422This project has no CDN scope, so no file can be attached to a reasoning.Your project has no file library.
422The file sent for '<name>' is not in this project's reasoning image library.The file belongs to another project, was deleted, or sits in another library. The message reads reasoning document library for a document field.
422The file sent for '<name>' cannot be read: accepted document formats are pdf, docx, xlsx, csv, txt.The document is in a format the model cannot read.
422The model resolved for this app cannot read images. Choose a model that accepts image input.The app has an image field but its model has no image input.

Launch several runs at once

Send a runs list instead of a single run to queue up to 100 runs in one call. Each item has the same shape as a single run.

curl
curl -X POST https://api.ultineo.com/v1/ai/run/create \
  -H "Authorization: Bearer sk-project-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "runs": [
      { "app_id": "mapp_0ceacb46e5b3a99e91b6", "data": { "message": "Where is my order?" } },
      { "app_id": "mapp_0ceacb46e5b3a99e91b6", "data": {} }
    ]
  }'
application/json
{
  "code": 200,
  "title": "OK",
  "response": {
    "object": "list",
    "data": [
      {
        "id": "runi_8d20c5e4a91f7b3a6c05",
        "object": "reasoning",
        "data": { "message": "Where is my order?" },
        "instruction": { "1": "Classify the message as low, normal or urgent." },
        "status": "new",
        "created_at": 1789210260
      }
    ],
    "created": 1,
    "failed": 1,
    "errors": [
      {
        "index": 1,
        "code": 422,
        "title": "Unprocessable entity",
        "response": "Missing required field: message."
      }
    ],
    "total": 2
  }
}

A refused item never blocks the others: each refusal is reported in errors with its position in your runs list, counting from 0. The call answers 200 as soon as one run was queued, and 422 only when every item was refused. A runs value that is not a list, is empty or holds more than 100 items is refused as a whole with 422.

Read one run

GET /v1/ai/run/detail/<run_id> returns the whole run, including the answer once it exists.

curl
curl https://api.ultineo.com/v1/ai/run/detail/runi_1b7f4e0c96a2d8531fca \
  -H "Authorization: Bearer sk-project-YOUR_KEY"
application/json
{
  "code": 200,
  "title": "OK",
  "response": {
    "id": "runi_1b7f4e0c96a2d8531fca",
    "object": "reasoning",
    "app_id": "mapp_0ceacb46e5b3a99e91b6",
    "model": {
      "id": "modi_c2b7e9a1d3f0a8645c8e",
      "model_library": "mlyi_c2a1e7d9b4f30685c3a3",
      "name": "qwen3:4b",
      "parameter": 4
    },
    "data": { "message": "My parcel has not arrived and the tracking has not moved for a week." },
    "instruction": {
      "miid_9a4c07e5b312df8641ca": "Classify the message as low, normal or urgent."
    },
    "response": {
      "miid_9a4c07e5b312df8641ca": "urgent"
    },
    "tokens": { "input": 412, "output": 3, "total": 415 },
    "status": "finished",
    "duration": 1.84,
    "started_at": 1789210202,
    "finished_at": 1789210204,
    "created_at": 1789210200,
    "updated_at": 1789210204
  }
}

instruction and response share their keys, so you can pair each answer with the instruction that produced it. A run of another project gives 404 and Reasoning not found.

List runs

GET /v1/ai/run/list returns your project's runs, newest first.

ParameterTypeDescription
app_idstringReturn only the runs of one app. Also accepted as app or app_ident.
limitintegerRows to return, 1 to 100. Defaults to 50.
offsetintegerRows to skip. Defaults to 0.
curl
curl "https://api.ultineo.com/v1/ai/run/list?app_id=mapp_0ceacb46e5b3a99e91b6&limit=2" \
  -H "Authorization: Bearer sk-project-YOUR_KEY"
application/json
{
  "code": 200,
  "title": "OK",
  "response": {
    "object": "list",
    "data": [
      {
        "id": "runi_1b7f4e0c96a2d8531fca",
        "object": "reasoning",
        "app_id": "mapp_0ceacb46e5b3a99e91b6",
        "status": "finished",
        "duration": 1.84,
        "started_at": 1789210202,
        "finished_at": 1789210204,
        "created_at": 1789210200,
        "updated_at": 1789210204,
        "tokens": { "input": 412, "output": 3, "total": 415 }
      }
    ],
    "total": 1
  }
}

The list omits data, instruction, response and model: read a run to get them. Listing never uses your quota, so you can poll a history freely.