Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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