ULTINEO

AI apps

An app is a reusable AI application: a model, a set of input fields your caller fills in, and the instructions sent to the model. You create apps through this resource, then run them through AI runs. An app is private to your project until you publish it.

Endpoints

MethodPathScope
POST/v1/ai/app/createapp:write
GET/v1/ai/app/listapp:read
GET/v1/ai/app/detail/<app_id>app:read
PATCH/v1/ai/app/detail/<app_id>app:write

<app_id> is an app identifier, prefixed mapp_. Editing acts on the detail path; there is no separate update path. The API does not delete apps.

The app object

FieldTypeDescription
idstringThe app identifier, prefixed mapp_.
objectstringAlways app.
namestringThe app's name.
descriptionstringWhat the app does.
catalog_categorystringThe trade the app is filed under, as its identifier (prefixed acci_), or an empty string.
typestringprivate or public.
statusstringdraft or activated. Only an activated app can be run.
logostringThe identifier of the app's logo file, or an empty string.
modelstringThe model the app runs on, prefixed modi_.
model_librarystringThe exact model version resolved for this app, prefixed mlyi_.
dataarrayThe input fields. One object per field the caller fills in.
instructionarrayThe instructions sent to the model. null when you do not own the app.
runsobjectRun counters, as private (runs by the owner's project) and public (runs by other projects).
tokensobjectTokens your project has spent on the app, as input, output and total, or null.
created_atintegerCreation time, in Unix seconds.
updated_atintegerLast change, in Unix seconds.

Each entry of data describes one input field:

FieldTypeDescription
idstringThe field identifier, prefixed madi_.
namestringThe field's name. This is the key you send when you launch a run.
typestringThe field's type, as its identifier (prefixed mdti_). See Field types.
placeholderstringThe hint shown to whoever fills the field in.
requiredbooleanWhether a run must supply a value.

tokens covers the app's whole life, not the current billing cycle, and counts only your own project's runs of it. Runs that ended in an error are left out. See AI quota and token usage for the figures of the current cycle.

Create an app

POST /v1/ai/app/create needs a name, a description, at least one input field and at least one instruction.

ParameterTypeRequiredDescription
namestringYesThe app's name.
descriptionstringYesWhat the app does.
dataarrayYesThe input fields. Each object needs a non-empty type and name, and may set placeholder and required (defaults to true). See Field types.
instructionarrayYesEach object needs a non-empty type and an instruction (also accepted as prompt). See Instruction types.
categorystringNoThe task the app performs, used to pick a model. See Task categories. Also accepted as task_type or model_task_type.
modelstringNoA model, by identifier or by name (such as qwen3). The best version your plan allows is used.
model_librarystringNoOne exact model version, by identifier.
catalog_categorystringNoThe trade to file the app under, by identifier or by English name (such as Customer Support & Call Centres).
statusstringNodraft or activated. Defaults to activated.
typestringNoprivate or public. Defaults to private.

Send category, model or model_library so the API can pick a model; model_library wins over model, which wins over category.

curl
curl -X POST https://api.ultineo.com/v1/ai/app/create \
  -H "Authorization: Bearer sk-project-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Support triage",
    "description": "Sorts an inbound support message by urgency.",
    "category": "classify",
    "data": [
      { "type": "text area", "name": "message", "placeholder": "Paste the message", "required": true }
    ],
    "instruction": [
      { "type": "text", "instruction": "Classify the message as low, normal or urgent." }
    ]
  }'
application/json
{
  "code": 200,
  "title": "OK",
  "response": {
    "id": "mapp_0ceacb46e5b3a99e91b6",
    "object": "app",
    "name": "Support triage",
    "description": "Sorts an inbound support message by urgency.",
    "category": "classify",
    "catalog_category": "",
    "model": "modi_c2b7e9a1d3f0a8645c8e",
    "model_library": "mlyi_c2a1e7d9b4f30685c3a3",
    "data": {
      "madi_5d8b21c74e0af96b3d15": {
        "type": "text area",
        "value": "message",
        "placeholder": "Paste the message",
        "required": true
      }
    },
    "instruction": {
      "miid_9a4c07e5b312df8641ca": {
        "type": "text",
        "instruction": "Classify the message as low, normal or urgent."
      }
    },
    "status": "activated",
    "created_at": 1789206600
  }
}

Note — Creation answers 200, not 201, and its data and instruction are objects keyed by the new identifiers rather than the arrays the read endpoints return. Read the app back with GET /v1/ai/app/detail/<app_id> if you need the standard shape.

CodeMessageCause
400Missing required field: name.No name was sent. The same message names description.
422Each data object must contain non-empty 'type' and 'name' properties.data is missing, or one of its fields has no type or name.
422Each instruction object must contain non-empty 'type' and 'instruction' properties.instruction is missing, or one of its entries is incomplete.
422At least one data field must be required.Every field was sent with "required": false.
404The selected data type does not exist.A field type is not one of the field types.
404The selected instruction type does not exist.An instruction type is not one of the instruction types.
404The selected model does not exist.model names no model.
422No available model was found for the selected category or current billing plan.No model fits the category within your plan, or nothing was sent to pick one.
422The selected catalog_category does not exist.catalog_category names no trade.
402The current billing plan does not allow custom app creation for this project.Your plan includes no apps.
402The current billing plan app limit has been reached for this project.Your project already holds as many apps as your plan allows.

Field types

The type you send in data is one of the names below. Its identifier is also accepted, and it is the identifier the read endpoints return.

typeIdentifierWhat the field collects
inputmdti_c2a39a7ad82bc017dccdOne line of text.
text areamdti_fdd932b23e1fa82aa942A block of text.
numbermdti_0bf56e96d417b823b7e8A number.
emailmdti_52f84207d0f8a95ed43dAn email address.
documentmdti_5017cf8657e1df4df809A PDF, DOCX, XLSX, CSV or TXT file. The model reads its text.
imagemdti_1777f1beedfbd5cde030An image. Only a model that reads images can use it.

