Code Examples
for using the Parabol API

Real, runnable examples against the Parabol API — starting with a one-line health check and ending with a bot that writes your retro for you. All examples assume your token is in the PARABOL_PAT environment variable.
1. Hello, Parabol (curl)
Scopes: users:read, teams:read
Who am I, and what teams am I on?
ċurl -s https://action.parabol.co/graphql \
-H "Authorization: Bearer $PARABOL_PAT" \
-H "Content-Type: application/json" \
-d '{"query": "{ viewer { id name email teams { id name } } }"}'
2. Create a task (Node.js)
Scopes: tasks:write
Push work into Parabol from anywhere — a support ticket, a monitoring alert, a Slack workflow. Two Parabol-isms to know: rich-text content fields are stringified TipTap JSON documents, and teamMemberId is ${teamId}::${userId}.
const ENDPOINT = 'https://action.parabol.co/graphql'
// Parabol rich text = a TipTap JSON document, passed as a string
const doc = (text) =>
JSON.stringify({
type: 'doc',
content: [{type: 'paragraph', content: [{type: 'text', text}]}]
})
const gql = async (query, variables) => {
const res = await fetch(ENDPOINT, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PARABOL_PAT}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({query, variables})
})
const {data, errors} = await res.json()
if (errors) throw new Error(errors[0].message)
return data
}
const {createTask} = await gql(
`mutation CreateTask($newTask: CreateTaskInput!) {
createTask(newTask: $newTask) {
task { id content status teamId }
error { message }
}
}`,
{
newTask: {
teamId: 'team123',
userId: 'user456',
teamMemberId: 'team123::user456',
status: 'active', // active | stuck | done | future
content: doc('Update the API documentation')
}
}
)
if (createTask.error) throw new Error(createTask.error.message)
console.log('Created task', createTask.task.id)
3. Export retro learnings to CSV (Python)
Scopes: meetings:read, teams:read
Your retrospectives are a longitudinal record of what your team thinks is going well and what isn’t. Pull the most-voted themes from recent retros into a CSV for your quarterly review.
import csv, os, requests
from datetime import datetime, timezone
ENDPOINT = "https://action.parabol.co/graphql"
HEADERS = {"Authorization": f"Bearer {os.environ['PARABOL_PAT']}"}
QUERY = """
query ExportRetros($teamIds: [ID!]!, $before: DateTime!) {
viewer {
meetings(first: 20, teamIds: $teamIds, meetingTypes: [retrospective], before: $before) {
edges {
node {
id
name
createdAt
... on RetrospectiveMeeting {
reflectionGroups(sortBy: voteCount) {
title
voteCount
reflections { plaintextContent }
}
}
}
}
}
}
}
"""
resp = requests.post(ENDPOINT, headers=HEADERS, json={
"query": QUERY,
"variables": {
"teamIds": ["team123"],
"before": datetime.now(timezone.utc).isoformat(),
},
})
resp.raise_for_status()
payload = resp.json()
if "errors" in payload:
raise SystemExit(payload["errors"][0]["message"])
with open("retro_themes.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["meeting", "date", "theme", "votes", "reflections"])
for edge in payload["data"]["viewer"]["meetings"]["edges"]:
meeting = edge["node"]
for group in meeting.get("reflectionGroups", []):
writer.writerow([
meeting["name"],
meeting["createdAt"][:10],
group["title"] or "(untitled)",
group["voteCount"],
" | ".join(r["plaintextContent"] for r in group["reflections"]),
])
print("Wrote retro_themes.csv")
4. Start a retro and seed it with reflections (Node.js)
Scopes: meetings:write (implies meetings:read)
The full loop: kick off a retrospective from code, find the prompt columns, and pre-populate reflections — say, from your changelog, incident reports, or support themes — so the meeting starts with the facts already on the board.
// helpers `gql` and `doc` from example 2
// 1. Start a retrospective for the team
const {startRetrospective} = await gql(
`mutation StartRetro($teamId: ID!) {
startRetrospective(teamId: $teamId) {
meeting {
id
name
phases {
phaseType
... on ReflectPhase {
reflectPrompts { id question }
}
}
}
error { message }
}
}`,
{teamId: 'team123'}
)
if (startRetrospective.error) throw new Error(startRetrospective.error.message)
const {meeting} = startRetrospective
// 2. Find the "What went well?" style prompt column
const reflectPhase = meeting.phases.find((p) => p.phaseType === 'reflect')
const prompt = reflectPhase.reflectPrompts[0]
// 3. Seed reflections gathered by your own tooling
const highlights = [
'Shipped the new onboarding flow on time',
'Zero pages during the database migration'
]
for (const [i, text] of highlights.entries()) {
const {createReflection} = await gql(
`mutation CreateReflection($input: CreateReflectionInput!) {
createReflection(input: $input) {
reflectionGroup { id }
error { message }
}
}`,
{input: {meetingId: meeting.id, promptId: prompt.id, content: doc(text), sortOrder: i}}
)
if (createReflection.error) throw new Error(createReflection.error.message)
}
console.log(`Seeded ${highlights.length} reflections into "${meeting.name}"`)
Reflections can only be added during the reflect phase of an active retro, and reflection content is capped at 2,000 characters.
5. Go further: an AI retro scribe
For a production-grade version of example 4, see retro-reflect-bot — our open-source reference app. It gathers sprint notes and GitHub activity, has Claude draft and critique reflection cards, and submits them to your retro with a scoped meetings:write token. Fork it, point it at your own data sources, and your next retro writes itself.
Building an agent instead? Hand it llms-full.txt — a complete, worked reference designed to be read by LLMs.