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
| Method | Path | Scope |
|---|---|---|
| POST | /v1/ai/app/create | app:write |
| GET | /v1/ai/app/list | app: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
| Field | Type | Description |
|---|---|---|
id | string | The app identifier, prefixed mapp_. |
object | string | Always app. |
name | string | The app's name. |
description | string | What the app does. |
catalog_category | string | The trade the app is filed under, as its identifier (prefixed acci_), or an empty string. |
type | string | private or public. |
status | string | draft or activated. Only an activated app can be run. |
logo | string | The identifier of the app's logo file, or an empty string. |
model | string | The model the app runs on, prefixed modi_. |
model_library | string | The exact model version resolved for this app, prefixed mlyi_. |
data | array | The input fields. One object per field the caller fills in. |
instruction | array | The instructions sent to the model. null when you do not own the app. |
runs | object | Run counters, as private (runs by the owner's project) and public (runs by other projects). |
tokens | object | Tokens your project has spent on the app, as input, output and total, or null. |
created_at | integer | Creation time, in Unix seconds. |
updated_at | integer | Last change, in Unix seconds. |
Each entry of data describes one input field:
| Field | Type | Description |
|---|---|---|
id | string | The field identifier, prefixed madi_. |
name | string | The field's name. This is the key you send when you launch a run. |
type | string | The field's type, as its identifier (prefixed mdti_). See Field types. |
placeholder | string | The hint shown to whoever fills the field in. |
required | boolean | Whether 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | The app's name. |
description | string | Yes | What the app does. |
data | array | Yes | The input fields. Each object needs a non-empty type and name, and may set placeholder and required (defaults to true). See Field types. |
instruction | array | Yes | Each object needs a non-empty type and an instruction (also accepted as prompt). See Instruction types. |
category | string | No | The task the app performs, used to pick a model. See Task categories. Also accepted as task_type or model_task_type. |
model | string | No | A model, by identifier or by name (such as qwen3). The best version your plan allows is used. |
model_library | string | No | One exact model version, by identifier. |
catalog_category | string | No | The trade to file the app under, by identifier or by English name (such as Customer Support & Call Centres). |
status | string | No | draft or activated. Defaults to activated. |
type | string | No | private 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 -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." }
]
}' {
"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, not201, and itsdataandinstructionare objects keyed by the new identifiers rather than the arrays the read endpoints return. Read the app back withGET /v1/ai/app/detail/<app_id>if you need the standard shape.
| Code | Message | Cause |
|---|---|---|
| 400 | Missing required field: name. | No name was sent. The same message names description. |
| 422 | Each data object must contain non-empty 'type' and 'name' properties. | data is missing, or one of its fields has no type or name. |
| 422 | Each instruction object must contain non-empty 'type' and 'instruction' properties. | instruction is missing, or one of its entries is incomplete. |
| 422 | At least one data field must be required. | Every field was sent with "required": false. |
| 404 | The selected data type does not exist. | A field type is not one of the field types. |
| 404 | The selected instruction type does not exist. | An instruction type is not one of the instruction types. |
| 404 | The selected model does not exist. | model names no model. |
| 422 | No 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. |
| 422 | The selected catalog_category does not exist. | catalog_category names no trade. |
| 402 | The current billing plan does not allow custom app creation for this project. | Your plan includes no apps. |
| 402 | The 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.
type | Identifier | What the field collects |
|---|---|---|
input | mdti_c2a39a7ad82bc017dccd | One line of text. |
text area | mdti_fdd932b23e1fa82aa942 | A block of text. |
number | mdti_0bf56e96d417b823b7e8 | A number. |
email | mdti_52f84207d0f8a95ed43d | An email address. |
document | mdti_5017cf8657e1df4df809 | A PDF, DOCX, XLSX, CSV or TXT file. The model reads its text. |
image | mdti_1777f1beedfbd5cde030 | An 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:
type | Identifier |
|---|---|
text | aiti_d0c8a5e7b2f3a6c4e9d1 |
nombre | aiti_d7a3c1f9e2b4d8a6c0f5 |
element | aiti_4a8e1d6b9c3f0a52c7e5 |
notation | aiti_8d6c3b1a9e0f7c24a5e8 |
percent | aiti_7f0a3b5d9c2e1a648c7e |
image | aiti_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 "https://api.ultineo.com/v1/ai/app/list?limit=2" \
-H "Authorization: Bearer sk-project-YOUR_KEY" {
"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 https://api.ultineo.com/v1/ai/app/detail/mapp_0ceacb46e5b3a99e91b6 \
-H "Authorization: Bearer sk-project-YOUR_KEY" {
"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.
| Parameter | Type | Description |
|---|---|---|
name | string | A new name. An empty value is refused. |
description | string | A new description. An empty value is refused. |
catalog_category | string | A trade, by identifier or English name. An empty string removes it. |
model | string | Another model, by identifier or name. The best version your plan allows is used. |
model_library | string | One exact model version, by identifier. |
logo | string | A file of your project's logo library, by identifier. An empty string removes the logo. |
type | string | public to publish the app, private to withdraw it. |
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)" }' {
"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.
| Code | Message | Cause |
|---|---|---|
| 404 | App not found. | The app is not one of your project's apps. |
| 422 | The app must be activated before it can be published. | You sent "type": "public" for a draft app. |
| 422 | The app needs an image logo before it can be published. | You sent "type": "public" for an app with no logo. |
| 422 | type must be 'public' or 'private'. | type is neither value. |
| 422 | The selected logo does not exist in this project's CDN library. | logo is not a file of your project's logo library. |
| 402 | The selected model library is not available for the current billing plan. | The model version is above your plan. |
| 422 | Nothing to update. Provide name, description, catalog_category, logo, model and/or type. | No recognised field was sent. |