GraphQL API
Query exactly the data you need in a single request. Build complex UIs efficiently with the GraphQL API.
GraphQL API
The TRCR GraphQL API gives you the flexibility to request exactly the data your application needs, reducing over-fetching and enabling efficient data loading for complex UIs.
POST https://api.trcr.pro/graphqlAuthentication
Include a Bearer token in the Authorization header, the same as with the REST API:
POST /graphql HTTP/1.1
Host: api.trcr.pro
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
{
"query": "{ me { id name email } }"
}Making Requests
curl -X POST https://api.trcr.pro/graphql \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query":"{ me { id name email } }"}'Schema Overview
The schema exposes queries for reading data and mutations for writing data. All types use the same IDs as the REST API.
Queries
User
# Current authenticated user
query {
me {
id
name
email
avatar_url
created_at
}
}Organizations
# List organizations
query {
organizations {
id
name
slug
settings {
timezone
currency
}
members {
id
user { id name email }
role
}
}
}
# Single organization
query {
organization(id: "org_abc123") {
id
name
members { id role }
}
}Projects
# List projects with filters
query {
projects(status: "active", page: 1, perPage: 10) {
data {
id
name
description
color
billable
hourly_rate
client { id name }
members { id user { name } role }
task_count
total_hours
}
pagination {
total
page
per_page
total_pages
}
}
}
# Single project with related data
query {
project(id: "prj_abc123") {
id
name
tasks(status: "open") {
id
title
status
assignee { name }
}
time_entries(from: "2026-03-01", to: "2026-03-31") {
id
description
duration
user { name }
}
}
}Tasks
# List tasks with nested data
query {
tasks(
projectId: "prj_abc"
status: "open"
sort: "priority"
order: "desc"
page: 1
perPage: 25
) {
data {
id
title
description
status
priority
start_date
due_date
estimated_hours
assignee { id name avatar_url }
labels { id name color }
comments_count
progress
complexity
group_id
checklists {
id
title
items { text checked }
}
dependencies { id title status }
time_entries {
id
duration
user { name }
}
created_at
updated_at
}
pagination { total page per_page total_pages }
}
}
# Single task
query {
task(id: "tsk_abc123") {
id
title
description
comments {
id
body
user { name avatar_url }
created_at
}
}
}
# All task dependencies in an organization (one call for the whole Gantt).
# Returns only edges where both endpoint tasks are visible to the caller
# (admins see all; members see edges within their accessible projects).
query {
allTaskDependencies(orgId: "org_abc123") {
id
taskId
dependsOnId
dependencyType # blocks | blocked_by
createdAt
}
}Time Entries
# List time entries
query {
timeEntries(
from: "2026-03-01"
to: "2026-03-31"
projectId: "prj_abc"
) {
data {
id
description
start_time
end_time
duration
billable
user { id name }
project { id name color }
task { id title }
}
pagination { total page }
}
}
# Currently running timer
query {
runningTimer {
id
description
start_time
project { id name }
task { id title }
}
}Invoices
# List invoices
query {
invoices(status: "sent", clientId: "cli_abc") {
data {
id
number
status
issue_date
due_date
subtotal
tax_amount
total
currency
client { id name email }
line_items {
id
description
quantity
unit_price
total
}
payments {
id
amount
method
paid_at
}
}
pagination { total page }
}
}Clients
# List clients with summaries
query {
clients(search: "acme") {
data {
id
name
email
phone
currency
hourly_rate
contacts { id name email role }
projects_count
total_invoiced
total_paid
outstanding_balance
}
pagination { total page }
}
}Reports
# Timesheet report
query {
timesheetReport(from: "2026-03-01", to: "2026-03-31", groupBy: "week") {
rows {
period
user { id name }
hours
billable_hours
}
totals {
hours
billable_hours
}
}
}
# Revenue report
query {
revenueReport(from: "2026-01-01", to: "2026-12-31", groupBy: "month") {
rows {
period
invoiced
paid
outstanding
}
totals { invoiced paid outstanding }
}
}
# Utilization report
query {
utilizationReport(from: "2026-03-01", to: "2026-03-31") {
rows {
user { id name }
tracked_hours
available_hours
utilization_percent
}
}
}
# Profitability report
query {
profitabilityReport(from: "2026-01-01", to: "2026-03-31", groupBy: "project") {
rows {
entity { id name }
revenue
cost
profit
margin_percent
}
}
}Task Checklists
# List checklist items for a task
query {
taskChecklists(orgId: "org_abc123", taskId: "tsk_abc123") {
id
task_id
title
completed
position
created_at
}
}Dashboard Summary
# Dashboard summary for an organization (scoped to current user)
query {
dashboardSummary(orgId: "org_abc123") {
total_tasks
done_tasks
overdue_count
overdue_urgent_tasks {
id
title
status
priority
due_date
project_id
assignee_id
}
}
}Project Stats
# Task counts per project for an organization
query {
projectStats(orgId: "org_abc123") {
project_id
total
done
}
}Sidebar Collapsed Nodes
# List sidebar node IDs that are collapsed for the current user
query {
sidebarCollapsedNodes(orgId: "org_abc123")
}Task Groups
# List task groups for a project
query {
taskGroups(orgId: "org_abc123", projectId: "prj_abc123") {
id
name
is_default
position
created_at
}
}
# Get collapsed task group IDs for the current user
query {
collapsedTaskGroups(orgId: "org_abc123", projectId: "prj_abc123")
}Task Statuses
# List custom task statuses for an organization
query {
taskStatuses(orgId: "org_abc123") {
id
code
label
color
position
is_terminal
supports_progress
progress_editable
on_complete
next_status_code
is_default
}
}Task Recurrences
# Get the recurrence rule for a task
query {
taskRecurrence(orgId: "org_abc123", taskId: "tsk_abc123") {
id
task_id
recurrence_pattern
next_occurrence_date
last_generated_date
stopped_at
}
}Milestones
# List a Space's milestones (ordered by target date)
query {
milestones(orgId: "org_abc123", projectId: "prj_abc123") {
id
organizationId
projectId
title
targetDate
color
description
createdAt
updatedAt
}
}Search
# Unified search
query {
search(q: "website redesign", type: "task", limit: 10) {
results {
type
id
title
description
score
}
}
}Notifications
query {
notifications(unread: true) {
data {
id
type
title
entity_type
entity_id
read
created_at
}
unread_count
}
}Mutations
Tasks
CreateTaskInput.startDate and UpdateTaskInput.startDate are optional Date fields (formatted YYYY-MM-DD), mirroringdueDate. TaskType.startDate exposes the value on reads. On update, startDate is MaybeUndefined: omit it to leave the value unchanged, or send null to clear it. startDate must be on or before dueDate, otherwise the mutation returns a validation error (start_date must be on or before due_date).
# Create a task
mutation {
createTask(input: {
title: "Implement search"
projectId: "prj_abc"
priority: "high"
assigneeId: "usr_xyz"
startDate: "2026-04-01"
dueDate: "2026-04-15"
labelIds: ["lbl_feature"]
}) {
id
title
status
priority
startDate
dueDate
}
}
# Update a task
mutation {
updateTask(id: "tsk_abc123", input: {
status: "done"
priority: "low"
startDate: "2026-04-02"
}) {
id
status
priority
startDate
dueDate
updated_at
}
}
# Delete a task
mutation {
deleteTask(id: "tsk_abc123") {
success
}
}
# Add a comment
mutation {
addTaskComment(taskId: "tsk_abc123", body: "LGTM! Merging.") {
id
body
user { name }
created_at
}
}Time Entries
# Start a timer
mutation {
startTimer(input: {
projectId: "prj_abc"
description: "Working on search feature"
}) {
id
description
start_time
project { name }
}
}
# Stop the running timer
mutation {
stopTimer {
id
description
start_time
end_time
duration
}
}
# Create a manual time entry
mutation {
createTimeEntry(input: {
projectId: "prj_abc"
description: "Client meeting"
startTime: "2026-03-28T09:00:00Z"
endTime: "2026-03-28T10:30:00Z"
billable: true
}) {
id
duration
billable
}
}
# Update a time entry
mutation {
updateTimeEntry(id: "te_abc123", input: {
description: "Updated description"
billable: false
}) {
id
description
billable
}
}
# Delete a time entry
mutation {
deleteTimeEntry(id: "te_abc123") {
success
}
}Projects
# Create a project
mutation {
createProject(input: {
name: "Website Redesign"
clientId: "cli_abc"
billable: true
hourlyRate: 15000
color: "#6366f1"
}) {
id
name
color
}
}
# Update a project
mutation {
updateProject(id: "prj_abc123", input: {
status: "archived"
}) {
id
status
}
}
# Add a member
mutation {
addProjectMember(projectId: "prj_abc", userId: "usr_xyz", role: "member") {
id
user { name }
role
}
}
# Remove a member
mutation {
removeProjectMember(projectId: "prj_abc", userId: "usr_xyz") {
success
}
}Invoices
# Create an invoice
mutation {
createInvoice(input: {
clientId: "cli_abc"
dueDate: "2026-04-30"
taxRate: 21
}) {
id
number
status
total
}
}
# Add a line item
mutation {
addInvoiceLineItem(invoiceId: "inv_abc", input: {
description: "Frontend development"
quantity: 10.5
unitPrice: 15000
}) {
id
description
total
}
}
# Generate from time entries
mutation {
generateInvoiceLines(invoiceId: "inv_abc", from: "2026-03-01", to: "2026-03-31") {
line_items {
id
description
quantity
unit_price
}
total
}
}
# Send invoice
mutation {
sendInvoice(id: "inv_abc", message: "Please find attached.") {
id
status
sent_at
}
}
# Mark as paid
mutation {
markInvoicePaid(id: "inv_abc", paymentMethod: "bank_transfer") {
id
status
paid_at
}
}Clients
# Create a client
mutation {
createClient(input: {
name: "Acme Corp"
email: "billing@acme.com"
currency: "USD"
hourlyRate: 15000
}) {
id
name
}
}
# Update a client
mutation {
updateClient(id: "cli_abc", input: {
hourlyRate: 17500
}) {
id
hourly_rate
}
}
# Add a contact
mutation {
addClientContact(clientId: "cli_abc", input: {
name: "John Smith"
email: "john@acme.com"
role: "CTO"
}) {
id
name
email
}
}Organizations and Members
# Create organization
mutation {
createOrganization(input: { name: "Acme Corp" }) {
id
name
slug
}
}
# Invite a member
mutation {
inviteMember(email: "newuser@example.com", role: "member") {
id
email
role
status
}
}
# Update member role
mutation {
updateMemberRole(memberId: "mem_abc", role: "admin") {
id
role
}
}
# Remove member
mutation {
removeMember(memberId: "mem_abc") {
success
}
}Checklists
# Delete a checklist item (requires manage_tasks permission)
mutation {
deleteChecklist(orgId: "org_abc123", itemId: "item_abc123")
}Task Groups
# Create a task group
mutation {
createTaskGroup(orgId: "org_abc123", projectId: "prj_abc123", input: {
name: "Backlog"
}) {
id
name
position
}
}
# Update a task group
mutation {
updateTaskGroup(orgId: "org_abc123", projectId: "prj_abc123", groupId: "grp_001", input: {
name: "Sprint Backlog"
}) {
id
name
}
}
# Delete a task group
mutation {
deleteTaskGroup(orgId: "org_abc123", projectId: "prj_abc123", groupId: "grp_001") {
success
}
}
# Reorder task groups
mutation {
reorderTaskGroups(orgId: "org_abc123", projectId: "prj_abc123", order: ["grp_002", "grp_001"]) {
success
}
}
# Toggle collapsed state for a task group
mutation {
toggleCollapsedTaskGroup(orgId: "org_abc123", projectId: "prj_abc123", groupId: "grp_001")
}Task Statuses
# Create a custom task status
mutation {
createTaskStatus(orgId: "org_abc123", input: {
code: "qa_review"
label: "QA Review"
color: "#8b5cf6"
supportsProgress: true
onComplete: "auto_advance"
nextStatusCode: "done"
}) {
id
code
label
color
position
}
}
# Update a task status
mutation {
updateTaskStatus(orgId: "org_abc123", id: "ts_abc123", input: {
label: "Code Review"
color: "#6366f1"
}) {
id
label
color
}
}
# Delete a task status
mutation {
deleteTaskStatus(orgId: "org_abc123", id: "ts_abc123") {
success
}
}
# Reorder task statuses
mutation {
reorderTaskStatuses(orgId: "org_abc123", order: ["ts_001", "ts_002", "ts_003"]) {
success
}
}Task Recurrences
# Create a recurrence rule for a task
mutation {
createTaskRecurrence(orgId: "org_abc123", taskId: "tsk_abc123", input: {
recurrencePattern: "weekly"
}) {
id
recurrence_pattern
next_occurrence_date
}
}
# Update a recurrence rule
mutation {
updateTaskRecurrence(orgId: "org_abc123", recurrenceId: "rec_abc123", input: {
recurrencePattern: "monthly"
}) {
id
recurrence_pattern
}
}
# Stop a recurrence
mutation {
deleteTaskRecurrence(orgId: "org_abc123", recurrenceId: "rec_abc123") {
success
}
}Sidebar
# Toggle a sidebar node's collapsed state
# Returns true if the node is now collapsed, false if expanded
mutation {
toggleSidebarCollapsedNode(orgId: "org_abc123", nodeId: "prj_abc123")
}Labels
mutation {
createLabel(input: { name: "Bug", color: "#ef4444" }) {
id
name
color
}
}
mutation {
updateLabel(id: "lbl_abc", input: { color: "#f97316" }) {
id
color
}
}
mutation {
deleteLabel(id: "lbl_abc") {
success
}
}Milestones
CreateMilestoneInput takes projectId, title, and a targetDate (a Date formatted YYYY-MM-DD), plus an optional color (hex, defaults to #6366f1) and description. On UpdateMilestoneInput every field is optional;description is MaybeUndefined: omit it to leave the value unchanged, or send null to clear it. Reads require access to the Space; create, update, and delete require manage_tasks permission on the Space.
# Create a milestone
mutation {
createMilestone(orgId: "org_abc123", input: {
projectId: "prj_abc123"
title: "Public beta"
targetDate: "2026-07-01"
color: "#22c55e"
description: "optional"
}) {
id
projectId
title
targetDate
color
}
}
# Update a milestone
mutation {
updateMilestone(orgId: "org_abc123", id: "mil_abc123", input: {
title: "Public beta launch"
targetDate: "2026-07-08"
}) {
id
title
targetDate
updatedAt
}
}
# Delete a milestone
mutation {
deleteMilestone(orgId: "org_abc123", id: "mil_abc123") {
success
}
}Chat
# Send a message
mutation {
sendChatMessage(channelId: "ch_abc", content: "Deployment complete!") {
id
content
user { name }
created_at
}
}
# Create a channel
mutation {
createChatChannel(input: {
name: "engineering"
isPrivate: false
memberIds: ["usr_abc", "usr_xyz"]
}) {
id
name
}
}Notifications
mutation {
markNotificationsRead(ids: ["ntf_001", "ntf_002"]) {
success
unread_count
}
}
mutation {
updateNotificationPreferences(input: {
emailEnabled: true
taskAssigned: true
chatMentioned: true
}) {
email_enabled
task_assigned
chat_mentioned
}
}Variables
Use variables to keep queries clean and avoid string interpolation:
curl -X POST https://api.trcr.pro/graphql \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation CreateTask($input: CreateTaskInput!) { createTask(input: $input) { id title } }",
"variables": {
"input": {
"title": "New feature",
"projectId": "prj_abc",
"priority": "high"
}
}
}'Error Handling
GraphQL errors are returned in the errors array:
{
"data": null,
"errors": [
{
"message": "Task not found",
"path": ["task"],
"extensions": {
"code": "NOT_FOUND"
}
}
]
}Partial errors are possible -- some fields may resolve while others fail. Always check the errors array even when data is present.
Rate Limits
The GraphQL endpoint is rate-limited to 100 requests per minute per user, the same as REST.
Depth and Complexity Limits
To prevent abuse, queries are limited to:
- Max depth: 10 levels of nesting
- Max complexity: 500 points (each field costs 1 point, list fields cost 10)
- Max aliases: 20 per query
Exceeding these limits returns an error before execution:
{
"errors": [{
"message": "Query complexity 750 exceeds maximum allowed 500",
"extensions": { "code": "QUERY_TOO_COMPLEX" }
}]
}Introspection
Schema introspection is enabled. You can explore the schema using tools like GraphQL Playground, Apollo Studio, or Insomnia by pointing them at https://api.trcr.pro/graphql.
# Introspect the schema
query {
__schema {
types {
name
description
fields { name type { name } }
}
}
}