ProjectLayer

Documentation

Everything ProjectLayer does, in one place


Setup, concepts, the REST API, and how the Claude Code and SupportLayer integrations work.

Overview

ProjectLayer is a project management tool built for development teams and the agencies who build for clients. It provides Kanban boards and Scrum, tasks with epics, subtasks, blockers and time logging, milestones, labels, test management, an Intake queue fed by clients, Sentry and SupportLayer, a branded client portal with release acceptance testing, reports, notifications, a full activity log, a REST API with webhooks, and Claude-assisted planning.

Quick start

  1. Create an account — Register here. Your first seat is free.
  2. Verify your email — Check your inbox for a verification link and click it to activate your account.
  3. Create a project — Give it a name and a prefix (e.g. API). This prefix is used in task keys like API-42.
  4. Add tasks — Create tasks with titles, descriptions, priorities, assignees, due dates, and labels.
  5. Use the board — Every project gets a default Kanban board with To Do, In Progress, and Done columns. Move tasks between columns.
  6. Create milestones — Set deadlines and track progress toward releases or goals.
  7. Invite your team — Go to Team Members in the admin sidebar and send email invitations with a chosen role.
  8. Invite a client (optional) — Under Clients, add the client, tick their projects and invite their contacts to the client portal.

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 intake items. Each project has a unique prefix for task keys.
  • Task — An individual work item with a key (e.g. API-42), status, type, 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.
  • Intake — 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, Tester, Test Manager. You can add your own.
  • Client — A customer of your organization. Its contacts sign in to the client portal and see only the projects linked to that client. Contacts are not members and never take a seat.

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 like WEB-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, Intake, Milestones, Sprints and Labels (plus Team on Scrum projects) 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, Testing, On Hold, Done, or Closed. Change inline on the task detail page.
  • Type — Epic, User story, Feature, Bug, Task, Investigation, or Idea. Default: Task. See Epics.
  • Epic — The epic the task belongs to, if any. Subtasks use their parent's epic.
  • 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. Type @ to mention a teammate.
  • Story points, estimated hours and attachments — Optional, set on the task page.

Subtasks, blockers and watchers

  • Subtasks — Add from the task's Subtasks section (needs task.create). A subtask can't have subtasks of its own.
  • Blocked by — Link the tasks that must finish first (same project, needs task.edit). Links that would create a loop are refused. A task with open blockers shows a Blocked badge and can't be set to Done or Closed, dropped into a board's done column, or signed off after testing.
  • Watchers — Use Watch on any task. You start watching automatically when you create a task, are assigned it, comment, or add a subtask. Watchers get comment and status notifications.
  • @mentions — Mention someone by the part of their email before the @ (e.g. @ada for ada@example.com). They get an in-app notification and an email.

Time logging

Log hours from the task page (needs task.edit): decimal hours such as 1.5, up to 1000 per entry, with an optional date and note. The page shows the total against the task's estimate. You can delete only your own entries.

Testing a task

Each task can carry a test script: plain-language steps, each with what to do and what should happen (up to 50 steps).

  1. Write the steps in the task's Testing section and choose Save test steps (needs task.edit).
  2. Choose Send for testing. The task moves to Testing and a numbered round starts. Steps are locked while it's in Testing.
  3. A tester (needs task.test) marks each step Pass or Fail. A fail needs a note: the round ends, the task goes back to In Progress and a comment records what happened.
  4. When every step passes, Mark as Done signs the task off. Open blockers, and on Scrum projects the Definition of Done, still apply.

Moving a task out of Testing any other way abandons the round. Earlier rounds are listed under Previous rounds. The same flow is available over the API. For tests you reuse across releases, see Test management; for testing by your client, see Client portal.

Import and export (CSV)

  • Export — Export CSV on a project's task list downloads task_key, title, status, type, priority, assignee_email, milestone, story_points, due_date, estimated_hours, created_at, epic. Closed tasks are left out. The Reports page has Export all tasks and Export all intake, which add a project_slug column. The Intake page has its own export.
  • Import — Import tasks on the task list (needs task.create): upload a CSV of up to 2 MB and 1000 rows, then map its columns. Only the title is required. You can map status, type, priority, assignee email, milestone name, story points, due date, estimated hours and epic key. Values that can't be used become warnings (an unknown status imports as Open, an unknown assignee as unassigned).
  • Your own data — Download my data on the Account page exports your profile, the tasks you reported or are assigned, and your comments.

Closed tasks are listed on the project's Archive tab.

Filtering tasks

The task list includes a filter toolbar. You can filter by:

  • Text search — Matches against task titles. (The search box at the top of every page also searches descriptions and comments; see Search.)
  • Status — Show only tasks in a specific status.
  • Type — Show only tasks of a specific type.
  • Priority — Show only tasks of a specific priority.
  • Assignee — Show tasks assigned to a specific member, or show unassigned tasks.
  • Epic — Show only the items in one epic, or the items that belong to no epic.

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.

Epics

