Workflow Agents
AI Workflow Agents are Agents you run by id. Automate research, drafting, and data extraction
across AlphaSense or your internal content, and move from request to finished deliverable with
minimal manual effort. AlphaSense's Agents are tailored to conduct hyperspecific, expert-level
research—no prompts necessary. Additionally, you can create your own Workflow Agents and run them
through the API.
Two kinds:
SYSTEM— pre-built AlphaSense templates like Company Primer and Bull & Bear Debates. Ids look likesystem__<24-hex>.USER_CUSTOM— agents you've saved yourself. Plain<24-hex>ids, no prefix.
Running an agent creates a conversation: a thread of one or more messages that you can follow up on. Most agents ask a clarifying question on the first turn ("Which company would you like me to research?") — you answer it as a follow-up, and the agent produces the real output on turn two.
This guide assumes you've already gone through Quick Start and have your API key, client id, and an access token ready.
Concepts at a glance
| Term | What it is |
|---|---|
id (agent) | The agent identifier you pass to agentExecution. Returned by the list query. |
conversationId | Identifier for a running or finished conversation. Returned by every mutation that starts work. |
progress | A Float from 0.0 to 1.0. 1.0 means the agent finished that message. |
thread | The full conversation — every message in order, with prompts and responses. |
executionMode | How much work an agent does per turn: AUTO, THINK_LONGER, DEEP_RESEARCH. Fixed by the agent, drives the credit cost. |
You'll use four operations: aiWorkflows.workflows to find an agent, genSearch.agentExecution to
run it, genSearch.auto to answer the follow up, and genSearch.thread to read the conversation as
it progresses and after it finishes.
Headers
Every request needs:
| Header | Value |
|---|---|
x-api-key | Your API key. |
clientid | Your client id. |
Authorization | Bearer <access_token> (token must include the ai:gensearch scope). |
Content-Type | application/json |
All examples below post to https://api.alpha-sense.com/gql.
1. List agents
import os, requests
ENDPOINT = "https://api.alpha-sense.com/gql"
headers = {
"x-api-key": os.environ["ALPHASENSE_API_KEY"],
"clientid": os.environ["ALPHASENSE_CLIENT_ID"],
"Authorization": f"Bearer {os.environ['ALPHASENSE_ACCESS_TOKEN']}",
"Content-Type": "application/json",
}
LIST_AGENTS = """
query ListAgents($limit: Int, $offset: Int) {
aiWorkflows {
workflows(limit: $limit, offset: $offset) {
data { id name workflowType executionMode }
hasMore
}
}
}"""
resp = requests.post(ENDPOINT, headers=headers,
json={"query": LIST_AGENTS, "variables": {"limit": 50, "offset": 0}})
for agent in resp.json()["data"]["aiWorkflows"]["workflows"]["data"]:
print(agent["id"], agent["executionMode"], agent["name"])
Page with limit/offset until hasMore: false. To narrow the list, pass a filter with
workflowTypes: [SYSTEM] or [USER_CUSTOM].
2. Run an agent
Pick an id from step 1 and fire the agent mutation. It returns a conversationId immediately and
starts the agent running on the server.
RUN_AGENT = """
mutation RunAgent($input: GenSearchAgentInput!) {
genSearch { agentExecution(input: $input) { id } }
}"""
# Example: Company Primer (a SYSTEM agent)
agent_id = "system__1234xxx"
resp = requests.post(ENDPOINT, headers=headers,
json={"query": RUN_AGENT, "variables": {"input": {"id": agent_id}}})
conversation_id = resp.json()["data"]["genSearch"]["agentExecution"]["id"]
To watch the agent finish, query genSearch.thread and poll until the last message reports
progress = 1.0:
import time
THREAD = """
query Thread($id: String!) {
genSearch {
thread(id: $id) {
id
messages {
id
request { prompt }
response {
markdown
progress
error { code }
}
}
}
}
}"""
while True:
resp = requests.post(ENDPOINT, headers=headers,
json={"query": THREAD, "variables": {"id": conversation_id}})
thread = resp.json()["data"]["genSearch"]["thread"]
latest = thread["messages"][-1] if thread["messages"] else None
response = (latest or {}).get("response") or {}
if response.get("error"):
raise RuntimeError(response["error"]["code"])
if response.get("progress") == 1.0:
print(response["markdown"])
break
time.sleep(2)
A few things worth knowing:
responseisnulluntil generation starts. Always guard for it before readingprogressormarkdown.- The first turn is usually a clarifying question. Company Primer will respond with something like "Which company would you like me to research?" — that's correct behavior. You answer it in step 3.
Streaming alternative
If you want chunks token-by-token instead of polling, use the genSearchExecuteAgent subscription.
It's delivered as multipart/mixed and emits, in order:
- One
GenSearchConversationCreatewith the newconversationId. - Many
GenSearchResponseMarkdownAppendevents, each carrying a chunk. - One final
GenSearchResponseMarkdownSetwith the complete markdown (treat as authoritative — it replaces what you accumulated from the appends).
subscription RunAgent($input: GenSearchAgentInput!) {
genSearchExecuteAgent(input: $input) {
deltas {
__typename
... on GenSearchConversationCreate {
conversation {
id
}
}
... on GenSearchResponseMarkdownAppend {
value
}
... on GenSearchResponseMarkdownSet {
value
}
}
}
}
Add Accept: multipart/mixed;subscriptionSpec=1.0 to the request headers. See
Streaming for the multipart parser. Pick mutation or subscription — each
is a fresh run and consumes credits independently.
3. Follow up
To answer the agent's clarifying question (or to ask anything else in the same thread), use
genSearch.auto with the conversationId from step 2. The agent's saved system prompt carries
through, so a one-word answer like "Apple Inc." can trigger a full report.
ANSWER = """
mutation Answer($input: GenSearchInput!) {
genSearch { auto(input: $input) { id } }
}"""
resp = requests.post(ENDPOINT, headers=headers, json={
"query": ANSWER,
"variables": {
"input": {"prompt": "Apple Inc.", "conversationId": conversation_id},
},
})
Poll thread again the same way to get the next part of the message. The same payload gives you
both turns (the original question and the new answer), so when polling finishes you can render the
whole conversation directly. Follow up as many times as you want — every call appends a new message.