The field to keep. It is exactly what expected_active_sha needs when you publish, so listing and then publishing is two calls with nothing to look up in between.
Appearance
Appearance
Find out what a key can reach, without anyone emailing you IDs.
INFO
GET /prompts and GET /skills are on-prem endpoints. GET /skills/whoami is available on SaaS and on-prem.
Three read-only calls, all using the API key you already have. A read key is enough — listing is a read.
None of them return prompt or skill content: names, kinds and version identifiers only. Content comes from Reading, so a listing cannot become a bulk export.
Start here. A key carries its owner's identity, so this answers "what is in scope for me".
curl -s "$IM_URL/skills/whoami?api_key=$IM_KEY"{
"user_id": "cd5c29a0-7b51-4f1f-8381-1b0c213e7131",
"email": "integrations@example.com",
"organizations": [
{ "id": "d7fb6107-1188-4f7c-97e2-978403f3e3be", "name": "Optisol" }
],
"accessible_projects": 3,
"projects": [
{ "id": "8112d9c4-8334-43dd-a3c0-436b5cf1f475", "name": "sample" }
],
"key_id": "ak_01H…",
"key_created_at": "2026-08-21T12:33:06Z"
}The path lives under /skills/ for historical reasons — it is the pm-skills CLI's login call — but it is not skill-specific. It is the identity of the key.
curl -s "$IM_URL/prompts?project_id=8112d9c4-8334-43dd-a3c0-436b5cf1f475" \
-H "X-API-Key: $IM_KEY"{
"project_id": "8112d9c4-8334-43dd-a3c0-436b5cf1f475",
"project_name": "sample",
"flow_mode": "ai_assisted",
"total": 4,
"limit": 100,
"offset": 0,
"prompts": [
{
"name": "sampleAgent",
"kind": "instruction",
"has_active_version": true,
"active_version_sha": "c8aceef013a8",
"active_version_label": "v1.0",
"environments": ["development", "test"],
"published_via": null,
"updated_at": "2026-08-21T09:14:02Z"
},
{
"name": "draftOnly",
"kind": "chat",
"has_active_version": false,
"active_version_sha": null,
"active_version_label": null,
"environments": [],
"published_via": null,
"updated_at": "2026-08-25T11:47:03Z"
}
]
}active_version_shastring | nullThe field to keep. It is exactly what expected_active_sha needs when you publish, so listing and then publishing is two calls with nothing to look up in between.
has_active_versionbooleanWhether anything is live. A prompt with no active version is listed rather than hidden — "exists but nothing is live" is a real state, and omitting it would leave you unable to explain why a name you were given is missing.
You cannot publish over one of these: there is no version to replace. Filter them out before offering a publish.
flow_modestringWhat a publish will actually do in this project — review_required, direct_publish or ai_assisted. See Publishing flows.
published_viastring | nullHow the live version got there. null means a human reviewed and approved it; "api" means it came through this API; "direct" means someone published it in the console without review.
environmentsstring[]Where the active version is released. An empty list means it is active but reaches no environment.
limit (default 100, max 500) and offset page the list; total is the unpaged count.
curl -s "$IM_URL/prompts?organization_id=d7fb6107-1188-4f7c-97e2-978403f3e3be" \
-H "X-API-Key: $IM_KEY"{
"organization_id": "d7fb6107-1188-4f7c-97e2-978403f3e3be",
"total": 4,
"projects": [
{
"project_id": "8112d9c4-8334-43dd-a3c0-436b5cf1f475",
"project_name": "sample",
"flow_mode": "ai_assisted",
"prompts": [ "…" ]
}
]
}Grouped by project, not flat. A prompt name is unique only within a project, so a flat list could carry the same name twice with nothing to tell them apart.
Only projects you can open appear. An organisation you belong to but whose projects you were never added to returns 200 with an empty projects list — not a 403, so "nothing here" stays distinguishable from "you may not look".
GET /skillsSkills are tiered — project, organisation and global — so the response shape differs from prompts: a project-scoped listing returns the effective set after resolution, and organisation scope reports the promotion ladder.
Documented with the rest of the skills API: Skills Manager → HTTP API → Discovery.
The scoping and authorization rules below are identical for both endpoints.
Pass exactly one of project_id or organization_id. Both, or neither, is a 422.
That is deliberate rather than lenient: silently preferring one would let you believe you had asked an organisation-wide question and quietly receive a single project.
| Status | Meaning |
|---|---|
401 | key missing, invalid or revoked |
403 | you are not a member of that project, or that organisation |
422 | both or neither scope parameter; or limit above 500 |
Nothing here is hardcoded. From a key alone:
# 1. what can this key see?
PID=$(curl -s "$IM_URL/skills/whoami?api_key=$IM_KEY" | jq -r '.projects[0].id')
# 2. what is in it, and what will a publish do here?
curl -s "$IM_URL/prompts?project_id=$PID" -H "X-API-Key: $IM_KEY" \
| jq '{flow: .flow_mode, prompts: [.prompts[] | select(.has_active_version) | {name, sha: .active_version_sha}]}'
# 3. publish, using only what the listing returned → see PublishingHi — ask anything about elsai docs. Mention the product name: elsai ARMS, Prompt Manager, Agent Framework, Core, or elsai Guardrails.