An epic is a container for a large body of work. The hierarchy is Epic → item (user story, task, bug and so on) → subtask.

  • Create one with New epic on the project's Epics tab, or by choosing the Epic type on a new task.
  • Add work to it by choosing the epic when you create a task, from the Epic field on any task, or with Add item on the epic. Subtasks follow their parent's epic.
  • Progress on the Epics tab and on the epic itself shows items done out of total, and story points done out of total. Subtasks are not counted separately, so points are never counted twice.
  • Epics are containers, not planned work. They never appear on the board, in sprints or on the Product Backlog; their items do. An epic cannot belong to another epic or have subtasks of its own.
  • Closing: an epic's status is set by a person. It cannot be marked Done or Closed while any of its items is unfinished; the error lists them. An epic that still contains items cannot be changed to another type, and a task cannot become an epic while it has a parent, subtasks, an epic or a sprint.

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 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.

Intake

Intake is a holding area for work items that haven't been promoted to full tasks.

  • Items can arrive via the Intake 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 intake item, with the title, description, and priority carried over. Sentry items become bugs.
  • Sentry items can be resolved in Sentry from here, and show as resolved when Sentry resolves them.
  • Accept, reject and convert are also available from the Intake API, so Claude Code can triage Intake for you.

Scrum mode

Every project starts in Kanban mode. To run Scrum, open the project, choose Edit, and under Way of working pick Scrum and choose a Product Owner. You can switch back to Kanban at any time; your team and roles are kept for if you switch back.

The Scrum team

The Team tab lists who is on the project's Scrum team and their roles:

  • Product Owner — exactly one per project. Owns the Product Backlog and accepts the Increment.
  • Scrum Master — at most one. Facilitates the ceremonies and can manage the team.
  • Developers — any number. Own the Sprint Backlog.

One person can hold several roles, but the Product Owner can't also be the Scrum Master. Anyone active in your organisation can join, including clients in the Tester group, so a client can be your Product Owner. Being on the team doesn't change what someone can see; their access group still decides that.

Actions that belong to a role can only be done by the person holding it, or by an administrator as an override. Anyone who can edit the project, and the Scrum Master, can manage the team. If a member is suspended, their roles stop counting until they're reactivated; a project whose Product Owner is suspended shows a warning until an admin assigns a new one.

Ceremonies and sprint length

Under Way of working you can also choose which ceremonies the team holds (Sprint Planning, Daily Scrum, Sprint Review and Sprint Retrospective, all on by default) and the usual sprint length (1–4 weeks, 2 by default).

Product Backlog and Product Goal

On a Scrum project the Backlog tab is the Product Backlog: every unfinished top-level task that isn't in a planned or active sprint, in the order the Product Owner chooses. New tasks join at the bottom. The Product Owner drags tasks into order or uses the move buttons; everyone else sees the order. Subtasks travel with their parent.

The Product Goal at the top of the Backlog is the team's next long-term objective. There is one at a time: the Product Owner marks it achieved or abandoned before setting the next, and past goals are kept.

Sprint Backlog and Sprint Goal

Developers and the Product Owner pull tasks into a planned or active sprint from the Backlog, or with Add from backlog on the sprint page, and write the Sprint Goal there. Removing a task from a sprint puts it back where it was on the Backlog. A completed sprint's tasks can't change.

Definition of Done

The Scrum team keeps its Definition of Done on the Team tab: a short checklist every task must meet. On a Scrum project with a Definition of Done, marking a task Done — from the task page, the board or a test sign-off — asks you to confirm every item, and the task page records who confirmed them. Closing a task doesn't need it. Reopening a Done task clears the confirmations. Tasks imported as Done arrive In Progress until someone confirms them.

Increment

Each Scrum sprint's page shows its Increment: the sprint's finished tasks that met the Definition of Done, with their story points, plus an optional version or tag and release note. Finished tasks that were never confirmed against the Definition of Done are listed separately.

Reports

The Reports page (needs report.view or settings.manage) 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 type — Bar chart showing type distribution.
  • 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.
  • Cycle and lead time, throughput, sprint velocity, burndown (for the active sprint) and completed by member.

The page can be printed, and has Export all tasks and Export all intake CSV downloads.

Scheduled reports

Under Scheduled reports in the admin sidebar (needs settings.manage), send a summary by email weekly or monthly to one address per report (yours by default). It includes total and overdue tasks, tasks completed in the period, logged time, average cycle time, average velocity and tasks by status. Reports can be paused or deleted.

Notifications

Notifications appear under the bell at the top of every page, and are also emailed. In-app notifications are always created; each person chooses which emails they get on the Profile & Notifications page.

EventWho is notifiedPreference
Task assigned to youThe new assigneeWhen a task is assigned to me
New comment on taskAssignee and reporter (excluding the comment author)When someone comments on my task
Task status changedAssignee, reporter and watchersWhen a task status changes
Task due soonThe assigneeWhen a task I own is due soon
You're @mentioned in a commentThe person mentionedAlways sent
A client sends a request or message, or fails or signs off a releaseEveryone with client.communicateAlways sent

