Overview
ProjectLayer is a project management tool built for development teams. It provides kanban boards, task tracking with filtering, milestones, labels, a backlog with direct SupportLayer integration, reports, email notifications, a full activity audit log, and team management with role-based permissions.
Quick start
- Create an account — Register here. Your first member seat is free.
- Verify your email — Check your inbox for a verification link and click it to activate your account.
- Create a project — Give it a name and a prefix (e.g.
API). This prefix is used in task keys likeAPI-42. - Add tasks — Create tasks with titles, descriptions, priorities, assignees, due dates, and labels.
- Use the board — Every project gets a default Kanban board with To Do, In Progress, and Done columns. Move tasks between columns.
- Create milestones — Set deadlines and track progress toward releases or goals.
- Invite your team — Go to Team Members in the admin sidebar and send email invitations with a chosen role.
Key concepts
- Organization — Your workspace. All projects, members, and settings belong to an organization. A user can belong to multiple organizations.
- Project — Groups related tasks, boards, milestones, labels, and backlog items. Each project has a unique prefix for task keys.
- Task — An individual work item with a key (e.g.
API-42), status, priority, assignee, due date, labels, and comments. - Board — A Kanban view with ordered columns. Tasks are placed in columns and moved between them.
- Milestone — A target date or goal. Assign tasks to a milestone to track progress.
- Label — A color-coded tag for categorizing tasks within a project.
- Backlog — A holding area for items that haven't been promoted to tasks yet. Items can arrive from the API (e.g. SupportLayer) or be converted to tasks.
- Access group — A role that defines what a member can do. Default groups: Administrator, Member, Viewer.
Projects
Projects are the top-level container for all work. Each project has:
- Name and prefix — The prefix (e.g.
WEB) is used in task keys likeWEB-15. - Description — Optional project description visible on the overview page.
- Owner — An optional team member who owns the project.
- Status — Active, Completed, or Archived. Archived projects are hidden from the main list.
From a project's overview page, you can navigate to Tasks, Board, Backlog, Milestones, and Labels using the tabbed sub-navigation.
Projects can be edited or archived from the project overview page.
Tasks & filtering
Tasks are the core work items. Each task includes:
- Task key — Auto-generated from the project prefix (e.g.
API-42). - Status — Open, In Progress, Done, or Closed. Change inline on the task detail page.
- Priority — Low, Medium, High, or Critical.
- Assignee — A team member responsible for the task. Change inline.
- Due date — Optional deadline. Set at creation or change inline.
- Labels — Color-coded tags from the project's label set.
- Description — Detailed information about the work.
- Comments — Threaded discussion on each task.
Filtering tasks
The task list includes a filter toolbar. You can filter by:
- Text search — Matches against task titles.
- Status — Show only tasks in a specific status.
- Priority — Show only tasks of a specific priority.
- Assignee — Show tasks assigned to a specific member, or show unassigned tasks.
Filters are applied via URL query parameters, so filtered views can be bookmarked or shared.
Deleting tasks
Tasks can be permanently deleted from the task detail page. This requires the task.delete permission.
Kanban boards
Every project comes with a default Kanban board containing three columns: To Do, In Progress, and Done.
Tasks can be moved between columns using the move buttons on each task card. Moving a task to a "complete" column (like Done) automatically sets its status to Done.
Tasks not assigned to any column appear in a separate "No Column" section on the board, with buttons to move them into any column.
Milestones
Milestones represent releases, sprints, or goals with optional due dates.
- Create milestones from the Milestones tab within a project.
- Assign tasks to milestones to track progress.
- Open or close milestones to mark them as complete.
- The milestone list shows the number of associated tasks.
Labels
Labels are color-coded tags for categorizing tasks within a project.
- Create labels from the Labels tab — pick a name and color.
- Assign labels to tasks using checkboxes on the task detail page.
- Labels appear as colored pills on both the task list and task detail views.
Backlog
The backlog is a holding area for work items that haven't been promoted to full tasks.
- Items can arrive via the Backlog API (e.g. from SupportLayer).
- Each item shows its source, priority, and a link back to the originating ticket.
- Accept — Mark as approved for future work.
- Reject — Decline the item.
- Convert to task — Create a full task from the backlog item, with the title, description, and priority carried over.
Reports
The Reports page (available to admins) provides an overview of your organization's work:
- Summary stats — Total tasks, overdue count, active tasks, completed tasks.
- Tasks by status — Bar chart showing the distribution of tasks across statuses.
- Tasks by priority — Bar chart showing priority distribution.
- Tasks by project — Count of tasks per project.
- Team workload — Bar chart showing how many open tasks each team member has.
Email notifications
ProjectLayer sends email notifications for key task events. Each user can control their preferences from the Profile & Notifications page.
| Event | Who is notified | Preference |
|---|---|---|
| Task assigned to you | The new assignee | When a task is assigned to me |
| New comment on task | Assignee and reporter (excluding the comment author) | When someone comments on my task |
| Task status changed | Assignee and reporter | When a task status changes |
All notifications include a direct link to the task.
Activity log
Every task has a full activity log visible on its detail page, below the comments section. The log records:
- Task creation (including creation from backlog items)
- Status changes (with old and new values)
- Priority changes
- Assignee changes
- Due date changes
- Comments added
Each entry shows the actor, the action, and the timestamp.
Team management
Manage your organization's members from Admin → Team Members.
- View members — See name, email, role, and status for every member.
- Change roles — Use the role dropdown on any member to change their access group instantly.
- Suspend members — Temporarily revoke access. Suspended members cannot log in to the organization.
- Reactivate members — Restore access for a suspended member.
You cannot suspend the last administrator — at least one admin must remain active.
Inviting members
Invite new members from the Team Members page:
- Enter the invitee's email address.
- Select a role (Administrator, Member, or Viewer).
- Click Send invite.
The invitee receives an email with a link to join. If they already have a ProjectLayer account, they are automatically added to your organization. If not, they are prompted to create an account and are added upon completion.
Invitations expire after 7 days.
Roles & permissions
Every member belongs to one access group within an organization. Three default groups are created with each organization:
| Role | Permissions |
|---|---|
| Administrator | All permissions — full control over projects, tasks, boards, backlog, settings, and user management. |
| Member | View and create projects; full task management (create, edit, assign); board and backlog management. Cannot access settings or manage users. |
| Viewer | View projects and tasks only. Cannot create, edit, or assign. |
Custom access groups can be created from Admin → Access Groups with any combination of permissions:
| Permission | Description |
|---|---|
project.view | View projects |
project.create | Create new projects |
project.edit | Edit project details, manage milestones and labels |
project.delete | Delete projects |
task.view_own | View tasks assigned to you |
task.view_all | View all tasks in a project |
task.create | Create new tasks |
task.edit | Edit task details, move on board |
task.assign | Assign tasks to team members |
task.delete | Delete tasks |
board.manage | Manage board columns |
backlog.manage | Accept, reject, and convert backlog items |
settings.manage | Access admin settings, branding, API keys, reports |
user.manage | Invite, suspend, and manage team members |
Registration
Create an account at the registration page. You'll provide your name, email, password, and organization name. A new organization is created automatically with you as the administrator.
Three default access groups (Administrator, Member, Viewer) are provisioned with your organization.
Email verification
After registration, you'll receive a verification email. Click the link to verify your email address. You must verify before you can sign in.
If you didn't receive the email, use the resend verification page.
Password reset
If you've forgotten your password:
- Go to Forgot password from the sign-in page.
- Enter your email address.
- Check your inbox for a reset link (valid for 1 hour).
- Click the link and set a new password (minimum 8 characters).
Branding
Customize the portal appearance from Admin → Branding:
- Primary colour — Used for the sidebar, buttons, and links. Enter a hex value (e.g.
#3d2f7c). - Accent colour — Used for highlights and secondary actions.
Leave fields empty to use ProjectLayer's default colours. Changes take effect immediately for all members.
Billing
ProjectLayer uses simple per-seat pricing:
- Free plan — 1 member seat included. No credit card required.
- Pro plan — 1 free seat + additional paid seats at $12 USD / £10 GBP / €12 EUR per month.
All features are available on both plans. There are no feature gates. Administrators and viewers do not count toward paid seat limits.
Manage your subscription from Account & Plan. Pro plan customers can manage billing, change payment methods, and adjust seat counts via the Stripe billing portal.
API Authentication
The ProjectLayer API uses Bearer token authentication. Generate API keys from Settings → API Keys in the portal. Download the OpenAPI spec for a complete, machine-readable description of every endpoint.
Include the key in the Authorization header:
Authorization: Bearer pl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
API keys are scoped to your organization. All requests made with a key will only see and modify data within that organization.
Scopes
Each key is created with one of two access levels:
| Scope | Can do |
|---|---|
| Read & write | All GET requests, plus POST/PATCH/PUT/DELETE |
| Read only | Only GET/HEAD/OPTIONS requests |
A request that exceeds a key's scope returns 403 Forbidden:
{
"error": {
"code": "insufficient_scope",
"message": "This API key lacks the 'write' scope."
}
}
Keys created before scopes existed have no scope recorded, which is treated as full (read & write) access.
Base URL
https://your-projectlayer-domain.com/api/v1/
Pagination
Every list endpoint accepts ?page (default 1) and ?per_page (default 25, clamped to 1–100) and returns a meta block alongside data:
{
"data": [ ... ],
"meta": { "page": 1, "per_page": 25, "total": 137, "total_pages": 6 }
}
Error responses
Errors return a JSON object with an error field:
{
"error": {
"code": "authentication_failed",
"message": "Invalid or revoked API key."
}
}
API — Projects
List projects
GET /api/v1/projects
Returns a page of active projects in your organization (see Pagination).
Response:
{
"data": [
{
"id": 1,
"name": "API Platform",
"slug": "api-platform",
"prefix": "API",
"status": "active"
}
],
"meta": { "page": 1, "per_page": 25, "total": 1, "total_pages": 1 }
}
API — Tasks
List tasks
GET /api/v1/tasks
Returns a page of tasks in your organization, most recently updated first (see Pagination).
| Query parameter | Type | Description |
|---|---|---|
has_plan | boolean | When 1 (or true), returns only tasks that have a Claude build plan, most recently planned first. |
project_id | integer | Restrict to a single project. Can be combined with has_plan. An unknown ID returns 404. |
page, per_page | integer | See Pagination. |
Response:
{
"data": [
{
"id": 42,
"task_key": "API-42",
"title": "Add CSV export",
"project_id": 1,
"status": "in_progress",
"priority": "high",
"has_plan": true,
"plan_updated_at": "2026-07-05T13:55:00+00:00"
}
],
"meta": { "page": 1, "per_page": 25, "total": 1, "total_pages": 1 }
}
Get a task
GET /api/v1/tasks/{id}
Returns a single task, including its description and full Claude build plan. claude_plan and plan_updated_at are null when no plan has been generated. Returns 404 if the task is not found in your organization.
Response:
{
"id": 42,
"task_key": "API-42",
"title": "Add CSV export",
"project_id": 1,
"status": "in_progress",
"priority": "high",
"has_plan": true,
"plan_updated_at": "2026-07-05T13:55:00+00:00",
"description": "Export the report as CSV.",
"claude_plan": "1. Add the export endpoint...\n2. Stream rows...\n3. Verify with a functional test."
}
Claude build plans: the claude_plan field holds the development plan generated by the "Plan with Claude" button on a task. Use GET /api/v1/tasks?has_plan=1 to discover which tasks already have a plan, then fetch each one to read it.
Update a task's status
PATCH /api/v1/tasks/{id}/status
Updates the status of a task — for example, to mark it in_progress when work begins or done when it is completed. Automation such as a Claude Code skill can call this to reflect build progress. POST is also accepted for clients that cannot send PATCH.
Request body:
{
"status": "in_progress"
}
| Field | Type | Required | Description |
|---|---|---|---|
status | string | Yes | open, in_progress, done, or closed. |
Setting the status to done or closed stamps the task's completion time; moving it back to open or in_progress clears it. The change is recorded in the task's activity log and notifies the assignee and reporter. An unknown status returns 400; a task not found in your organization returns 404.
Response: the updated task (same shape as Get a task), for example:
{
"id": 42,
"task_key": "API-42",
"title": "Add CSV export",
"project_id": 1,
"status": "in_progress",
"priority": "high",
"has_plan": true,
"plan_updated_at": "2026-07-05T13:55:00+00:00",
"description": "Export the report as CSV.",
"claude_plan": "1. Add the export endpoint..."
}
Create a task
POST /api/v1/tasks
Requires the write scope. Allocates the next task number for the project, the same way the "New task" form does.
| Field | Type | Required | Description |
|---|---|---|---|
project_id | integer | Yes | Target project ID. An unknown ID returns 404. |
title | string | Yes | Task title |
description | string | No | Description |
priority | string | No | low, medium, high, or critical. Default: medium |
assignee_id | integer | No | Must be an active member of your organization |
milestone_id | integer | No | Must belong to the same project |
claude_plan | string | No | Build plan in Markdown. Sets plan_updated_at when provided. |
Response (201 Created): the created task (same shape as Get a task). Invalid input returns 400.
Update a task
PATCH /api/v1/tasks/{id}
Requires the write scope. Partial update: only fields present in the body are changed. Accepts title, description, priority, status, assignee_id, milestone_id, and claude_plan (same rules as create). Setting assignee_id or milestone_id to null clears it. Writing claude_plan sets the task's build plan (Markdown), stamps plan_updated_at, and clears any in-progress or errored planning state; sending null or an empty string clears the plan.
Response: the updated task. Invalid input returns 400; not found in your organization returns 404.
Delete a task
DELETE /api/v1/tasks/{id}
Requires the write scope. Returns 204 No Content on success, 404 if not found in your organization.
API — Comments
List a task's comments
GET /api/v1/tasks/{taskId}/comments
Returns the task's comments, oldest first, paginated (see below). 404 if the task isn't found in your organization.
Response:
{
"data": [
{
"id": 9,
"task_id": 42,
"author": "Ada Lovelace",
"author_type": "user",
"body": "Looks good to me.",
"created_at": "2026-07-05T09:12:00+00:00"
}
],
"meta": { "page": 1, "per_page": 25, "total": 1, "total_pages": 1 }
}
Create a comment
POST /api/v1/tasks/{taskId}/comments
Requires the write scope. body is required and is HTML-sanitized the same way the web comment form is (scripts and event handler attributes are stripped). A body that's empty after sanitizing returns 400.
Response (201 Created): the created comment (same shape as above, a single object rather than a list).
API — Milestones
List milestones
GET /api/v1/milestones?project_id=1
GET /api/v1/projects/{projectId}/milestones
project_id is optional on the first form; both forms return the same shape, paginated.
Response:
{
"data": [
{
"id": 3,
"project_id": 1,
"name": "v1.0",
"description": null,
"due_date": "2026-08-01",
"status": "open"
}
],
"meta": { "page": 1, "per_page": 25, "total": 1, "total_pages": 1 }
}
Get a milestone
GET /api/v1/milestones/{id}
Response: a single milestone (same shape as above). 404 if not found in your organization.
Create a milestone
POST /api/v1/milestones
Requires the write scope.
| Field | Type | Required | Description |
|---|---|---|---|
project_id | integer | Yes | Target project ID |
name | string | Yes | Milestone name |
description | string | No | Description |
due_date | string | No | YYYY-MM-DD |
status | string | No | open or closed. Default: open |
Response (201 Created): the created milestone.
Update / delete a milestone
PATCH /api/v1/milestones/{id}
DELETE /api/v1/milestones/{id}
Both require the write scope. PATCH applies only the fields present in the body and returns the updated milestone. DELETE returns 204 No Content.
API — Backlog
Create a backlog item
POST /api/v1/backlog
Request body:
{
"project_id": 1,
"title": "Customer reports broken CSV export",
"description": "Details from the support ticket...",
"priority": "high",
"source_service": "supportlayer",
"source_ticket_id": 1842,
"source_ticket_url": "https://support.example.com/portal/tickets/1842"
}
| Field | Type | Required | Description |
|---|---|---|---|
project_id | integer | Yes | Target project ID |
title | string | Yes | Backlog item title |
description | string | No | Detailed description |
priority | string | No | low, medium, high, or critical. Default: medium |
source_service | string | No | Source system identifier. Default: supportlayer |
source_ticket_id | integer | No | External ticket ID (used for deduplication) |
source_ticket_url | string | No | URL back to the source ticket |
Response (201 Created):
{
"id": 15,
"status": "pending"
}
Duplicate detection: If a backlog item already exists for the same source_service + source_ticket_id, the API returns 409 Conflict with the existing item's ID and status.
Get a backlog item
GET /api/v1/backlog/{id}
Response:
{
"id": 15,
"title": "Customer reports broken CSV export",
"status": "pending",
"priority": "high",
"project_id": 1,
"source_service": "supportlayer",
"source_ticket_id": 1842,
"converted_task_id": null,
"created_at": "2026-04-10T14:30:00+00:00"
}
List backlog items
GET /api/v1/backlog?project_id=1&status=pending
Both query parameters are optional. Returns items newest first, paginated (see below).
Claude Code
Plan a task with Claude, then let Claude Code build it. Press Plan with Claude on a task and Claude — reading your connected GitHub repo for context — writes a build plan onto the task. Claude Code, running in your own terminal, then discovers planned tasks over the REST API, reads the plan, implements the work, and updates the task's status as it goes.
Install the plugin
The skills that drive this loop are published as a Claude Code plugin marketplace at github.com/XegenLtd/Claude-Skills. Add it from inside Claude Code:
/plugin marketplace add XegenLtd/Claude-Skills
Then install the ProjectLayer skills from that marketplace and authenticate with a ProjectLayer API token (see API Authentication).
How the loop uses the API
GET /api/v1/tasks?has_plan=1— discover tasks that already have a Claude build plan.GET /api/v1/tasks/{id}— read a task's fullclaude_plan.PATCH /api/v1/tasks/{id}/status— mark the taskin_progresswhile building anddonewhen finished.
Enable "Plan with Claude" first under Settings › AI / Claude by adding your own Anthropic API key (and an optional GitHub token for repo-aware plans).
SupportLayer Integration
ProjectLayer integrates with SupportLayer to convert support tickets into backlog items automatically.
How it works
- Generate an API key in ProjectLayer (Settings → API Keys).
- Configure the key in your SupportLayer instance under Integrations.
- When a support agent clicks "Send to ProjectLayer" on a ticket, SupportLayer calls the
POST /api/v1/backlogendpoint. - The ticket appears in your ProjectLayer backlog with a link back to the original ticket.
- Your team reviews the backlog and converts accepted items into tasks.
Backlog statuses
| Status | Meaning |
|---|---|
pending | Newly arrived, awaiting review |
accepted | Approved by the team, not yet a task |
rejected | Declined — won't be worked on |
converted | Promoted to a full task (linked to the new task) |
Deduplication
If SupportLayer sends the same ticket twice (same source_service + source_ticket_id), the API returns 409 Conflict instead of creating a duplicate. The response includes the existing backlog item's ID and current status.