Prompts and runs
Create saved prompts and their variables, fill them in, run them against one or more models, stream the result and share prompts, all through the REST API.
A saved prompt is a reusable template from your prompt library. Its text contains {{variables}} that you fill in each time you run it. The REST API lets you build prompts from code, run them with fresh values against the model of your choice, and read the results back. Every request runs as the user who owns the API key.
All paths are relative to https://api.stickyprompts.com/api/v1. Request bodies are JSON.
Key concepts
A prompt is a named template. Its text lives in a prompt version: there is one version per prompt, and posting new content overwrites it. A version has variables, referenced in the text as {{slug}}, for example Write a {{tone}} summary of: {{text}}.
To run a prompt you fill in its variables. That creates an instance: an immutable copy of the template with the values substituted. You then run the instance against one or more models. Each run creates a conversation (called conversation_context in the API) that you can keep chatting in through the Conversations endpoints.
prompt -> prompt_version (content with {{slug}} placeholders) -> variables
|
+-> instance (variable values, parsed content) -> run -> conversation
Shortcut endpoints under /run/prompt/... create the instance and run it in one call. POST /run sends raw text to a model with no prompt at all.
Conventions
- Pagination. Where noted, send
page(default1) andsize(default10). The response headersX-Page,X-Page-SizeandX-Total-Countdescribe the result. See Errors, pagination and quotas. - Project scope. Several endpoints read an optional
X-Project-ID: <numeric id>header. A non-numeric value is treated as absent. - Field casing. Database objects use capitalised
ID,CreatedAt,UpdatedAtandDeletedAtnext to snake_case fields. The RunHistory result uses lowercaseidandcreated_at. - Rendered text. Run
resultandthinkingfields contain HTML rendered from the model’s Markdown, not the raw Markdown. - Errors are
{"error": "..."}. Quota failures are429 {"error":"Quota exceeded","reason":"..."}. Guardrail blocks add a machine code, for example{"error": "...", "code": "SENSITIVE_DATA_BLOCKED"}. Data-residency failures use the codesEU_INFERENCE_REQUIRED,ZERO_RETENTION_REQUIRED,NO_PROVIDER_ASSIGNEDandNO_ENDPOINT_AVAILABLE.
The Prompt object
{
"ID": 42,
"CreatedAt": "2026-09-20T10:15:00Z",
"UpdatedAt": "2026-09-20T10:15:00Z",
"DeletedAt": null,
"name": "Renewal check-in email",
"slug": "k3j9x2qa",
"user_id": 7,
"project_id": null,
"shared_with_workspace_id": null,
"current_version_id": 88,
"current_version": null,
"ai_model_id": 12,
"description": "Friendly renewal reminder for a Northwind customer",
"is_favorite": false,
"category_id": 3,
"category": null,
"example_output": "",
"duplicated_from_id": null,
"file_data": [],
"tools": [{ "name": "web_search", "use": true }],
"mcp_servers": [],
"ai_style": "prompty"
}
current_versionis{ID, ..., prompt_id, content, variables[], current_instance_id, current_instance}when loaded; most list endpoints leave itnull.categoryis{ID, CreatedAt, UpdatedAt, DeletedAt, name}when loaded.file_datais an array of File objects when loaded, otherwisenull. A File is{ID, CreatedAt, UpdatedAt, DeletedAt, name, path, preview_path, public_path, user_id, workspace_id, project_id, fileType, partial}; note the camelCasefileType.toolsis[{"name", "use"}]ornull. Tool names:web_search,image_generation,video_generation,speech_generation,music_generation,remote_mcp.mcp_serversis an array of remote MCP server IDs.
Prompts
GET /prompt
/api/v1/prompt Lists your own prompts that are not in a project, newest first. With X-Project-ID, lists that project’s prompts instead.
-
searchstring query optional - Matched against name and description.
-
pageinteger query optional default1 - Page number.
-
sizeinteger query optional default10 - Items per page.
-
X-Project-IDinteger header optional - List this project’s prompts.
Response. 200 with an array of Prompt objects (current_version, category and file_data not loaded). With X-Project-ID, each item also has "can_delete": bool, true only when you can manage the project’s prompts and own that prompt.
Errors. 404 {"error":"Project not found"}, 403 {"error":"You don't have access to this project"}.
GET /prompt/all
/api/v1/prompt/all Returns your own prompts, prompts shared with you and prompts you have favorited in one list, ordered by when you last ran each one (falling back to creation time). Prompts inside a project are excluded.
-
searchstring query optional - Matched against name and description.
-
pageinteger query optional default1 - Page number.
-
sizeinteger query optional default10 - Items per page.
-
showWorkspaceSharedstring query optional "true"also includes prompts shared with your workspace.
Response. 200 with an array of Prompt objects with file_data loaded, or [].
GET /prompt/last_used
/api/v1/prompt/last_used Returns up to 8 prompts you have run, most recently run first, with file_data loaded. Prompts you never ran are left out. Pagination headers are set, but page and size are ignored.
GET /prompt/most_used
/api/v1/prompt/most_used Returns up to 8 prompts ordered by how often you have run them. No pagination headers.
GET /prompt/recently_added
/api/v1/prompt/recently_added Returns up to 8 of your own prompts, newest first, with file_data loaded.
POST /prompt
/api/v1/prompt Creates a prompt. It starts without a version: call POST /prompt_version next to give it content. The model comes from your preferences (or the first model in the catalogue), and ai_style from your preferences (or prompty). To change either, call PUT /prompt/:id afterwards.
-
namestring optional - Not validated on create, but send it.
-
descriptionstring optional - Shown on the prompt card.
-
example_outputstring optional - Sample output.
-
category_iduint optional - Prompt category ID, or null.
-
toolsarray optional - Default tools,
[{"name", "use"}]. -
mcp_serversuint[] optional - Remote MCP server IDs.
-
X-Project-IDinteger header optional - Create the prompt inside this project. You need permission to manage the project’s prompts.
ai_model_id, ai_style and slug are ignored on create; a random slug is generated.
curl -X POST https://api.stickyprompts.com/api/v1/prompt \
-H "Authorization: Bearer $STICKY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Renewal check-in email", "description": "Friendly renewal reminder" }'
Response. 201 with the Prompt object.
Errors. 400 malformed JSON, 404 {"error":"Project not found"}, 403 {"error":"You don't have access to manage prompts of this project"}, 409 {"error":"Entity already exists"}.
PUT /prompt/:id
/api/v1/prompt/:id Replaces a prompt’s editable fields. Only your own prompts.
-
idinteger path required - Numeric prompt ID.
-
namestring required - Must not be blank.
-
descriptionstring optional - Overwritten.
-
ai_model_iduint optional - Overwritten. Omitting it or sending
0stores0. -
example_outputstring optional - Overwritten.
-
category_iduint optional - Overwritten.
-
ai_stylestring optional - Overwritten.
-
toolsarray optional - Changed only when present.
-
mcp_serversuint[] optional - Changed only when present.
All of name, description, ai_model_id, example_output, category_id and ai_style are overwritten, so always send the current values of fields you do not want to change.
Response. 200 with the Prompt object. Errors. 404 {"error":"Prompt not found"}, 400 {"error":"Name is required"}.
POST /prompt_version
/api/v1/prompt_version Sets a prompt’s template content. The first call creates the version and makes it current; later calls overwrite content on the same version. Create variables with POST /variable, not through this body.
-
prompt_iduint required - One of your own prompts.
-
contentstring required - Template text. Reference variables as
{{slug}}.
Response. 201 on both create and update:
{
"ID": 88, "CreatedAt": "...", "UpdatedAt": "...", "DeletedAt": null,
"prompt_id": 42,
"content": "Write a renewal check-in email to {{customer_name}} about their {{plan}} plan.",
"variables": null,
"current_instance_id": null,
"current_instance": null
}
variables is not loaded on the update path. Errors. 400 malformed JSON, 404 {"error":"Prompt not found"}.
DELETE /prompt/:id
/api/v1/prompt/:id Soft-deletes one of your own prompts, and removes its shares and everyone’s favorites of it.
-
idinteger path required - Numeric prompt ID.
Response. 204. 404 with an empty body when the prompt does not exist or is not yours.
POST /prompt/:id/duplicate
/api/v1/prompt/:id/duplicate Copies a prompt you own or can access (the public catalogue, prompts shared with you, prompts shared with your workspace). The copy includes name, description, model, example output, tools, style, category, version content and variables (name, slug, description, data_type, default_value, required, sort, is_primary, is_multi_select, options). Not copied: mcp_servers, attached files, and each variable’s file_types and default_file_id.
-
idstring path required - Numeric prompt ID or slug.
-
X-Project-IDinteger header optional - Create the copy in this project, keeping the original name. Without it,
" (copy)"is appended to the name.
Response. 200 with the new Prompt object, current_version.variables filled in.
Errors. 404 {"error":"Prompt not found"}, 403 {"error":"You don't have access to this prompt"}, 404 {"error":"Project not found"}, 403 {"error":"You don't have access to manage prompts of this project"}.
PUT /prompt/:id/move-to-project
/api/v1/prompt/:id/move-to-project Moves a prompt into a project, between projects, or out of a project. No body. You need manage-prompts permission on every project involved, and a prompt moved into a project must be yours.
-
idinteger path required - Numeric prompt ID.
-
X-Project-IDinteger header optional - The target project. Leave it out to remove the prompt from its project.
Response. 200 with the Prompt object, project loaded.
Errors. 404 {"error":"Prompt not found"} or {"error":"Prompt not found or you don't have access to it"}, 400 {"error":"Prompt is already not in a project"}, 400 {"error":"Prompt is already in this project"}, 404 {"error":"Project not found"}, 403 without project permissions.
PUT /prompt/:id/share_with_workspace
/api/v1/prompt/:id/share_with_workspace Makes one of your own prompts visible to your whole workspace, or private again.
-
idinteger path required - Numeric prompt ID.
-
sharedbool required trueto share with the workspace,falseto make it private.
Response. 200 with the Prompt object; shared_with_workspace_id is your workspace ID or null. Errors. 404 {"error":"Prompt not found"}, 400 {"error":"User is not part of a workspace"}.
Favorites
GET /prompt/favorite
/api/v1/prompt/favorite Returns your favorite prompts in the order you favorited them (oldest first). Every item has "is_favorite": true; version, category and files are not loaded. No pagination.
POST /prompt/:id/favorite
/api/v1/prompt/:id/favorite Marks a prompt as a favorite. It must be your own, shared with you directly, or a public catalogue prompt. A prompt shared only with your workspace returns 403.
-
idinteger path required - Numeric prompt ID.
Response. 201. The nested prompt key really is capitalised Prompt:
{ "ID": 311, "CreatedAt": "2026-09-28T09:00:00Z", "UpdatedAt": "2026-09-28T09:00:00Z", "DeletedAt": null,
"user_id": 7, "prompt_id": 42, "Prompt": null }
Errors. 404 {"error":"Prompt not found"}, 403 {"error":"You don't have access to this prompt"}, 409 {"error":"This prompt is already marked as favorite"}.
DELETE /prompt/:id/favorite
/api/v1/prompt/:id/favorite Removes a favorite.
-
idinteger path required - Numeric prompt ID.
Response. 204. Errors. 404 {"error":"Prompt not found in favorites"}.
Variables
Variables belong to a prompt version. The slug must match a {{slug}} placeholder in the content and be unique within the version. See Prompt variables for how they behave in the app.
{
"ID": 301, "CreatedAt": "...", "UpdatedAt": "...", "DeletedAt": null,
"prompt_version_id": 88,
"name": "Customer name",
"slug": "customer_name",
"description": "Who the email is addressed to",
"data_type": "short_text",
"default_value": "",
"required": true,
"sort": 0,
"is_primary": true,
"is_multi_select": false,
"options": null,
"file_types": "",
"default_file_id": null,
"default_file": null
}
data_typeisshort_text,long_text,select,number,jsonorlov.options(for select-style variables) is[{"label": "...", "value": "..."}].is_primarymarks the variable that the/run/prompt/lastand/run/prompt/:idshortcuts fill in.
POST /variable
/api/v1/variable Adds a variable to a prompt version. The prompt must be yours. The body is the Variable object without ID and timestamps; the server does not validate fields other than these three.
-
prompt_version_iduint required - The version to add to.
-
namestring required - Display name.
-
slugstring required - Matches
{{slug}}in the content. -
descriptionstring optional - Help text.
-
data_typestring optional short_text,long_text,select,number,jsonorlov.-
default_valuestring optional - Used when no value is sent.
-
requiredbool optional - Enforced by
POST /instance. -
sortinteger optional - Display order.
-
is_primarybool optional - The variable the shortcut endpoints fill.
-
is_multi_selectbool optional - Allow several options.
-
optionsarray optional [{"label", "value"}].-
file_typesstring optional - Accepted file types.
-
default_file_iduint optional - Default file.
Response. 201 with the Variable object.
Errors. 400 malformed JSON, 404 {"error":"Prompt version not found"}, 403 {"error":"You are not allowed to add variables to this prompt"}, 409 {"error":"Entity already exists"} (duplicate slug).
PUT /variable/:id
/api/v1/variable/:id Replaces a variable’s fields. The update overwrites every field, so send them all: name, slug, description, data_type, default_value, required, sort, is_primary, is_multi_select, options, file_types and default_file_id.
-
idinteger path required - Variable ID.
History is preserved: if you change name or slug on a variable that already has stored values from earlier runs, the server first archives the old variable (a soft-deleted copy whose slug gets a _deleted_<timestamp> suffix), moves those values to it, then updates the variable in place. On that path, file_types and default_file_id are not updated.
Response. 200 with the Variable object. Errors. 404 {"error":"Variable not found"}, 403 {"error":"You are not allowed to update variables of this prompt"}.
DELETE /variable/:id
/api/v1/variable/:id Deletes a variable.
-
idinteger path required - Variable ID.
Response. 204. Errors. 404 {"error":"Variable not found"}, 403 {"error":"You are not allowed to delete variables of this prompt"}.
Instances
An instance is the template with values filled in. Every {{slug}} is replaced by the value you sent, or by the variable’s default_value when the value is empty or missing. Placeholders that match no variable stay as they are. If your workspace has guardrails, they are applied to the values and the parsed content before anything is stored, so values may come back redacted.
{
"ID": 5120, "CreatedAt": "...", "UpdatedAt": "...", "DeletedAt": null,
"prompt_version_id": 88,
"content": "Write a renewal check-in email to Northwind Traders about their Business plan.",
"variable_values": [
{ "ID": 9001, "variable_id": 301, "prompt_version_instance_id": 5120, "value": "Northwind Traders", "file_id": null, "file": null }
]
}
POST /instance
/api/v1/instance Fills in a version’s variables and stores the result as a new instance, which also becomes the version’s current instance.
-
prompt_version_iduint required - The version to fill in.
-
variable_valuesarray optional - Items
{"variable_id": uint, "value": string, "file_id": uint | null}. Required when the version has required variables.
curl -X POST https://api.stickyprompts.com/api/v1/instance \
-H "Authorization: Bearer $STICKY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt_version_id":88,"variable_values":[{"variable_id":301,"value":"Northwind Traders"},{"variable_id":302,"value":"Business"}]}'
Response. 201 with the instance object.
Errors. 404 {"error":"Prompt version not found"}, 400 {"error":"Invalid instance request"} when a required variable has no non-empty value, 400 {"error":"...","code":"SENSITIVE_DATA_BLOCKED"} when a guardrail blocks the input.
GET /instance/:id
/api/v1/instance/:id Returns an instance with its variable_values. 404 with an empty body if it does not exist.
-
idinteger path required - Instance ID.
Running prompts
How the model is chosen
| Endpoint | Model used |
|---|---|
POST /instance/:id/run and /run/stream | Each ID in ai_model_ids. No default and no "auto" value: send no IDs and nothing runs. With agent_id, or when continuing a conversation that has an agent, the agent’s model replaces ai_model_ids. |
POST /run/prompt/last and POST /run/prompt/:id | The model of the run being replayed. When /run/prompt/:id has no earlier run, the prompt’s own ai_model_id. |
POST /run/prompt/:id/variables | The prompt’s ai_model_id. |
POST /run | ai_model_id, or the model whose version (the provider’s model identifier) equals ai_model_name. |
None of these use automatic model routing; that belongs to the Conversations endpoints. Every run endpoint checks the chosen model against your workspace’s model access rules (400 {"error":"The selected AI model has been restricted by your organization"}, worded slightly differently on the shortcuts) and data residency rules (400 with a residency code).
The RunHistory object
{
"id": 7781,
"created_at": "2026-09-28T09:12:03Z",
"result": "<p>Hi Northwind team, your Business plan renews on ...</p>\n",
"raw_text": null,
"thinking": "",
"runtime": 4210,
"ai_model_id": 12,
"input_prompt": "Write a renewal check-in email to {{customer_name}} about their {{plan}} plan.",
"variables": [],
"conversation_context_id": 16044,
"conversation_context_slug": "q8w2m1zc",
"conversation_context_name": "",
"is_interactive": true,
"input_file_data": [],
"output_file_data": [],
"error_text": null,
"error_id": null,
"run_status": "success"
}
| Field | Meaning |
|---|---|
result | The model’s answer rendered from Markdown to HTML. <think> blocks are stripped. |
raw_text | Always null here. For the raw Markdown, read the conversation (GET /conversation/:id) or the stream’s raw_text. |
thinking | Reasoning rendered as HTML, or "". |
runtime | Milliseconds from start to end. 0 while not finished, for example in the streaming response. |
input_prompt | The unparsed template. The substituted text is the instance’s content. |
variables | {id, variable_id, name, slug, value} items. Empty on the run endpoints; filled by GET /prompt/:id/run_history. |
conversation_context_id, _slug, _name | The conversation created or continued by this run. |
is_interactive | Whether the conversation can be continued. false for comparison-mode runs. |
input_file_data, output_file_data | {id, name, path} files attached to the user message, and files the model generated. |
run_status | pending, then generating, then success or error. |
error_text, error_id | Set when the provider call failed. |
Usage and credits are not part of any run response.
POST /instance/:id/run
/api/v1/instance/:id/run Runs an instance and waits until the model finishes. A new conversation holding the user message (the parsed instance content) and the AI reply is created, unless you pass conversation_context_id. If that conversation has an active model comparison, the instance runs once in each thread and all results are returned.
-
idinteger path required - Instance ID, from
POST /instance.
-
comparison_modestring query optional defaultfalse "true"marks the conversation non-interactive; a restricted model then produces “One of the selected AI models has been restricted by your organization”.-
X-Project-IDinteger header optional - The project’s files are added to the run’s attachments and the conversation is filed under the project.
-
ai_model_idsuint[] required - One run per model ID. Required in practice: an empty array runs nothing and returns
200 null. The number of IDs is limited by server configuration (default 1); going over returns503 {"error":"cannot run this many models in parallel"}. The free tier cannot send more than one:503 {"error":"free tier does not support comparison mode"}. -
ai_stylestring optional - Response style, for example
prompty. Not taken from the prompt automatically, so pass the prompt’sai_style. -
attachmentsarray optional - Items
{"id": uint, "type": "file" | "transcription", "temp": bool}. Files with an extension the model does not support are silently skipped. -
conversation_context_iduint optional - Continue this conversation instead of creating one.
-
is_prompt_studiobool optional - Tag the conversation as a Prompt Studio run (source
studio). Only studio runs appear inGET /prompt/:id/run_history. -
flash_modebool optional - Forwarded to the model runner.
-
reasoning_effortstring optional - For thinking models, for example
low,mediumorhigh. Allowed values depend on the model; unknown values fall back to the model’s default. -
knowledge_base_idsuint[] optional - Knowledge bases to retrieve context from. Only bases you may access are used.
-
knowledge_base_limit_node_idsuint[] optional - Restrict retrieval to these nodes.
-
world_knowledgebool optional - With knowledge bases, whether the model may also use general knowledge.
-
agent_iduint optional - Run as this agent. Its model, system prompt, files, tools, MCP servers and knowledge bases override the request fields.
-
toolsarray optional - Items
{"name", "use", "mcp_tool_id"}.mcp_tool_idis needed only forremote_mcp.
curl -X POST https://api.stickyprompts.com/api/v1/instance/5120/run \
-H "Authorization: Bearer $STICKY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ai_model_ids":[12],"ai_style":"prompty","tools":[{"name":"web_search","use":true}]}'import os, requests
r = requests.post(
"https://api.stickyprompts.com/api/v1/instance/5120/run",
headers={"Authorization": f"Bearer {os.environ['STICKY_API_KEY']}"},
json={"ai_model_ids": [12], "ai_style": "prompty",
"tools": [{"name": "web_search", "use": True}]},
timeout=300,
)
r.raise_for_status()
for run in r.json():
if run["run_status"] == "error":
print("Failed:", run["error_text"], run["error_id"])
else:
print(run["conversation_context_id"], run["result"]) # result is HTMLconst res = await fetch("https://api.stickyprompts.com/api/v1/instance/5120/run", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.STICKY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
ai_model_ids: [12],
ai_style: "prompty",
tools: [{ name: "web_search", use: true }],
}),
});
const runs = await res.json(); // one RunHistory per model
for (const run of runs) console.log(run.run_status, run.error_id ?? run.result);Response. 200 with an array of RunHistory objects, one per model.
Errors
| Status | When |
|---|---|
400 | Invalid JSON; restricted model; residency violation (with code); knowledge base not found; invalid project ({"error":"Invalid project ID"}); sensitive-data block (code: SENSITIVE_DATA_BLOCKED); MCP errors ({"error": "...", "error_code": "..."}). |
404 | Instance not found (empty body), {"error":"ConversationContext not found"}, {"error":"Agent not found"}, {"error":"Project not found"}. |
429 | Quota exceeded. |
503 | Other failures before the model call, such as the model limit or the free-tier restriction. |
POST /instance/:id/run/stream
/api/v1/instance/:id/run/stream Takes the same path, query, header and body as /run and runs the same validation, but it does not stream over this HTTP response. Instead:
- The server creates the runs and the conversation, starts generation in the background, and returns 200 right away with an array of RunHistory objects in
run_status: "pending",result: "",runtime: 0, each with a validconversation_context_id. - Tokens are pushed to the conversation’s WebSocket,
GET /conversation/{conversation_context_id}/ws. It takes the sameAuthorization: Bearerheader and needs theconversations_getpermission. Server-side clients can send that header; browsers cannot. - Every frame is a JSON text message
{"type": ..., "data": ...}:
type | data | Notes |
|---|---|---|
message | Message object | A full snapshot of the AI message so far, not a delta. text is the HTML rendering and raw_text the Markdown; also parts, thinking, citations, status (thinking, generating, finished) and ai_model_id. Replace your copy on each frame. |
run_data | RunHistory | Sent with message updates. result holds the HTML so far and run_status the current status. |
ai_message_end | none | Generation finished successfully. |
error_message | Message object | Generation failed; see error_text, error_id and error_code. Terminal. |
ai_model_change | {ai_model_id, ai_style, auto_model_type, reasoning_effort} | Conversation settings changed. Not specific to prompt runs. |
- After
ai_message_end, read the final state withGET /conversation/:id.
Events sent before any client connects are buffered and replayed to the first connection, so you can open the socket right after the POST returns. Your own user message is not echoed back. Once the POST has returned 200, provider errors arrive only as error_message frames; errors before generation (model limit, free tier, residency) come back as the non-200 statuses listed for /run. The legacy GET /conversation/:id/stream SSE endpoint does not relay message snapshots for these runs, so use the WebSocket. Full WebSocket details and client examples are in Conversations.
POST /run/prompt/last
/api/v1/run/prompt/last Replays your most recent prompt run with new “primary data”. Built for quick re-runs, for example from a browser extension. It picks the newest run of any prompt you own or that is shared with you, builds a new instance where the primary variable (or the only variable) gets data and every other variable keeps its value from that run (or its default), and runs it with the same model and tools and the prompt’s ai_style. A new interactive conversation is created (source extension).
-
datastring required - New text for the primary variable.
Response. 200 with a single RunHistory object, not an array.
Errors. 404 {"error":"No runs found"}, 400 {"error":"Invalid instance request"} when a required variable ends up empty, 400 for a restricted model or a residency or sensitive-data block, 429 on quota. If the model call fails before a run is recorded, the request fails with 500.
POST /run/prompt/:id
/api/v1/run/prompt/:id The same as /run/prompt/last, but for one prompt. It replays the prompt’s latest run with data in the primary variable. If the prompt was never run and it is your own, a first run is built from the variables’ defaults and the prompt’s ai_model_id. The replayed run is the prompt’s latest run overall, not only yours, so on a shared prompt the non-primary values can come from another user’s latest run.
-
idinteger path required - Numeric prompt ID.
-
datastring required - New text for the primary variable.
curl -X POST https://api.stickyprompts.com/api/v1/run/prompt/42 \
-H "Authorization: Bearer $STICKY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"data":"Northwind Traders"}'
Response. 200 with a single RunHistory object. Errors. 404 {"error":"Prompt not found"}, plus the errors of /run/prompt/last.
POST /run/prompt/:id/variables
/api/v1/run/prompt/:id/variables Runs a prompt’s current version with a full set of variable values: instance creation and the run in one call. Uses the prompt’s ai_model_id and ai_style. The new instance becomes the version’s current instance, and a new interactive conversation is created (source extension).
-
idinteger path required - Numeric prompt ID. The prompt must be yours or shared with you.
-
variable_valuesarray optional - Items
{"variable_id": uint, "value": string, "file_id": uint | null}. Omitted or empty values fall back to the default.requiredis not enforced here. -
web_contextstring optional - Extra text placed before the parsed prompt, separated by a blank line. Guardrails apply to it.
-
toolsarray optional - Same format as in
POST /instance/:id/run.
curl -X POST https://api.stickyprompts.com/api/v1/run/prompt/42/variables \
-H "Authorization: Bearer $STICKY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"variable_values":[{"variable_id":301,"value":"Northwind Traders"},{"variable_id":302,"value":"Business"}]}'import os, requests
r = requests.post(
"https://api.stickyprompts.com/api/v1/run/prompt/42/variables",
headers={"Authorization": f"Bearer {os.environ['STICKY_API_KEY']}"},
json={"variable_values": [
{"variable_id": 301, "value": "Northwind Traders"},
{"variable_id": 302, "value": "Business"},
]},
timeout=300,
)
run = r.json()
print(run["run_status"], run["result"])const run = await fetch("https://api.stickyprompts.com/api/v1/run/prompt/42/variables", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.STICKY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
variable_values: [
{ variable_id: 301, value: "Northwind Traders" },
{ variable_id: 302, value: "Business" },
],
}),
}).then((r) => r.json());
console.log(run.run_status, run.result);Response. 200 with a single RunHistory object.
Errors. 404 {"error":"Prompt not found"}, 400 for a restricted model or a residency or sensitive-data block, 429 on quota, 503 {"error":"..."} on a run failure.
The MCP tool run_prompt does the same job with a map of variable slugs to values; it returns only the text and does not create an interactive conversation.
POST /run
/api/v1/run Sends free text to a model and returns the answer, with no prompt, no variables and no conversation. The call is synchronous. No system prompt, workspace prompt or project instructions are added, the style is fixed to prompty, and nothing is saved as a conversation. Usage is still recorded.
-
promptstring required - The text sent as the single user message.
-
ai_model_iduint optional - Model ID. Send this or
ai_model_name. -
ai_model_namestring optional - Matched against the model’s
versionfield, the provider-level model identifier. Send this orai_model_id.
curl -X POST https://api.stickyprompts.com/api/v1/run \
-H "Authorization: Bearer $STICKY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"Suggest a subject line for a Northwind renewal email.","ai_model_id":12}'import os, requests
r = requests.post(
"https://api.stickyprompts.com/api/v1/run",
headers={"Authorization": f"Bearer {os.environ['STICKY_API_KEY']}"},
json={"prompt": "Suggest a subject line for a Northwind renewal email.", "ai_model_id": 12},
timeout=300,
)
r.raise_for_status()
print(r.json()["text"]) # raw Markdownconst msg = await fetch("https://api.stickyprompts.com/api/v1/run", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.STICKY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ prompt: "Suggest a subject line for a Northwind renewal email.", ai_model_id: 12 }),
}).then((r) => r.json());
console.log(msg.text); // raw MarkdownResponse. 200 with a Message object that is not saved: its ID is 0 and its timestamps are zero. text holds the model’s raw Markdown, unlike the run endpoints, which return HTML.
{
"ID": 0, "CreatedAt": "0001-01-01T00:00:00Z", "UpdatedAt": "0001-01-01T00:00:00Z", "DeletedAt": null,
"conversation_context_id": null,
"ai_model_id": 12,
"text": "Your Northwind plan renews soon - here is what is new",
"isUser": false,
"error_text": null, "error_id": null,
"ai_style": "prompty",
"status": "finished",
"type": "normal"
}
Errors. 404 {"error":"AI model not found"}, 400 for a restricted model or a residency or sensitive-data block, 429 on quota, 500 {"error":"..."} when the model call fails (this endpoint uses 500, not 503).
GET /prompt/:id/run_history
/api/v1/prompt/:id/run_history Returns your Prompt Studio run history for a prompt, newest conversation first. Only conversations you started with is_prompt_studio: true are included.
-
idinteger path required - Numeric prompt ID. The prompt must be yours, shared with you, or a catalogue prompt.
-
pageinteger query optional default1 - Page number. The pagination headers count conversations.
-
sizeinteger query optional default10 - Conversations per page.
Response. 200 with an array of arrays of RunHistory objects: one inner array per conversation, with several items when that run compared models. variables is filled in here. [] when there is nothing.
Prompt files
Files attached to the prompt itself. OpenAI-protocol models use them through file search; Together models get them as an extra user message; for other providers, how they are used is not documented. Upload files first with POST /file.
POST /prompt/:id/file
/api/v1/prompt/:id/file Links uploaded files to a prompt. Files linked earlier stay linked.
-
idinteger path required - Numeric prompt ID.
-
file_idsuint[] required - IDs of uploaded files.
Response. 200 with the Prompt object; file_data lists only the files added in this call.
Errors. 400 {"error":"Invalid request body"}, 400 {"error":"Invalid prompt ID"}, 404 {"error":"Prompt not found"}, 400 {"error":"File not found"}.
GET /prompt/:id/file
/api/v1/prompt/:id/file Returns an array of File objects linked to the prompt. 400 if id is not numeric.
-
idinteger path required - Numeric prompt ID.
DELETE /prompt/:id/file/:fileId
/api/v1/prompt/:id/file/:fileId Unlinks a file from one of your own prompts. If no other prompt or message uses the file, the stored file is deleted too.
-
idinteger path required - Numeric prompt ID.
-
fileIdinteger path required - File ID.
Response. 204. Errors. 404 {"error":"Prompt not found"}, 403 {"error":"You do not have permission to delete this file from the prompt"}, 404 {"error":"File not found"}, 502 {"error":"Failed to delete file"}.
Sharing with individual users
GET /share
/api/v1/share Returns prompts shared with you, newest first.
-
scopestring query optional defaultall user(shared directly with you),workspace(shared with your workspace) orall.-
searchstring query optional - Filter by text.
-
pageinteger query optional default1 - Page number.
-
sizeinteger query optional default10 - Items per page.
Response. 200 with an array of Prompt objects, each with a shared_by object. scope=workspace without a workspace returns []; an empty result in the other scopes is null, not [].
[{ "ID": 42, "name": "Renewal check-in email", "...": "...",
"shared_by": { "email": "john.smith@northwind.example", "first_name": "John", "last_name": "Smith" } }]
Errors. 400 {"error":"Invalid scope parameter"}.
GET /prompt/:id/share
/api/v1/prompt/:id/share Lists the users a prompt is shared with. Each Share has a full user object.
-
idinteger path required - Numeric prompt ID.
[{ "ID": 55, "CreatedAt": "...", "UpdatedAt": "...", "DeletedAt": null,
"user_id": 19, "user": { "ID": 19, "email": "anna@northwind.example", "first_name": "Anna", "status": "active", "...": "..." },
"prompt_id": 42, "read_only": true }]
POST /share/:id
/api/v1/share/:id Shares prompt :id read-only with a user, by email. If no account exists for that email, the server creates an invited account and sends an invitation email; an already-invited user gets the invitation again.
-
idinteger path required - Prompt ID.
-
emailstring required - The recipient’s email address.
Response. 200 with an empty body. Errors. 404 {"error":"prompt not found"}, 400 {"error":"invalid email format"}, 400 {"error":"share already exists"}, 400 {"error":"..."} when the invitation fails.
POST /share/:id/resend
/api/v1/share/:id/resend Sends the invitation email again to a user who has not registered yet.
-
idinteger path required - The prompt ID.
-
emailstring required - The invited address.
Response. 204. Errors. 400 {"error":"invalid email format"}, 404 {"error":"prompt not found"}, 404 {"error":"user not found"}, 400 {"error":"user not invited or already registered"}, 404 {"error":"share not found"}.
DELETE /share/:id
/api/v1/share/:id Removes a share and cancels any pending invitations of that user.
-
idinteger path required - The share ID (the
IDfromGET /prompt/:id/share), not the prompt ID.
Response. 204. Errors. 404 {"error":"share not found"}.
Related
- Conversations and messages - continue a run’s conversation and stream replies.
- Prompt library and Prompt variables - the same features in the app.
- MCP tool reference -
list_prompts,get_prompt,run_prompt,custom_run.