Watchers also get comment notifications. Notifications link straight to the task or conversation. Client contacts get their own updates in the client portal and by email, which they can turn off.

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 intake items)
  • Status changes (with old and new values)
  • Type changes
  • Priority changes
  • Assignee changes
  • Due date changes
  • Comments added

It also records milestone, epic and description changes, attachments, blockers, time logged, test steps and results, Plan with Claude runs, client visibility changes and failed client acceptance tests. Each entry shows the actor, the action, and the timestamp.

Organization-wide events (sign-ins, settings changes, invitations, role changes, API keys, webhooks, clients, test cycles, release sign-offs) are listed in the Audit log in the admin sidebar (needs settings.manage).

Test management

Beyond each task's own test script, every project has a Tests tab (needs test.view) for tests you keep and rerun.

  • Test cases (create and edit with test.manage) have a key such as API-T3, a title, preconditions, priority, a status (Draft, Ready or Deprecated), an optional suite and linked task, and up to 50 steps. Copy to test case on a task turns its test script into a Ready case.
  • Suites group cases. Deleting a suite leaves its cases unfiled.
  • Test cycles (manage with test.cycle_manage) plan a round of testing, optionally tied to a milestone or sprint: add cases, assign each run to someone who can run tests, start, then complete. Unfinished runs are marked Skipped when a cycle completes, and completed cycles are read-only.
  • Runs (with test.execute) record Pass or Fail per step; a fail needs a note. A run can also be marked Blocked (with a reason) or Skipped. From a failed run, Raise bug creates a Bug task listing the failed steps (also needs task.create).

Test Management in the sidebar (needs test.view_all) shows every project's tests, pass rates, failing tests, active cycles and tasks waiting to be tested, plus the runs assigned to you.

The search box at the top of every page searches projects (name and description), tasks (title and description) and comments in your organization. Type at least two characters; up to 20 results are shown per group.

Boards, task lists, the Intake page and its badge, the comments and test results on a task, the notification bell, client conversations and client portal pages all update live when something changes, without reloading. If you're part-way through writing in a conversation, you're offered a reload instead so your draft isn't lost.

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:

  1. Enter the invitee's email address.
  2. Select a role (Administrator, Member, Viewer, Tester, or Test Manager).
  3. 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. Five default groups are created with each organization:

RolePermissions
Administrator All permissions — full control over projects, tasks, boards, intake, settings, and user management.
Member View, create and edit projects; full task management (create, edit, assign); test tasks and run test cases; board and intake management; client conversations. Cannot access settings or manage users.
Viewer View projects, tasks and tests only. Cannot create, edit, or assign.
Tester Can view projects and tasks, run task tests, and run test cases in a project's Tests section. Ideal for QA staff. (Clients test in the client portal instead.)
Test Manager Everything a Tester can do, plus writing test cases and suites, planning and completing test cycles, and the cross-project Test Management view.

Every member takes a seat, whatever their group (see Billing). Custom access groups can be created from Access groups in the admin sidebar (needs settings.manage): give the group a name and a machine key (lowercase with underscores, fixed once created) and tick any combination of permissions. Groups can be renamed and their permissions changed later; change a member's group from Team members.

PermissionDescription
project.viewView projects
project.createCreate new projects
project.editEdit project details, manage milestones and labels
project.deleteDelete projects
task.view_ownView tasks assigned to you
task.view_allView all tasks in a project
task.createCreate new tasks
task.editEdit task details, move on board
task.assignAssign tasks to team members
task.testRun a task's test steps, record Pass/Fail and sign off tested work
task.deleteDelete tasks
test.viewSee a project's Tests section: test cases, suites, cycles and run history
test.manageCreate, edit and delete test cases and suites; copy a task's test steps into a test case
test.executeRun test cases: record Pass/Fail per step, mark runs blocked or skipped, raise bugs from failures
test.cycle_manageCreate test cycles, add cases, assign runs, start and complete cycles
test.view_allOpen Test Management to see tests and cycles from every project in one place
board.manageManage board columns
backlog.manageManage intake: accept, reject and convert intake items
settings.manageAdmin settings: access groups, branding, API keys, AI / Claude, webhooks, scheduled reports, Sentry, the audit log; also opens Reports
report.viewOpen the Reports page, its print view and CSV exports
user.manageInvite, suspend, and manage team members
client.manageCreate clients, link their projects, invite and disable client contacts
client.communicateRead and reply to client conversations and requests (Client inbox)
uat.managePublish releases for client acceptance testing, waive items, withdraw rounds

Client portal

