Skip to main content

Code Examples

for using the Parabol API

Our space-voyaging lady and her octopus pal reviewing Parabol API usage examples

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.