Skip to main content

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 like system__<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

TermWhat it is
id (agent)The agent identifier you pass to agentExecution. Returned by the list query.
conversationIdIdentifier for a running or finished conversation. Returned by every mutation that starts work.
progressA Float from 0.0 to 1.0. 1.0 means the agent finished that message.
threadThe full conversation — every message in order, with prompts and responses.
executionModeHow 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:

HeaderValue
x-api-keyYour API key.
clientidYour client id.
AuthorizationBearer <access_token> (token must include the ai:gensearch scope).
Content-Typeapplication/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:

  • response is null until generation starts. Always guard for it before reading progress or markdown.
  • 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:

  1. One GenSearchConversationCreate with the new conversationId.
  2. Many GenSearchResponseMarkdownAppend events, each carrying a chunk.
  3. One final GenSearchResponseMarkdownSet with 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.