Your customers get their own area at /client, in your workspace's colours. There they can send requests, talk to your team, follow progress and test releases. They never see your boards, sprints, internal descriptions, comments or assignees.

  • Set up a client in Settings → Clients (needs the client.manage permission): name it, tick the projects that are for them, and invite their contacts by email. A project belongs to one client. Contacts never take a seat.
  • Contacts are not team members. They can't open the staff portal or use the API. The same email can't be both a team member and a client contact in one workspace.
  • Requests a client sends land in the project's Intake with the client's name and who asked. Declining one needs a reason, which the client sees. Converting one keeps the new task visible to the client.
  • Conversations: each request (and the task it becomes), each client-visible task, and each project has a conversation. Reply from the Client inbox (needs client.communicate). Choose Reply to client or Add internal note; internal notes and their files are never shown to the client. Task comments are always internal.
  • What the client sees of a task is set in the task's Client panel: whether it's shown, and a plain-language summary used instead of the internal description. Statuses are simplified: Received, Under review, Planned, In progress, On hold, Ready for your testing, Changes requested, Done, Declined. Contacts are emailed when one of theirs changes (they can turn email off).
  • Release testing (UAT): on a milestone, open Client testing (needs uat.manage), choose the tasks and publish. Every task needs test steps, written so the client can follow them. The client marks each step passed or failed, with a note and optional screenshot. A failure adds a comment to the task and notifies the team, but never changes the task's status: reopen the task or waive the item, then publish a new round. A contact allowed to approve releases signs off once every item has passed or been waived.

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.

Five default access groups (Administrator, Member, Viewer, Tester, Test Manager) 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:

  1. Go to Forgot password from the sign-in page.
  2. Enter your email address.
  3. Check your inbox for a reset link (valid for 1 hour).
  4. Click the link and set a new password (minimum 8 characters).

Two-factor authentication

Turn on two-factor sign-in from Profile & Notifications → Set up two-factor authentication:

  1. Scan the QR code with an authenticator app and enter the 6-digit code it shows. Nothing changes until a valid code is entered.
  2. Save the 8 recovery codes. They're shown only once, and each works once, in place of an app code.

After that you're asked for a code once per session. To turn it off, enter a current code or a recovery code.

Branding

Customize the portal appearance from Branding in the admin sidebar (needs settings.manage):

  • Logo — PNG, JPEG or WebP, up to 1 MB. Shown in the sidebar and across the client portal, including the page clients accept their invitation on. A wide logo about 40px tall works best. Tick Remove the current logo to go back to the default.
  • 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. Your colours are also used in the client portal and on buttons in emails. Changes take effect immediately. Custom domains aren't available.

Billing

ProjectLayer uses simple per-seat pricing:

  • Free plan — 1 seat included. No credit card required.
  • Pro plan — 1 free seat + additional paid seats at $15 USD / £12 GBP / €15 EUR / A$22 AUD / NZ$25 NZD per seat per month.

All features are available on both plans. There are no feature gates.

Who takes a seat: every active member of your organization, whatever their access group: administrators, members, viewers, testers, test managers and custom groups. Suspended members don't count. Client contacts never take a seat, however many you invite. An invitation can't be accepted, and a suspended member can't be reactivated, while every seat is in use.

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 API keys in the admin sidebar (needs settings.manage): give the key a name and choose Read & write or Read only. The full key is shown once; the list shows each key's prefix and when it was last used, and keys can be revoked. A key acts as the person who created it, and stops working if they're suspended or leave. 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:

ScopeCan do
Read & writeAll GET requests, plus POST/PATCH/PUT/DELETE
Read onlyOnly 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 }
}

Get a project

GET /api/v1/projects/{id}

Returns the project with its Scrum settings and team. mode is kanban or scrum; product_owner and scrum_master are null when nobody active holds the role. A member's active is false while they are suspended, and their roles don't count.

{
  "id": 1,
  "name": "API Platform",
  "slug": "api-platform",
  "prefix": "API",
  "status": "active",
  "mode": "scrum",
  "ceremonies": {
    "planning": true,
    "daily_scrum": true,
    "review": true,
    "retrospective": false
  },
  "default_sprint_length_days": 14,
  "product_owner": { "id": 4, "name": "Sam Owner" },
  "scrum_master": { "id": 7, "name": "Alex Master" },
  "team": [
    { "user": { "id": 4, "name": "Sam Owner" }, "roles": ["product_owner"], "active": true },
    { "user": { "id": 7, "name": "Alex Master" }, "roles": ["scrum_master", "developer"], "active": true }
  ]
}

Update a project's way of working

PATCH /api/v1/projects/{id}

Requires the write scope, and the key's creator needs permission to edit projects. Only the fields present are applied, and the response is the updated project (same shape as above).

FieldTypeDescription
modestringkanban or scrum
product_owner_idintegerUser to make Product Owner. Required when switching a project to Scrum without an active Product Owner; on a project already in Scrum it reassigns the Product Owner. Only valid for Scrum projects: sending it for a Kanban project returns 422.
ceremoniesobjectPartial object of booleans: planning, daily_scrum, review, retrospective
default_sprint_length_daysinteger7, 14, 21 or 28

Errors: 400 for bad types or values, 422 validation_failed for rule violations (switching to Scrum without a Product Owner, a sprint length other than 7, 14, 21 or 28, or a user id that is unknown or not in your organisation), and 403 forbidden.

Manage the Scrum team

