Apps
An app is a reusable 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 Runs. An app is private to your project until you publish it, at which point it also appears in the Catalog.
Endpoints
| Method | Path | Scope |
|---|---|---|
| POST | /v1/app/create | app:write |
| GET | /v1/app/list | app:read |
| GET | /v1/app/detail/<app_id> | app:read |
| PATCH | /v1/app/detail/<app_id> | app:write |
| DELETE | /v1/app/detail/<app_id> | app:write |
<app_id> is an app identifier, prefixed mapp_. Editing and deleting both act on the detail path; there is no separate update or delete path.
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. |
type | string | private or public. A public app is listed in the catalog. |
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. |
model_library | string | The exact model build resolved for this app. |
data | array | The input schema. 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 and public. |
tokens | object | Tokens the app has consumed, 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 data type, as its identifier (prefixed mdti_). See Data field types. |
placeholder | string | The hint shown to whoever fills the field in. |
required | boolean | Whether a run must supply a value. |
runs and tokens answer two different questions: how often the app was launched, and what those launches spent. tokens covers the app's whole life, not the current billing cycle, and counts your project's own runs of it — so reading someone else's public app reports what you spent on it. Runs that ended in an error are left out, which is what makes the total match the figure the workspace shows for the same app. See Quota and token usage for the project-wide cycle figures.
Note —
tokensisnull, rather than three zeros, when the project's runs carry no token measurement at all. An app that exists but has never run answers0on all three.
Create an app
POST /v1/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. |
category | string | no | The task category used to pick a model. Also accepted as task_type or model_task_type. |
data | array | yes | The input schema. Each object needs a non-empty type and name, and may set placeholder and required. See Data field types below. |
instruction | array | yes | Each object needs a non-empty type and an instruction (also accepted as prompt). |
status | string | no | draft or activated. Defaults to activated. |
type | string | no | private or public. Defaults to private. |
curl -X POST https://api.thinkhx.com/v1/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": "text",
"data": [
{ "type": "input", "name": "message", "placeholder": "Paste the message", "required": true }
],
"instruction": [
{ "type": "system", "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": "text",
"model": "modi_7b1e4c2a9d05f3618ea4",
"model_library": "mlyi_2c9a7f45b8e1d06a3427",
"data": {
"madi_5d8b21c74e0af96b3d15": {
"type": "input",
"value": "message",
"placeholder": "Paste the message",
"required": true
}
},
"instruction": {
"miid_9a4c07e5b312df8641ca": {
"type": "system",
"instruction": "Classify the message as low, normal or urgent."
}
},
"status": "activated",
"created_at": 1754205600
}
} 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/app/detail/<app_id>if you need the standard shape.
At least one field of data must be required, otherwise the call is refused with 422 and At least one data field must be required. An unknown field type gives 404 and The selected data type does not exist., and an unknown instruction type gives 404 and The selected instruction type does not exist.
Data 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 | A PNG, JPG or WebP image. The model looks at it. |
document and image fields do not take typed text: a run supplies a file your project has already uploaded. See Runs for how to send one, and note that an image field only works with a model that reads images.
Creation is also gated on the project's plan. A plan that allows no custom apps, and a project that has reached its app limit, are both refused with 402 — see Error codes.
List your apps
GET /v1/app/list returns the project's own apps, newest first, excluding deleted ones. It accepts limit and offset.
curl "https://api.thinkhx.com/v1/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.",
"type": "private",
"status": "activated",
"model": "modi_7b1e4c2a9d05f3618ea4",
"model_library": "mlyi_2c9a7f45b8e1d06a3427",
"runs": { "private": 12, "public": 0 },
"tokens": { "input": 8420, "output": 3115, "total": 11535 },
"created_at": 1754205600,
"updated_at": 1754205600
}
],
"total": 1
}
} The list omits data, instruction and logo. Read one app to get them.
Read one app
GET /v1/app/detail/<app_id> returns the full object, including the input schema you need in order to launch a run.
curl https://api.thinkhx.com/v1/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.",
"type": "private",
"status": "activated",
"logo": "file_d81e4afa6c8bf11fa881",
"model": "modi_7b1e4c2a9d05f3618ea4",
"model_library": "mlyi_2c9a7f45b8e1d06a3427",
"data": [
{
"id": "madi_5d8b21c74e0af96b3d15",
"name": "message",
"type": "mdti_c2a39a7ad82bc017dccd",
"placeholder": "Paste the message",
"required": true
}
],
"instruction": [
{
"id": "miid_9a4c07e5b312df8641ca",
"type": "system",
"instruction": "Classify the message as low, normal or urgent."
}
],
"runs": { "private": 12, "public": 0 },
"tokens": { "input": 8420, "output": 3115, "total": 11535 },
"created_at": 1754205600,
"updated_at": 1754205600
}
} You can read your own apps in any status, and any activated public app of any project. Reading someone else'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 rather than the owner's total. Anything else gives 404 and App not found.
Edit an app
PATCH /v1/app/detail/<app_id> changes the name, the description and the visibility. Send at least one of them.
| Parameter | Type | Description |
|---|---|---|
name | string | A new name. An empty value is refused. |
description | string | A new description. An empty value is refused. |
type | string | public to publish the app, private to withdraw it. |
curl -X PATCH https://api.thinkhx.com/v1/app/detail/mapp_0ceacb46e5b3a99e91b6 \
-H "Authorization: Bearer sk-project-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "type": "public" }' {
"code": 200,
"title": "OK",
"response": {
"id": "mapp_0ceacb46e5b3a99e91b6",
"object": "app",
"name": "Support triage",
"description": "Sorts an inbound support message by urgency.",
"type": "public",
"status": "activated",
"model": "modi_7b1e4c2a9d05f3618ea4",
"model_library": "mlyi_2c9a7f45b8e1d06a3427",
"created_at": 1754205600,
"updated_at": 1754209200
}
} Publishing has two conditions. The app must already be activated, otherwise the call is refused with 422 and The app must be activated before it can be published., and it must carry an image logo, otherwise 422 and The app needs an image logo before it can be published. Withdrawing an app always succeeds. A type that is neither value gives 422 and type must be 'public' or 'private'.
Sending nothing recognisable gives 422 and Nothing to update. Provide name, description and/or type.
Delete an app
DELETE /v1/app/detail/<app_id> withdraws the app. It stops appearing in every listing and can no longer be read or run.
curl -X DELETE https://api.thinkhx.com/v1/app/detail/mapp_0ceacb46e5b3a99e91b6 \
-H "Authorization: Bearer sk-project-YOUR_KEY" {
"code": 200,
"title": "OK",
"response": {
"deleted": true,
"id": "mapp_0ceacb46e5b3a99e91b6"
}
} Note — The runs the app has already produced are kept and stay readable through Runs.