Goal of this module
Define TaskFlow's two entities, see the CRUD endpoints each one generates, and create your first records — all without writing or deploying code.
The data model
TaskFlow needs two entities. A Project is a container; a Task belongs to a project via a project_id field. Defining each entity is a single POST /api/{apiname} to the apistudio gateway — {apiname} is a path parameter you choose (by convention create-<entity>), and the entity name comes from entityName in the body. The payload carries a field catalog (UpdateParams) and record key templates (baseKeys).
1 · Define Project
POST /api/create-project # {apiname} = "create-project" (your choice)
{
"entityName": "project",
"baseKeys": {
"pkTemplate": "org#{org};data#project",
"skTemplate": "project#{id}"
},
"UpdateParams": [
{ "name": "id", "colType": "guid" },
{ "name": "name", "colType": "string", "validate": true, "minlength": 1 },
{ "name": "status", "colType": "enum", "enumValues": ["active", "archived"] }
]
} 2 · Define Task
Task carries the relationship (project_id), a workflow status, a priority (the AI crew will set this in Module 6), an assignee, and a due_date.
POST /api/create-task # {apiname} = "create-task" (your choice)
{
"entityName": "task",
"baseKeys": {
"pkTemplate": "org#{org};data#task",
"skTemplate": "task#{id}"
},
"UpdateParams": [
{ "name": "id", "colType": "guid" },
{ "name": "title", "colType": "string", "validate": true, "minlength": 1 },
{ "name": "description", "colType": "string" },
{ "name": "project_id", "colType": "string", "validate": true },
{ "name": "status", "colType": "enum", "enumValues": ["todo", "in_progress", "done"] },
{ "name": "priority", "colType": "enum", "enumValues": ["low", "medium", "high"] },
{ "name": "assignee", "colType": "string" },
{ "name": "due_date", "colType": "string" }
]
} What each definition writes
The handler stores the primary create-<entity> definition and generates four more configs — get, list, update, delete. No per-entity table is created; records land in the shared app table under org#…;data#<entity>, scoped to the caller's workspace at runtime.
3 · The routes you now have
Both entities expose a full API on the app gateway immediately. Every route is authenticated with an app token and scoped to the caller's workspace.
POST /app/{orgCode}/create/task create a record
GET /app/{orgCode}/item/task/{id} read one
PUT /app/{orgCode}/update/task/{id} update fields
DELETE /app/{orgCode}/delete/task/{id} delete
GET /app/{orgCode}/list/task list (filters, search, paging)
GET /app/{orgCode}/count/task { count } 4 · Seed some records
Create a project, then a couple of tasks in it. (You'll get a real app token in Module 3; for now use the console's data tools or a token from your test workspace.)
# a project
curl -X POST https://{domain}.nostackai.com/app/{orgCode}/create/project \
-H 'Authorization: <app-token>' -H 'Content-Type: application/json' \
-d '{ "name": "Website relaunch", "status": "active" }'
# -> { "id": "<projectId>" }
# a task in that project
curl -X POST https://{domain}.nostackai.com/app/{orgCode}/create/task \
-H 'Authorization: <app-token>' -H 'Content-Type: application/json' \
-d '{ "title": "Draft the homepage copy",
"description": "Customer keeps emailing about the broken checkout link — urgent",
"project_id": "<projectId>", "status": "todo" }' Why writes return only the id
Create and update responses return only the fields in the entity's default response view — by default just id. This avoids echoing the whole record on every write; shape richer responses with views when you need them.
Try it
Define both entities, create one project and two tasks (leave priority unset — the crew fills it in Module 6). Then GET /app/{orgCode}/list/task and confirm both come back. Try GET /app/{orgCode}/count/task too.