GET /api/v1/projects/{id}/team
PUT /api/v1/projects/{id}/team/{userId}
DELETE /api/v1/projects/{id}/team/{userId}
PUT /api/v1/projects/{id}/product-owner
  • GET .../team returns {"team": [...]}, using the member shape shown above.
  • PUT .../team/{userId} takes {"roles": ["developer", "scrum_master"]} (values: product_owner, scrum_master, developer), sets that person's roles, and returns the member.
  • DELETE .../team/{userId} removes the person and returns 204 No Content.
  • PUT .../product-owner takes {"user_id": 4}, reassigns the Product Owner, and returns the project.

Writes need the write scope, and the key's creator must be able to edit the project or be its Scrum Master. Errors: 409 on Kanban projects and when removing the Product Owner (assign a new one instead), 422 for rule violations (including a user id that is unknown or belongs to another organisation), 404 when deleting someone who isn't on the team, and 403 forbidden.

API keys act as the user who created them: Scrum role checks use that user's role and membership.

API — Scrum artifacts

These endpoints cover a Scrum project's Product Backlog, Product Goals and Definition of Done, plus each sprint's goal, tasks and Increment. Reads need the read scope; writes need the write scope. API keys act as the user who created them, so the role checks below use that person's Scrum role. Every endpoint answers 409 on a project that isn't using Scrum.

Product Backlog

GET  /api/v1/projects/{id}/backlog?limit=&offset=
POST /api/v1/projects/{id}/backlog/move

GET returns the Product Backlog in order. limit is 1–100 (100 by default) and offset starts at 0. rank_position counts from 1 across the whole Backlog.

{
  "items": [
    {
      "rank_position": 1,
      "task": {
        "id": 12,
        "task_key": "PORT-12",
        "title": "Add CSV export",
        "status": "open",
        "type": "feature",
        "priority": "high",
        "story_points": 3,
        "assignee": null
      }
    }
  ],
  "total": 1,
  "total_points": 3
}

POST .../backlog/move is for the Product Owner only. Body: {"task_id": 12, "after_task_id": null}. The task goes directly after after_task_id, or to the top when it is null. The response is the Backlog again, in the shape above.

Product Goals

GET  /api/v1/projects/{id}/product-goals
POST /api/v1/projects/{id}/product-goals
POST /api/v1/projects/{id}/product-goals/{goalId}/close

GET returns {"current": {...} | null, "history": [...]}. Each goal looks like this:

{
  "id": 5,
  "text": "Customers can self-serve their invoices",
  "status": "active",
  "set_by": { "id": 4, "name": "Sam Owner" },
  "set_at": "2026-09-01T09:30:00+00:00",
  "closed_at": null
}

POST .../product-goals takes {"text": "…"}, is for the Product Owner only, and returns 201 with the new goal, or 409 when a goal is already active. POST .../product-goals/{goalId}/close takes {"status": "achieved"} or {"status": "abandoned"}, is for the Product Owner only, and returns the closed goal.

Definition of Done

GET /api/v1/projects/{id}/definition-of-done
PUT /api/v1/projects/{id}/definition-of-done

GET returns {"criteria": [{"id": 3, "text": "Code reviewed", "position": 1}]}.

PUT replaces the whole ordered list and needs a member of the Scrum team. Body:

{"criteria": [{"id": 3, "text": "Code reviewed"}, {"text": "New item"}]}

Send an id to keep and reword an existing item, and leave it out to add a new one; items you leave out of the list are removed. An id that isn't one of this project's current items returns 404, and a repeated id returns 422. The response is the new list.

Sprints

GET    /api/v1/sprints/{id}
PATCH  /api/v1/sprints/{id}
PUT    /api/v1/sprints/{id}/tasks/{taskId}
DELETE /api/v1/sprints/{id}/tasks/{taskId}
PATCH  /api/v1/sprints/{id}/increment

Kanban projects have sprints too, but these endpoints only serve Scrum projects: a sprint on a Kanban project returns 409 for every request, including GET. The writes need a Developer or the Product Owner.

  • PATCH .../sprints/{id} takes {"goal": "…"} (or null to clear it).
  • PUT .../sprints/{id}/tasks/{taskId} pulls a task from the Backlog into the sprint. DELETE on the same path puts it back where it was on the Backlog and returns 204 No Content. A completed sprint's tasks can't change (409). A task that's already finished, already in another sprint or not in this sprint is refused with 422 or 409 and a message saying why.
  • PATCH .../sprints/{id}/increment takes {"version": "v2.4.0", "release_note": "…"}. Either field is optional, and null clears it.

Every sprint call except DELETE returns the sprint:

{
  "id": 8,
  "project_id": 1,
  "name": "Sprint 8",
  "status": "active",
  "start_date": "2026-09-14",
  "end_date": "2026-09-28",
  "goal": "Ship invoice export",
  "tasks": [ { "id": 12, "task_key": "PORT-12", "title": "Add CSV export", "status": "done", "type": "feature", "priority": "high", "story_points": 3, "assignee": null } ],
  "increment": {
    "version": "v2.4.0",
    "release_note": "Invoices can now be exported.",
    "updated_at": "2026-09-28T16:00:00+00:00",
    "points": 3,
    "included_task_ids": [12],
    "excluded_task_ids": []
  }
}

