Managing tasks in GitHub Projects (Beta) is powerful — but adding a long list of draft issues manually? Tedious. In this post, I’ll show you how to automate the process using a simple PHP script and GitHub’s GraphQL API.
Whether you're planning a product roadmap, organising feature releases, or just bootstrapping a backlog — this workflow will save you hours.
✅ What You'll Learn
- How to create Draft Issues in GitHub Projects v2 using PHP
- How to use GraphQL for GitHub automation
- How to handle titles, descriptions, and client mutation IDs dynamically
📦 What You Need
- A GitHub Project (Beta) — created in your organisation or personal space.
- 
A GitHub Personal Access Token (PAT) — with at least the projectscope.
- Project Node ID — required for GraphQL mutations.
- PHP CLI with curlenabled.
🎯 Step 1: Get Your Project Node ID
GitHub Projects v2 uses GraphQL Node IDs, not plain numbers like /projects/2.
Use the GitHub GraphQL Explorer and run this query:
query {
  organization(login: "your-org-name") {
    projectsV2(first: 10) {
      nodes {
        id
        number
        title
      }
    }
  }
}
Replace your-org-name with your actual GitHub org.
📝 Copy the id where number matches your project (e.g. 2).
🛠 Step 2: Write the PHP Script
Here’s a full working script to create Draft Issues with title and description:
<?php
$githubToken = 'your_github_token_here';        // 🔐 GitHub PAT with `project` scope
$projectId = 'PVT_your_project_node_id_here';   // 🆔 Project node ID
$features = [
    'Jetstream Team Management',
    'User Authentication',
    'Soft Delete + UUID Support',
    // ... (more features)
    'Priority Support System',
];
$apiUrl = 'https://api.github.com/graphql';
foreach ($features as $index => $title) {
    $body = "This is a placeholder description for **$title**.\n\nFeel free to update the details as needed.";
    $clientMutationId = uniqid("draft_");
    $mutation = <<<GRAPHQL
mutation {
  addProjectV2DraftIssue(input: {
    projectId: "$projectId",
    title: "$title",
    body: "$body",
    clientMutationId: "$clientMutationId"
  }) {
    projectItem {
      id
    }
  }
}
GRAPHQL;
    echo "📝 Creating draft: $title\n";
    $response = githubGraphQL($apiUrl, $githubToken, $mutation);
    if (isset($response['data']['addProjectV2DraftIssue']['projectItem']['id'])) {
        echo "✅ Created: $title\n";
    } else {
        echo "❌ Failed: $title\n";
        print_r($response['errors'] ?? []);
    }
}
function githubGraphQL($url, $token, $query)
{
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['query' => $query]));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        "Authorization: Bearer $token",
        "Content-Type: application/json",
        "User-Agent: PHP Script"
    ]);
    $response = curl_exec($ch);
    curl_close($ch);
    return json_decode($response, true);
}
🚀 How to Run
- Save the file as create-draft-issues.php
- Replace:
- 
your_github_token_herewith your actual PAT
- 
PVT_your_project_node_id_herewith your actual Project ID- Run:
 
php create-draft-issues.php
💡 Use Cases
- 📋 Populate your GitHub project with your entire product backlog
- 🧪 Convert a spreadsheet of features into tasks
- 🔁 Use it in a CI/CD pipeline to bootstrap project boards
🔄 What’s Next?
Want to go further? You could:
- Parse CSV/Excel files to create issues dynamically
- Assign owners using assigneeIds
- Add custom fields (like “Feature Type”) via updateProjectV2ItemFieldValue
If you're building tools that interact with GitHub Projects at scale, GraphQL is the way to go.
🧵 Final Thoughts
Automation doesn’t have to be complex. This PHP-based approach makes it super easy to scaffold a GitHub Project in minutes.
If you’re managing your projects manually, this is your sign to automate it and focus more on shipping — not clicking.
✉️ Have any questions or want to see the Excel integration version next? Let me know!
Photo by Brands&People on Unsplash
 
 
              
 
    
Top comments (0)