Projects & Tickets
Projects group related work under a short key (e.g. BACK, WEB). Tickets
represent individual tasks, bugs, or features within a project. This guide
covers creating, listing, updating, and managing both.
1. Prerequisites
You need a running Alloy server and a registered user. Set these shell variables for the examples below:
BASE_URL="http://localhost:3000"
Register a user and capture the token:
curl -s -X POST "$BASE_URL/api/v1/auth/register" \
-H "Content-Type: application/json" \
-d '{
"email": "guide-pt@alloy.dev",
"password": "guide-pt-pass1",
"display_name": "PT Guide User"
}' | jq .
{
"user_id": "...",
"email": "guide-pt@alloy.dev",
"display_name": "PT Guide User",
"access_token": "..."
}
Save the token and user ID:
TOKEN="<access_token from above>"
USER_ID="<user_id from above>"
Create an organization:
curl -s -X POST "$BASE_URL/api/v1/orgs" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"name": "Guide Org",
"slug": "guide-pt-org"
}' | jq .
{
"id": "...",
"name": "Guide Org",
"slug": "guide-pt-org"
}
ORG_ID="<id from above>"
2. Create a Project
Every project belongs to an organization and has a unique short key used in
ticket references (e.g. BACK-1, WEB-42).
curl -s -X POST "$BASE_URL/api/v1/projects" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"org_id\": \"$ORG_ID\",
\"key\": \"GUIDE\",
\"name\": \"Guide Project\",
\"description\": \"A project for the guide walkthrough\"
}" | jq .
{
"id": "...",
"org_id": "...",
"key": "GUIDE",
"name": "Guide Project",
"description": "A project for the guide walkthrough",
"ticket_counter": 0
}
Save the project ID:
PROJECT_ID="<id from above>"
CLI shortcut:
alloy project create --org-id "$ORG_ID" --key GUIDE --name "Guide Project"
3. List Projects
List all projects in your organization:
curl -s "$BASE_URL/api/v1/projects?org_id=$ORG_ID" \
-H "Authorization: Bearer $TOKEN" | jq .
{
"items": [
{
"id": "...",
"org_id": "...",
"key": "GUIDE",
"name": "Guide Project"
}
],
"next_cursor": null,
"has_more": false
}
Use limit and cursor for pagination:
curl -s "$BASE_URL/api/v1/projects?org_id=$ORG_ID&limit=5" \
-H "Authorization: Bearer $TOKEN" | jq .
{
"items": [
{
"id": "...",
"key": "GUIDE",
"name": "Guide Project"
}
],
"next_cursor": null,
"has_more": false
}
CLI shortcut:
alloy project list --org-id "$ORG_ID"
4. Get & Look Up Projects
Fetch a project by ID:
curl -s "$BASE_URL/api/v1/projects/$PROJECT_ID" \
-H "Authorization: Bearer $TOKEN" | jq .
{
"id": "...",
"org_id": "...",
"key": "GUIDE",
"name": "Guide Project",
"description": "A project for the guide walkthrough"
}
Or look it up by key:
curl -s "$BASE_URL/api/v1/projects/key/GUIDE?org_id=$ORG_ID" \
-H "Authorization: Bearer $TOKEN" | jq .
{
"id": "...",
"key": "GUIDE",
"name": "Guide Project"
}
CLI shortcut:
alloy project get "$PROJECT_ID"
5. Update a Project
Rename a project or change its description with a PATCH request. Only include the fields you want to change:
curl -s -X PATCH "$BASE_URL/api/v1/projects/$PROJECT_ID" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"name": "Guide Project (Updated)", "description": "Updated description"}' | jq .
{
"id": "...",
"key": "GUIDE",
"name": "Guide Project (Updated)",
"description": "Updated description"
}
CLI shortcut:
alloy project update "$PROJECT_ID" --name "Guide Project (Updated)"
6. Create Tickets
Tickets live inside a project. Each ticket gets an auto-incrementing number
combined with the project key (e.g. GUIDE-1):
curl -s -X POST "$BASE_URL/api/v1/projects/$PROJECT_ID/tickets" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"title\": \"Set up CI pipeline\",
\"description\": \"Configure GitHub Actions for the project\",
\"priority\": \"High\",
\"reporter_id\": \"$USER_ID\"
}" | jq .
{
"id": "...",
"project_id": "...",
"ticket_number": 1,
"title": "Set up CI pipeline",
"description": "Configure GitHub Actions for the project",
"status": "Backlog",
"priority": "High",
"reporter_id": "..."
}
Save the ticket ID:
TICKET_ID="<id from above>"
Fetch the ticket back by ID:
curl -s "$BASE_URL/api/v1/tickets/$TICKET_ID" \
-H "Authorization: Bearer $TOKEN" | jq .
{
"id": "...",
"project_id": "...",
"ticket_number": 1,
"title": "Set up CI pipeline",
"status": "Backlog",
"priority": "High"
}
Resolve a ticket by key-number reference (e.g. GUIDE-1):
curl -s "$BASE_URL/api/v1/tickets/resolve?ref=GUIDE-1&org_id=$ORG_ID" \
-H "Authorization: Bearer $TOKEN" | jq .
{
"ticket": {
"id": "...",
"ticket_number": 1,
"title": "Set up CI pipeline"
},
"suggestions": []
}
CLI shortcut:
alloy ticket create --project "$PROJECT_ID" --title "Set up CI pipeline" --priority high alloy ticket get GUIDE-1
7. List & Filter Tickets
Create a second ticket so there is something to filter against:
curl -s -X POST "$BASE_URL/api/v1/projects/$PROJECT_ID/tickets" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"title\": \"Write API docs\",
\"description\": \"Document all REST endpoints\",
\"priority\": \"Medium\",
\"status\": \"Todo\",
\"reporter_id\": \"$USER_ID\"
}" | jq .
{
"id": "...",
"project_id": "...",
"ticket_number": 2,
"title": "Write API docs",
"description": "Document all REST endpoints",
"status": "Todo",
"priority": "Medium",
"reporter_id": "..."
}
List all tickets in a project:
curl -s "$BASE_URL/api/v1/projects/$PROJECT_ID/tickets" \
-H "Authorization: Bearer $TOKEN" | jq .
{
"items": [
{
"id": "...",
"title": "...",
"status": "...",
"priority": "..."
}
],
"next_cursor": null,
"has_more": false
}
Filter by status:
curl -s "$BASE_URL/api/v1/projects/$PROJECT_ID/tickets?status=Backlog" \
-H "Authorization: Bearer $TOKEN" | jq .
{
"items": [
{
"id": "...",
"ticket_number": 1,
"title": "Set up CI pipeline",
"status": "Backlog",
"priority": "High"
}
],
"next_cursor": null,
"has_more": false
}
Filter by priority:
curl -s "$BASE_URL/api/v1/projects/$PROJECT_ID/tickets?priority=Medium" \
-H "Authorization: Bearer $TOKEN" | jq .
{
"items": [
{
"id": "...",
"ticket_number": 2,
"title": "Write API docs",
"status": "Todo",
"priority": "Medium"
}
],
"next_cursor": null,
"has_more": false
}
CLI shortcut:
alloy ticket list --project "$PROJECT_ID" --status backlog
8. Update & Transition Tickets
Update a ticket’s fields:
curl -s -X PATCH "$BASE_URL/api/v1/tickets/$TICKET_ID" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d "{\"priority\": \"Urgent\", \"assignee_id\": \"$USER_ID\"}" | jq .
{
"id": "...",
"ticket_number": 2,
"title": "Write API docs",
"status": "Todo",
"priority": "Urgent",
"assignee_id": "..."
}
Transition a ticket to a new status:
curl -s -X POST "$BASE_URL/api/v1/tickets/$TICKET_ID/transition" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"to_status": "InProgress"}' | jq .
{
"id": "...",
"ticket_number": 2,
"title": "Write API docs",
"status": "InProgress",
"priority": "Urgent"
}
Check which transitions are available from the current status:
curl -s "$BASE_URL/api/v1/tickets/$TICKET_ID/transitions" \
-H "Authorization: Bearer $TOKEN" | jq .
{
"current_status": "InProgress",
"available": [
{
"name": "...",
"category": "..."
}
]
}
CLI shortcut:
alloy ticket update GUIDE-2 --priority urgent --assignee-id "$USER_ID"
Learn More
- Workflows & Statuses — customize ticket lifecycle transitions
- Sprints & Boards — plan and track work in time-boxed iterations
- Labels, Tags & Organization — categorize and filter tickets
- Teams, Roles & Permissions — control who can create and edit tickets
- End-to-End Walkthrough — see projects and tickets in a full workflow