included_task_ids are the finished tasks that met the Definition of Done; excluded_task_ids are finished tasks that never were confirmed against it. points adds up the story points of the included tasks.

Completing tasks with a Definition of Done

When a Scrum project has a Definition of Done, moving a task to done means confirming every current item. Send the item ids as dod_confirmed on any of these:

PATCH /api/v1/tasks/{id}                                 {"status": "done", "dod_confirmed": [3, 4]}
PATCH /api/v1/tasks/{id}/status                          {"status": "done", "dod_confirmed": [3, 4]}
POST  /api/v1/tasks/{id}/test-runs/current/sign-off      {"dod_confirmed": [3, 4]}

If any current id is missing, the task isn't changed and the response is 422, listing what still needs confirming:

{
  "error": {
    "code": "dod_not_confirmed",
    "message": "Confirm every Definition of Done item before marking this task Done.",
    "details": { "criteria": [ { "id": 3, "text": "Code reviewed" } ] }
  }
}

dod_confirmed must be a list of integers, otherwise the response is 400. Closing a task doesn't need it. Other errors from PATCH|POST /api/v1/tasks/{id}/status keep their existing shape, {"error": "message"}; only this refusal and a malformed dod_confirmed (400) use the structured {"error": {"code", "message"}} form. GET /api/v1/tasks/{id} now also returns backlog_rank_position (the task's place on the Product Backlog, or null when it isn't on it) and done_checks:

"done_checks": [
  {
    "criterion_id": 3,
    "text": "Code reviewed",
    "confirmed_by": { "id": 7, "name": "Alex Master" },
    "confirmed_at": "2026-09-28T15:45:00+00:00"
  }
]

Errors

These endpoints return {"error": {"code": "…", "message": "…"}} with:

  • 404 not_found — the project, sprint, task, goal or Definition of Done item doesn't exist in your organization.
  • 400 invalid_request — the body isn't in the shape shown.
  • 422 validation_failed — the request breaks a Scrum rule; the message says which.
  • 409 conflict — the request clashes with the current state, including "This project isn't using Scrum." on Kanban projects.
  • 403 forbidden — the key's creator doesn't hold the needed role; the message names who can act.

API — Tasks

List tasks

GET /api/v1/tasks

Returns a page of tasks in your organization, most recently updated first (see Pagination).

Query parameterTypeDescription
has_planbooleanWhen 1 (or true), returns only tasks that have a Claude build plan, most recently planned first.
project_idintegerRestrict to a single project. Can be combined with has_plan. An unknown ID returns 404.
typestringOne type, or several separated by commas (for example epic or user_story,bug). An unknown value returns 400.
epic_idintegerThe items in that epic. 0 returns items that belong to no epic.
page, per_pageintegerSee Pagination.

Response:

{
  "data": [
    {
      "id": 42,
      "task_key": "API-42",
      "title": "Add CSV export",
      "project_id": 1,
      "status": "in_progress",
      "type": "bug",
      "priority": "high",
      "story_points": 3,
      "epic": { "id": 40, "task_key": "API-40", "title": "Checkout" },
      "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",
  "type": "bug",
  "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"
}
FieldTypeRequiredDescription
statusstringYesopen, 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",
  "type": "bug",
  "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.

FieldTypeRequiredDescription
project_idintegerYesTarget project ID. An unknown ID returns 404.
titlestringYesTask title
descriptionstringNoDescription
prioritystringNolow, medium, high, or critical. Default: medium
typestringNoepic, user_story, feature, bug, task, investigation, or idea. Default: task. An unrecognised value returns 400 (invalid_request).
assignee_idintegerNoMust be an active member of your organization
milestone_idintegerNoMust belong to the same project
story_pointsintegerNo0 to 10000. null clears it on update.
epic_idintegerNoThe epic this task belongs to: a task of type epic in the same project. null or 0 removes it on update. An unknown id returns 400; breaking an epic rule (an epic inside an epic, a subtask choosing its own epic) returns 409.
claude_planstringNoBuild 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. Task details also carry epic_progress (items_total, items_done, points_total, points_done) on an epic, and null on anything else. An epic cannot be marked done or closed while any item is unfinished (409).

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, type, 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 — Testing

Every task can carry a plain-language test script that a non-technical tester can run in the task page, marking each step Pass or Fail. Sending a task to the testing status opens a numbered round with a copy of the script; a failed step sends the task back to in_progress with a comment, and when every step passes someone signs the round off, which marks the task done. Earlier rounds are kept as history.

Get or replace the script

GET /api/v1/tasks/{id}/test-steps
PUT /api/v1/tasks/{id}/test-steps

PUT requires the write scope and replaces every step. Body: {"steps": [{"instruction": "Open Invoices and click Export", "expected_result": "A CSV file downloads"}]}. Up to 50 steps, each field up to 2000 characters. Returns 409 while the task is in testing and 422 for invalid steps.

Send for testing

PATCH /api/v1/tasks/{id}/status   {"status": "testing"}

Returns 409 if the task has no test steps. While a task is in testing, setting it to done or closed through the API also returns 409: finish the round with a sign-off instead.

Rounds and results

GET   /api/v1/tasks/{id}/test-runs
GET   /api/v1/tasks/{id}/test-runs/{number|current}
PATCH /api/v1/tasks/{id}/test-runs/current/results/{resultId}   {"result": "pass"}
PATCH /api/v1/tasks/{id}/test-runs/current/results/{resultId}   {"result": "fail", "note": "Nothing downloaded"}
POST  /api/v1/tasks/{id}/test-runs/current/sign-off

result is pass, fail (a note is required) or pending (undo). Sign-off needs every step passed and no open blockers. Writes need the write scope; outside an open round they return 409. GET /api/v1/tasks/{id} also returns test_steps, current_test_run and test_run_count, and GET /api/v1/tasks?status=testing lists tasks waiting to be tested (status also takes a comma-separated list, such as open,in_progress). Every task includes last_test_outcome, the result of its latest round: failed on an in_progress task means a tester found a problem.

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 — Client messages

A task's client conversation. The API key's user needs the client.communicate permission, and the task's project must have a client (otherwise 404).

List

GET /api/v1/tasks/{taskId}/client-messages

Returns client, client_visible and data: every message, oldest first, each with id, side (client, staff or system), internal, author, body and created_at. Internal notes are included and flagged.

Post

POST /api/v1/tasks/{taskId}/client-messages
{ "body": "Deployed to staging.", "internal": false }

Requires the write scope. internal defaults to true, so nothing reaches the client unless you send "internal": false. Replying to the client on a task that isn't client-visible returns 409.

Tasks also carry client_visible (in lists and detail) and client_summary (in detail); both can be set with PATCH /api/v1/tasks/{id}. Intake items carry client, requested_by and client_decline_reason; declining a client's request needs a body of {"reason": "..."}.

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.

FieldTypeRequiredDescription
project_idintegerYesTarget project ID
namestringYesMilestone name
descriptionstringNoDescription
due_datestringNoYYYY-MM-DD
statusstringNoopen 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 — Intake

The endpoint path stays /api/v1/backlog for compatibility. Intake items arrive from SupportLayer, from Sentry, or from this API. Reads need the read scope; creating, accepting, rejecting and converting need write, and accepting, rejecting and converting also need the key owner to have the backlog.manage permission.

Create an intake 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"
}
FieldTypeRequiredDescription
project_idintegerYesTarget project ID
titlestringYesIntake item title
descriptionstringNoDetailed description
prioritystringNolow, medium, high, or critical. Default: medium
source_servicestringNoSource system identifier. Default: supportlayer
source_ticket_idintegerNoExternal ticket ID (used for deduplication)
source_ticket_urlstringNoURL back to the source ticket

Response (201 Created):

{
  "id": 15,
  "status": "pending"
}

Duplicate detection: If an intake 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 an intake item

GET /api/v1/backlog/{id}

Response:

{
  "id": 15,
  "title": "Customer reports broken CSV export",
  "description": "Details from the support ticket...",
  "status": "pending",
  "priority": "high",
  "project_id": 1,
  "source_service": "supportlayer",
  "source_ticket_id": 1842,
  "source_external_id": null,
  "source_ticket_url": "https://support.example.com/portal/tickets/1842",
  "converted_task_id": null,
  "created_at": "2026-04-10T14:30:00+00:00",
  "updated_at": "2026-04-10T14:30:00+00:00"
}

source_external_id holds non-numeric source ids, such as a Sentry issue id.

List intake items

GET /api/v1/backlog?project_id=1&status=pending

Both query parameters are optional. status is pending, accepted, rejected, converted or resolved (resolved at the source, e.g. in Sentry, before anyone acted on it). Returns items newest first, paginated (see below). List rows leave out description; fetch the item to read it.

Accept or reject an intake item

POST /api/v1/backlog/{id}/accept
POST /api/v1/backlog/{id}/reject

Only a pending item can be accepted or rejected; otherwise the response is 409. Returns the updated item.

Convert an intake item to a task

POST /api/v1/backlog/{id}/convert

Works on a pending or accepted item. The body is optional; any field you send overrides what the item carries.

{
  "title": "Checkout crashes when the basket is empty",
  "description": "<p>Steps to reproduce...</p>",
  "priority": "high",
  "type": "bug",
  "assignee_id": 7,
  "milestone_id": 3
}
FieldTypeDescription
titlestring1–500 characters. Default: the item's title
descriptionstringHTML, sanitized like a task description. Default: the item's text, with a link back to its source
prioritystringlow, medium, high or critical. Default: the item's priority
typestringepic, user_story, feature, bug, task, investigation or idea. Default: bug for Sentry items, otherwise task
assignee_idintegerAn active member of your organization
milestone_idintegerA milestone of the item's project
epic_idintegerAn epic in the item's project to file the new task under (not with type epic)
story_pointsinteger0 to 10000

Response (200): the intake item (now converted) plus the new task:

{
  "id": 15,
  "status": "converted",
  "converted_task_id": 88,
  "task": {
    "id": 88,
    "task_key": "API-42",
    "project_id": 1,
    "title": "Checkout crashes when the basket is empty",
    "status": "open",
    "type": "bug",
    "priority": "high"
  }
}

Converting an item that was already converted returns 409 with its converted_task_id, so a retry never creates a second task. An invalid field returns 400 and leaves the item untouched. The new task is reported by, and the conversion is recorded against, the API key's user.

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 full claude_plan.
  • PATCH /api/v1/tasks/{id}/status — mark the task in_progress while building.
  • PUT /api/v1/tasks/{id}/test-steps — when the code is finished, write plain-language test steps for a non-technical tester, then set the status to testing.
  • A person (for example someone in the Tester group) runs the round on the task page. Only if you ask it to does Claude run the steps itself, record them with PATCH …/test-runs/current/results/{resultId} and POST …/sign-off. Anything it records is attributed to the API key's creator.
  • When a step fails, Claude finds the task with GET /api/v1/tasks?status=in_progress and last_test_outcome failed, fixes it, and sets testing again.

Plan with Claude

Set it up under AI / Claude in the admin sidebar (needs settings.manage):

  • Anthropic API key — your own; Claude usage is billed to it. Without a key, tasks show a Set up Claude link instead.
  • GitHub access token (optional) — needed for private repos; public repos work without it.
  • Model — Always the latest Claude Opus (the default), Fable, Sonnet or Haiku, or pin a specific model. If a pinned model is retired, the organization moves to the newest model in that family and admins are notified.
  • Plan with Claude switch — untick to hide the button everywhere without removing the key. The key and token can also be removed.

Connect a repository per project under the project's Edit page (owner, repo name and optional branch). On a task, Plan with Claude (needs task.edit) writes the plan into Claude's Build Plan; only one run per task at a time. If Claude has questions, it asks them in a comment: reply in that thread and choose Send replies to Claude to continue.

Webhooks

Send task events to your own systems from Webhooks in the admin sidebar (needs settings.manage). Enter an http or https URL and choose events. Private, internal and loopback addresses are refused.

EventWhen
task.createdA task is created
task.updatedA field changes (the payload names the field)
task.status_changedThe status changes
task.commentedA comment is added
task.movedThe task moves board column (the payload names the column)

Each delivery is a POST with a JSON body {"event", "occurred_at", "data": {id, task_key, title, status, type, priority, project, …}} and the headers X-ProjectLayer-Event, X-ProjectLayer-Delivery and X-ProjectLayer-Signature: sha256=…, an HMAC-SHA256 of the raw body using the signing secret. The secret is shown once, when you create the webhook. Any response other than 2xx within 10 seconds counts as a failure, and a delivery is tried up to 3 times. Deliveries shows the last 20 attempts with their responses.

Note: webhooks fire for changes made in the web app. Changes made through the REST API don't send webhooks.

Sentry Integration

New Sentry issues arrive in a project's Intake as pending items, ready to accept, reject or convert into bug tasks. Each project has its own connection.

  1. Open the project's Intake page and choose Sentry (needs the settings.manage permission), then Generate webhook URL.
  2. In Sentry go to Settings → Integrations → Custom Integrations and create an Internal Integration. Set its Webhook URL to the generated URL, give it Read access to Issue & Event, subscribe it to Issue events and save.
  3. Copy the integration's Client Secret back into the project's Sentry page and choose Connect.

Every request is verified with the Client Secret. A new issue creates one Intake item (duplicates are ignored); an issue that regresses after being resolved reopens its item as pending with a note. Sentry's level sets the priority: fatal is critical, error high, info and debug low. Pause or disconnect the integration at any time; existing items are kept.

Resolving issues both ways

Sentry to ProjectLayer. When an issue is resolved in Sentry, an Intake item nobody has acted on becomes Resolved (with the release, if Sentry names one). If the item was already turned into a task, the task gets a Sentry comment and its assignee and reporter are notified; the task itself is left for a person to verify and close. An issue that regresses reopens its item as pending.

ProjectLayer to Sentry. Give the integration Read & Write access to Issue & Event, then paste its Token in the project's Sentry settings (step 4). A Resolve in Sentry button then appears on Sentry items in Intake, and the issue is resolved automatically when its task is marked Done, however that happens (portal, API or tester sign-off). Regional or self-hosted Sentry can set its own URL. The token is stored encrypted and never shown again; without it the connection stays inbound-only.

SupportLayer Integration

ProjectLayer integrates with SupportLayer to convert support tickets into intake items automatically.

How it works

  1. Generate an API key in ProjectLayer (Settings → API Keys).
  2. Configure the key in your SupportLayer instance under Integrations.
  3. When a support agent clicks "Send to ProjectLayer" on a ticket, SupportLayer calls the POST /api/v1/backlog endpoint.
  4. The ticket appears in your ProjectLayer Intake with a link back to the original ticket.
  5. Your team reviews Intake and converts accepted items into tasks.

Intake statuses

StatusMeaning
pendingNewly arrived, awaiting review
acceptedApproved by the team, not yet a task
rejectedDeclined — won't be worked on
convertedPromoted 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 intake item's ID and current status.