document and image fields do not take typed text: a run supplies a file already stored in your project's file library. See AI runs.

Instruction types

The type of each instruction says what kind of answer you expect:

typeIdentifier
textaiti_d0c8a5e7b2f3a6c4e9d1
nombreaiti_d7a3c1f9e2b4d8a6c0f5
elementaiti_4a8e1d6b9c3f0a52c7e5
notationaiti_8d6c3b1a9e0f7c24a5e8
percentaiti_7f0a3b5d9c2e1a648c7e
imageaiti_8c4e9d2a0b6f1c73a5e1

Task categories

category takes one of these names (or its identifier): general, summarizing, writing, analyze, solving, decision, excracting, coding, translation, classify, chatting, scoring, embedding. Send the names exactly as written here.

List your apps

GET /v1/ai/app/list returns your project's own apps, newest first, excluding deleted ones. It accepts limit and offset.

curl
curl "https://api.ultineo.com/v1/ai/app/list?limit=2" \
  -H "Authorization: Bearer sk-project-YOUR_KEY"
application/json
{
  "code": 200,
  "title": "OK",
  "response": {
    "object": "list",
    "data": [
      {
        "id": "mapp_0ceacb46e5b3a99e91b6",
        "object": "app",
        "name": "Support triage",
        "description": "Sorts an inbound support message by urgency.",
        "catalog_category": "",
        "type": "private",
        "status": "activated",
        "model": "modi_c2b7e9a1d3f0a8645c8e",
        "model_library": "mlyi_c2a1e7d9b4f30685c3a3",
        "runs": { "private": 12, "public": 0 },
        "tokens": { "input": 8420, "output": 3115, "total": 11535 },
        "created_at": 1789206600,
        "updated_at": 1789206600
      }
    ],
    "total": 1
  }
}

The list omits data, instruction and logo: read one app to get them.

Read one app

GET /v1/ai/app/detail/<app_id> returns the full object, including the input fields you need to launch a run.

curl
curl https://api.ultineo.com/v1/ai/app/detail/mapp_0ceacb46e5b3a99e91b6 \
  -H "Authorization: Bearer sk-project-YOUR_KEY"
application/json
{
  "code": 200,
  "title": "OK",
  "response": {
    "id": "mapp_0ceacb46e5b3a99e91b6",
    "object": "app",
    "name": "Support triage",
    "description": "Sorts an inbound support message by urgency.",
    "catalog_category": "",
    "type": "private",
    "status": "activated",
    "logo": "",
    "model": "modi_c2b7e9a1d3f0a8645c8e",
    "model_library": "mlyi_c2a1e7d9b4f30685c3a3",
    "data": [
      {
        "id": "madi_5d8b21c74e0af96b3d15",
        "name": "message",
        "type": "mdti_fdd932b23e1fa82aa942",
        "placeholder": "Paste the message",
        "required": true
      }
    ],
    "instruction": [
      {
        "id": "miid_9a4c07e5b312df8641ca",
        "type": "aiti_d0c8a5e7b2f3a6c4e9d1",
        "instruction": "Classify the message as low, normal or urgent."
      }
    ],
    "runs": { "private": 12, "public": 0 },
    "tokens": { "input": 8420, "output": 3115, "total": 11535 },
    "created_at": 1789206600,
    "updated_at": 1789206600
  }
}

You can read your own apps in any status, and any activated public app of another project. Reading another project's public app hides what belongs to its owner: instruction comes back as null and runs.private as 0, and tokens reports what your own project spent on it. An app that does not exist or was deleted gives 404 and App not found.; another project's private app gives 422 and the general message.

Edit an app

PATCH /v1/ai/app/detail/<app_id> changes one or more of these fields of one of your own apps. Send at least one.

ParameterTypeDescription
namestringA new name. An empty value is refused.
descriptionstringA new description. An empty value is refused.
catalog_categorystringA trade, by identifier or English name. An empty string removes it.
modelstringAnother model, by identifier or name. The best version your plan allows is used.
model_librarystringOne exact model version, by identifier.
logostringA file of your project's logo library, by identifier. An empty string removes the logo.
typestringpublic to publish the app, private to withdraw it.
curl
curl -X PATCH https://api.ultineo.com/v1/ai/app/detail/mapp_0ceacb46e5b3a99e91b6 \
  -H "Authorization: Bearer sk-project-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Support triage (EN)" }'
application/json
{
  "code": 200,
  "title": "OK",
  "response": {
    "id": "mapp_0ceacb46e5b3a99e91b6",
    "object": "app",
    "name": "Support triage (EN)",
    "description": "Sorts an inbound support message by urgency.",
    "catalog_category": "",
    "type": "private",
    "status": "activated",
    "model": "modi_c2b7e9a1d3f0a8645c8e",
    "model_library": "mlyi_c2a1e7d9b4f30685c3a3",
    "created_at": 1789206600,
    "updated_at": 1789210200
  }
}

Publishing has two conditions: the app must already be activated, and it must carry a logo. Withdrawing an app always succeeds.

CodeMessageCause
404App not found.The app is not one of your project's apps.
422The app must be activated before it can be published.You sent "type": "public" for a draft app.
422The app needs an image logo before it can be published.You sent "type": "public" for an app with no logo.
422type must be 'public' or 'private'.type is neither value.
422The selected logo does not exist in this project's CDN library.logo is not a file of your project's logo library.
402The selected model library is not available for the current billing plan.The model version is above your plan.
422Nothing to update. Provide name, description, catalog_category, logo, model and/or type.No recognised field was sent.