diff --git a/README.md b/README.md index ecf1f06..f8e5466 100644 --- a/README.md +++ b/README.md @@ -1,85 +1,209 @@ +# ⚠️ Deprecation Notice -# Zenhub API +> [!WARNING] +> This API has not been updated since 2021 is scheduled for deprecation. Please use our new GraphQL API at https://developers.zenhub.com -This document outlines the setup and usage of ZenHub's API: a RESTful API with JSON responses. +# Getting support +If you have any questions or feedback, **contact [support](mailto:support@zenhub.com)**. You can submit feature requests [here](https://portal.productboard.com/zenhub). + +# Overview + +[Overview](#overview) + +- [Root Endpoint](#root-endpoint) - [Authentication](#authentication) -- [Endpoints](#endpoints) - - [Get issue data](#get-issue-data) - - [Get issue events](#get-issue-events) - - [Get the ZenHub Board data for a - repository](#get-the-zenhub-board-data-for-a-repository) - - [Get Epics for a repository](#get-epics-for-a-repository) - - [Get Epic data](#get-epic-data) -- [API limits](#api-limits) +- [Content-Type: JSON](#content-type-json) +- [API Rate Limit](#api-rate-limit) - [Errors](#errors) -- [Webhooks](#webhooks) -- [Contact us](#contact-us) + +[Endpoint Reference](#endpoint-reference) + +- [Issues](#issues) + - [Get Issue Data](#get-issue-data) + - [Get Issue Events](#get-issue-events) + - [Move an Issue Between Pipelines](#move-an-issue-between-pipelines) + - [Move an Issue Between Pipelines in the oldest Workspace](#move-an-issue-between-pipelines-in-the-oldest-workspace) + - [Set Issue Estimate](#set-issue-estimate) +- [Epics](#epics) + - [Get Epics for a Repository](#get-epics-for-a-repository) + - [Get Epic Data](#get-epic-data) + - [Convert an Epic to an Issue](#convert-an-epic-to-an-issue) + - [Convert an Issue to Epic](#convert-issue-to-epic) + - [Add or Remove Issues from an Epic](#add-or-remove-issues-to-epic) +- [Workspaces](#workspace) + - [Get ZenHub Workspaces for a repository](#get-zenhub-workspaces-for-a-repository) + - [Get a ZenHub Board for a repository](#get-a-zenhub-board-for-a-repository) + - [Get the oldest ZenHub Board for a repository](#get-the-oldest-zenhub-board-for-a-repository) +- [Milestones](#milestones) + - [Set the Milestone Start Date](#set-milestone-start-date) + - [Get the Milestone Start Date](#get-milestone-start-date) +- [Dependencies](#dependencies) + - [Get Dependencies for a Repository](#get-dependencies-for-a-repository) + - [Create a Dependency](#create-a-dependency) + - [Remove a Dependency](#remove-a-dependency) +- [Release Reports](#release-reports) + - [Create a Release Report](#create-a-release-report) + - [Get a Release Report](#get-a-release-report) + - [Get Release Reports for a Repository](#get-release-reports-for-a-repository) + - [Edit a Release Report](#edit-a-release-report) + - [Add a Repository to a Release Report](#add-a-repository-to-a-release-report) + - [Remove a Repository from a Release Report](#remove-a-repository-from-a-release-report) +- [Release Report Issues](#release-report-issues) + - [Get all the Issues in a Release Report](#get-all-the-issues-for-a-release-report) + - [Add or Remove Issues from a Release Report](#add-or-remove-issues-to-or-from-a-release-report) + +[Webhooks](#webhooks) + +- [Custom Webhooks](#custom-webhooks) + - [Content Type: urlencoded](#content-type-urlencoded) + +[Contact Us](#contact-us) + +## Root Endpoint + +The ZenHub API root endpoint for Cloud is different than that of ZenHub On-Premise Enterprise instances and has also changed across Enterprise versions. Please refer to the table below for the appropriate endpoint. + +ZenHub Version | API Root Endpoint +--- | --- +Cloud | `https://api.zenhub.com/` +Enterprise 2 | `https:///` +Enterprise 3 | `https:///api/` ## Authentication -### For ZenHub IO users -All requests to the API need an API token. Generate a token in the [Settings](https://dashboard.zenhub.io/#/settings) section of your ZenHub Dashboard. -Note: Each user may only have one token, so generating a new token will make any previous tokens invalid. +All requests to the API need an API token. Generate a token in the **API Tokens** section of your ZenHub [Dashboard](https://app.zenhub.com/dashboard/tokens) (for ZenHub Enterprise, refer to the table below for the proper link). -The token is sent in the `X-Authentication-Token` header. For example, using `curl` it'd be `curl -H 'X-Authentication-Token: TOKEN' URL`. Alternatively, you can send the token in the URL using the `access_token` query string attribute. To do so, add ```?access_token=TOKEN``` to any url. +ZenHub Enterprise Version | Auth Token Generation Page +--- | --- +Enterprise 2 | `https:///app/dashboard/tokens` +Enterprise 3 | `https:///dashboard/tokens` -### For ZenHub Enterprise users -Please follow the instruction in https://{{zenhub_enterprise_host}}/setup/howto/api +The token is sent in the `X-Authentication-Token` header. For example, using `curl` it would be: -## Endpoints +```sh +curl -H 'X-Authentication-Token: TOKEN' URL +``` -### Get issue data +Alternatively, you can choose to send the token in the URL using the `access_token` query string attribute. To do so, add `?access_token=TOKEN` to any URL. -Here are the current endpoints available for ZenHub's API. +#### Notes +- Each user may only have one token, so generating a new token will invalidate previously created tokens. +## Content-Type: JSON + +Our REST API only supports JSON content for requests with a body and for responses. +For each request containing a body with JSON, you will need to attach the header `'Content-Type: application/json'` with your request. For example, using `curl` it’d be: + +```sh +curl -H 'Content-Type: application/json' URL ``` -GET https://api.zenhub.io/p1/repositories/:repo_id/issues/:issue_number -``` -Please note: `repo_id` is the ID of the repository, not its full name. For example, the ID of the `ZenHubIO/support` repository is `13550592`. -To find out the ID of your repository, use [GitHub's API](https://developer.github.com/v3/repos/#get). +## API Rate Limit + +We allow a maximum of 100 requests per minute to our API. All requests responses include the following headers related to this limitation. + +| Header | Description | +| ------------------- | --------------------------------------------------------------------------------- | +| `X-RateLimit-Limit` | Total number of requests allowed before the reset time | +| `X-RateLimit-Used` | Number of requests sent in the current cycle. Will be set to 0 at the reset time. | +| `X-RateLimit-Reset` | Time in UTC epoch seconds when the usage gets reset. | + +To avoid time differences between your computer and our servers, we suggest to use the `Date` header in the response to know exactly when the limit is reset. + +# Errors + +The ZenHub API can return the following errors: + +| Status Code | Description | +| ----------- | --------------------------------------------------------------------- | +| `401` | The token is not valid. See [Authentication](#authentication). | +| `403` | Reached request limit to the API. See [API Limits](#api-rate-limits). | +| `404` | Not found. | -Issue number is the same as displayed in your GitHub Issues page. For example, to fetch the [Zenhub Public API](https://github.com/ZenHubIO/support/issues/172) issue information, the URL would be `https://api.zenhub.io/p1/repositories/13550592/issues/172`. +# Endpoint Reference -The endpoint returns that issue's assigned _Time Estimate_ (if applicable), its _Pipeline_ in the Board, an _is epic_ flag (true/false), as well as any _+1s_. +#### Notes -NOTE: Closed issues might take up to one minute to show up in the Closed pipeline. Similarly, reopened issues might take up to one minute to show in the right pipeline. +- `repo_id` is the ID of the repository, not its full name. For example, the ID of the `ZenHubIO/API` repository is `47655910`. To find out the ID of your repository, use [GitHub’s API](https://developer.github.com/v3/repos/#get), or copy it from the URL of the Board (for this repo, the Board URL is https://github.com/ZenHubIO/API#boards?repos=47655910). +- `workspace_id` is the ID of the ZenHub Workspace. This is found in the URL for the Workspace after the name of the workspace. For example, the `workspace_id` for `https://app.zenhub.com/workspaces/workflows---product--design-workspace-5f6b5c9ab4fd7d76a3e5b7d8/board?` is `5f6b5c9ab4fd7d76a3e5b7d8`. + +## Issues + +- [Get Issue Data](#get-issue-data) +- [Get Issue Events](#get-issue-events) +- [Move an Issue Between Pipeline](#move-an-issue-between-pipelines) +- [Move an Issue Between Pipelines in the oldest Workspace](#move-an-issue-between-pipelines-in-the-oldest-workspace) +- [Set Issue Estimate](#set-issue-estimate) + +### Get Issue Data + +Get the data for a specific issue. + +#### Endpoint + +`GET /p1/repositories/:repo_id/issues/:issue_number` + +#### URL Parameters + +| Name | Type | Comments | +| -------------- | -------- | -------- | +| `repo_id` | `Number` | Required | +| `issue_number` | `Number` | Required | + +#### Example Response -Here is an example of returned JSON data: ```json { "estimate": { "value": 8 }, - "plus_ones": [ + "pipeline": { + "name": "QA", + "pipeline_id": "5d0a7a9741fd098f6b7f58a7", + "workspace_id": "5d0a7a9741fd098f6b7f58ac" + }, + "pipelines": [ { - "user_id": 16717, - "created_at": "2015-12-11T18:43:22.296Z" + "name": "QA", + "pipeline_id": "5d0a7a9741fd098f6b7f58a7", + "workspace_id": "5d0a7a9741fd098f6b7f58ac" + }, + { + "name": "Done", + "pipeline_id": "5d0a7cea41fd098f6b7f58b7", + "workspace_id": "5d0a7cea41fd098f6b7f58b8" } ], - "pipeline": { - "name": "In Progress" - }, "is_epic": true } ``` -### Get issue events +#### Notes -``` -GET https://api.zenhub.io/p1/repositories/:repo_id/issues/:issue_number/events -``` +- `pipeline` object references the oldest Workspace pipeline this issue is in. + - **NOTE:** If an issue's status is closed, the `pipeline` value will describe the Pipeline that the issue was in prior to the issue being closed. The ZenHub API does not consider the "Closed" Pipeline to be a distinct Pipeline at this time and you should **not** use the Pipeline value to determine whether or not an issue is closed or open (use `status` instead). + - **NOTE:** Reopened issues might take up to one minute to show up in the correct Pipeline. +- `pipelines` contains all pipelines in all Workspaces this issue is in. + +### Get Issue Events + +Get the events for an issue. -Please note: `repo_id` is the ID of the repository, not its full name. For example, the ID of the `ZenHubIO/support` repository is `13550592`. -To find out the ID of your repository, use [GitHub's API](https://developer.github.com/v3/repos/#get). +#### Endpoint -Issue number is the same one displayed in your GitHub Issues page. For example, to fetch the [Zenhub Public API](https://github.com/ZenHubIO/support/issues/172) issue information, the URL would be `https://api.zenhub.io/p1/repositories/13550592/issues/172`. +`GET /p1/repositories/:repo_id/issues/:issue_number/events` -The endpoint returns that issue's events, sorted by most recent. Each event contains the _User ID_ of the person who performed the change, the _Creation Date_ of the event, and _Type_. Type can be either `estimateIssue` or `transferIssue`. Old and new values are included for both event types. +#### URL Parameters + +| Name | Type | Comments | +| -------------- | -------- | -------- | +| `repo_id` | `Number` | Required | +| `issue_number` | `Number` | Required | + +#### Example Response -Here is an example of returned JSON data: ```json [ { @@ -118,7 +242,8 @@ Here is an example of returned JSON data: }, "to_pipeline": { "name": "In progress" - } + }, + "workspace_id": "5d0a7a9741fd098f6b7f58ac" }, { "user_id": 16717, @@ -131,34 +256,467 @@ Here is an example of returned JSON data: ] ``` -### Get the ZenHub Board data for a repository +#### Notes + +- Returns issue events, sorted by creation time, most recent first. +- Each event contains the _User ID_ of the user who performed the change, the _Creation Date_ of the event, and the event _Type_. +- Type can be either `estimateIssue` or `transferIssue`. The values before and after the event are included in the event data. +- `transferIssue` events include a `workspace_id` indicating in which Workspace the transfer occurred. + +### Move an Issue Between Pipelines + +Moves an issue between Pipelines in a Workspace + +#### Endpoint + +`POST /p2/workspaces/:workspace_id/repositories/:repo_id/issues/:issue_number/moves` + +#### URL Parameters + +| Name | Type | Comments | +| -------------- | -------- | -------- | +| `workspace_id` | `String` | Required | +| `repo_id` | `Number` | Required | +| `issue_number` | `Number` | Required | + +#### Body Parameters + +| Name | Type | Comments | +| ------------- | -------------------- | -------- | +| `pipeline_id` | `String` | Required | +| `position` | `String` or `Number` | Required | + +#### Notes + +- `workspace_id` is the ID of the Workspace you're transferring the issue in. To get a list of Workspaces for `repo_id`, you can use the [_Get ZenHub Workspaces for a repository_](#get-zenhub-workspaces-for-a-repository) endpoint. +- `pipeline_id` is the ID for one of the Pipelines in the Workspace specified by `workspace_id` (i.e: In Progress, Done, QA). In order to obtain this ID, you can use the [_Get a ZenHub Board for a repository_](#get-a-zenhub-board-for-a-repository) endpoint. +- `position` can be specified as `top` or `bottom`, or a `0`-based position in the Pipeline such as `1`, which would be the second position in the Pipeline. + +#### Example Request Body + +```json +{ + "pipeline_id": "58bf13aba426771426665e60", + "position": "top" +} +``` + +#### Example Response + +Status `200` for a successful move. No response body. + +### Move an Issue Between Pipelines in the oldest Workspace + +Moves an issue between Pipelines for a repository in your oldest Workspace + +#### Endpoint + +`POST /p1/repositories/:repo_id/issues/:issue_number/moves` + +#### URL Parameters + +| Name | Type | Comments | +| -------------- | -------- | -------- | +| `repo_id` | `Number` | Required | +| `issue_number` | `Number` | Required | + +#### Body Parameters + +| Name | Type | Comments | +| ------------- | -------------------- | -------- | +| `pipeline_id` | `String` | Required | +| `position` | `String` or `Number` | Required | + +#### Notes + +- `pipeline_id` is the ID for one of the Pipelines in your oldest Workspace(i.e: In Progress, Done, QA). In order to obtain this ID, you can use the [_Get the oldest ZenHub Board for a repository_](#get-the-oldest-zenhub-board-for-a-repository) endpoint. +- `position` can be specified as `top` or `bottom`, or a `0`-based position in the Pipeline such as `1`, which would be the second position in the Pipeline. + +#### Example Request Body + +```json +{ + "pipeline_id": "58bf13aba426771426665e60", + "position": "top" +} +``` + +#### Example Response + +Status `200` for a successful move. No response body. + +### Set Issue Estimate + +#### Endpoint + +`PUT /p1/repositories/:repo_id/issues/:issue_number/estimate` + +#### URL Parameters + +| Name | Type | Comments | +| -------------- | -------- | -------- | +| `repo_id` | `Number` | Required | +| `issue_number` | `Number` | Required | + +#### Body Parameters + +| Name | Type | Comments | +| ---------- | -------- | -------------------------------------------- | +| `estimate` | `Number` | Required, number representing estimate value | + +#### Example Request + +```json +{ "estimate": 15 } +``` + +#### Example Response + +```json +{ "estimate": 15 } +``` + +## Epics + +- [Get Epics for a Repository](#get-epics-for-a-repository) +- [Get Epic Data](#get-epic-data) +- [Convert an Epic to an Issue](#convert-an-epic-to-an-issue) +- [Convert an Issue to Epic](#convert-issue-to-epic) +- [Add or Remove Issues from an Epic](#add-or-remove-issues-to-epic) + +### Get Epics for a repository + +Get all Epics for a repository + +#### Endpoint + +`GET /p1/repositories/:repo_id/epics` + +#### URL Parameters + +| Name | Type | Comments | +| --------- | -------- | -------- | +| `repo_id` | `Number` | Required | + +#### Example Response + +```json +{ + "epic_issues": [ + { + "issue_number": 3953, + "repo_id": 1234567, + "issue_url": "https://github.com/RepoOwner/RepoName/issues/3953" + }, + { + "issue_number": 1342, + "repo_id": 1234567, + "issue_url": "https://github.com/RepoOwner/RepoName/issues/1342" + } + ] +} +``` + +#### Notes + +- The endpoint returns an array of the repository’s Epics. The issue number, repository ID, + and GitHub issue URL is provided for each Epic. +- If an issue is only an issue belonging to an Epic (and not a parent Epic), it is not considered an Epic and won’t be included in the return array. + +### Get Epic Data + +Get the data for an Epic issue. + +#### Endpoint + +`GET /p1/repositories/:repo_id/epics/:epic_id` + +#### URL Parameters + +| Name | Type | Comments | +| --------- | -------- | ----------------------------- | +| `repo_id` | `Number` | Required | +| `epic_id` | `Number` | Required, Github issue number | + +#### Notes + +- `epic_id` is the GitHub issue number. You may fetch the list of Epics using `Get Epics for a repository` endpoint. +- The Epic and the Epic's issues contain `pipeline` (the oldest Workspace's pipeline) and `pipelines` (all Workspace Pipelines that issue is in). + +#### Example Response + +```json +{ + "total_epic_estimates": { "value": 60 }, + "estimate": { "value": 10 }, + "pipeline": { + "workspace_id": "5d0a7a9741fd098f6b7f58ac", + "name": "Backlog", + "pipeline_id": "5d0a7a9741fd098f6b7f58a8" + }, + "pipelines": [ + { + "workspace_id": "5d0a7a9741fd098f6b7f58ac", + "name": "Backlog", + "pipeline_id": "5d0a7a9741fd098f6b7f58a8" + }, + { + "workspace_id": "5d0a7cea41fd098f6b7f58b8", + "name": "In Progress", + "pipeline_id": "5d0a7cea41fd098f6b7f58b5" + } + ], + "issues": [ + { + "issue_number": 3161, + "is_epic": true, + "repo_id": 1099029, + "estimate": { "value": 40 }, + "pipelines": [ + { + "workspace_id": "5d0a7a9741fd098f6b7f58ac", + "name": "Backlog", + "pipeline_id": "5d0a7a9741fd098f6b7f58a8" + }, + { + "workspace_id": "5d0a7cea41fd098f6b7f58b8", + "name": "In Progress", + "pipeline_id": "5d0a7cea41fd098f6b7f58b5" + } + ], + "pipeline": { + "workspace_id": "5d0a7a9741fd098f6b7f58ac", + "name": "Backlog", + "pipeline_id": "5d0a7a9741fd098f6b7f58a8" + } + }, + { + "issue_number": 2, + "is_epic": false, + "repo_id": 1234567, + "estimate": { "value": 10 }, + "pipelines": [ + { + "workspace_id": "5d0a7a9741fd098f6b7f58ac", + "name": "Backlog", + "pipeline_id": "5d0a7a9741fd098f6b7f58a8" + }, + { + "workspace_id": "5d0a7cea41fd098f6b7f58b8", + "name": "In Progress", + "pipeline_id": "5d0a7cea41fd098f6b7f58b5" + } + ], + "pipeline": { + "workspace_id": "5d0a7a9741fd098f6b7f58ac", + "name": "Backlog", + "pipeline_id": "5d0a7a9741fd098f6b7f58a8" + } + } + ] +} +``` + +#### Notes + +The endpoint returns: + +- the total Epic Estimate value (the sum of all the Estimates of Issues contained within + the Epic, as well as the Estimate of the Epic itself) +- the Estimate of the Epic +- the name of the Pipeline the Epic is in +- issues belonging to the Epic + +For each issue belonging to the Epic: + +- issue number +- repo ID +- Estimate value +- `is_epic` flag (`true` or `false`) + +### Convert an Epic to an Issue + +Converts an Epic back to a regular issue. + +#### Endpoint + +`POST /p1/repositories/:repo_id/epics/:issue_number/convert_to_issue` + +#### URL Parameters + +| Name | Type | Comments | +| -------------- | -------- | ------------------------------------------------- | +| `repo_id` | `Number` | Required | +| `issue_number` | `Number` | Required, the number of the issue to be converted | + +#### Example Response + +- `200` if the issue was converted to Epic successfully + +Does not return any body in the response. + +### Convert Issue to Epic + +Converts an issue to an Epic, along with any issues that should be part of it. + +#### Endpoint + +`POST /p1/repositories/:repo_id/issues/:issue_number/convert_to_epic` + +#### URL Parameters + +| Name | Type | Comments | +| -------------- | -------- | -------- | +| `repo_id` | `Number` | Required | +| `issue_number` | `Number` | Required | + +#### Body Parameters + +| Name | Type | Comments | +| -------- | ------------------------------------------- | ------------------------------------------------------------ | +| `issues` | `[{repo_id: Number, issue_number: Number}]` | Required, array of Objects with `repo_id` and `issue_number` | + +#### Example Request Body + +```json +{ + "issues": [ + { "repo_id": 13550592, "issue_number": 3 }, + { "repo_id": 13550592, "issue_number": 1 } + ] +} +``` + +#### Response + +Does not return any body in the response. + +- `200` if the issue was converted to Epic successfully +- `400` if the supplied issue is already an Epic + +### Add or remove issues to Epic + +Bulk add or remove issues to an Epic. The result returns which issue was added or removed from the Epic. + +#### Endpoint + +`POST /p1/repositories/:repo_id/epics/:issue_number/update_issues` +#### URL Parameters + +| Name | Type | Comments | +| -------------- | -------- | -------- | +| `repo_id` | `Number` | Required | +| `issue_number` | `Number` | Required | + +#### Body Parameters + +| Name | Type | Comments | +| --------------- | ------------------------------------------- | ------------------------------------------------------------ | +| `remove_issues` | [`{repo_id: Number, issue_number: Number}`] | Required, array of Objects with `repo_id` and `issue_number` | +| `add_issues` | [`{repo_id: Number, issue_number: Number}`] | Required, array of Objects with `repo_id` and `issue_number` | + +#### Example Request Body + +```json +{ + "remove_issues": [{ "repo_id": 13550592, "issue_number": 3 }], + "add_issues": [ + { "repo_id": 13550592, "issue_number": 2 }, + { "repo_id": 13550592, "issue_number": 1 } + ] +} ``` -GET https://api.zenhub.io/p1/repositories/:repo_id/board + +#### Notes + +- `remove_issues` is an array that indicates with issues we want to remove from the specified Epic. They should be specified as an array containing objects with the issue’s `repo_id` and `issue_number`. +- `add_issues` is an array that indicates with issues we want to add to the specified Epic. They should be specified as an array containing objects with the issue’s `repo_id` and `issue_number`. + +#### Example Response + +```json +{ + "removed_issues": [{ "repo_id": 3887883, "issue_number": 3 }], + "added_issues": [ + { "repo_id": 3887883, "issue_number": 2 }, + { "repo_id": 3887883, "issue_number": 1 } + ] +} ``` -Note: The `repo_id` is the ID of the repository, not the full name. For example, the ID of the `ZenHubIO/support` repository is `13550592`. Use [GitHub's API](https://developer.github.com/v3/repos/#get) to find out your repository's ID. +#### Notes + +- `removed_issues` shows which issues were removed in this operation. +- `add_issues` shows which issues were added in this operation. +- Returns a `404` if the Epic doesn’t exist + +## Workspace + +- [Get ZenHub Workspaces for a repository](#get-zenhub-workspaces-for-a-repository) +- [Get a ZenHub Board for a repository](#get-a-zenhub-board-for-a-repository) +- [Get the oldest ZenHub Board for a repository](#get-the-oldest-zenhub-board-for-a-repository) -For example, the URL to fetch the [ZenHubIO/support](https://github.com/ZenHubIO/support#boards) board would be `https://api.zenhub.io/p1/repositories/13550592/board`. +### Get ZenHub Workspaces for a repository -The endpoint returns the Board's pipelines, plus the issues contained within each pipeline. It returns each issues' _issue number_, its _position_ in the board, an _is epic_ flag(true/false), and its _Time Estimate_ (if one is assigned). +Gets all Workspaces containing `repo_id` -Even if the issues are returned in the right order, the _position_ can't be guessed from its index. Notice some issues won't have _position_ – this is because they have not been prioritized on your Board. +#### Endpoint -Note: Closed issues might take up to one minute to show up in the Closed pipeline. Similarly, reopened issues might take up to one minute to show in the right pipeline. +`GET /p2/repositories/:repo_id/workspaces` + +#### URL Parameters + +| Name | Type | Comments | +| --------- | -------- | -------- | +| `repo_id` | `Number` | Required | + +#### Example Response + +```json +[ + { + "name": "Design and UX", + "description": null, + "id": "5d0a7a9741fd098f6b7f58ac", + "repositories": [12345678, 912345] + }, + { + "name": "Roadmap", + "description": "Feature planning and enhancements", + "id": "5d0a7cea41fd098f6b7f58b8", + "repositories": [12345678] + } +] +``` + +### Get a ZenHub Board for a repository + +Get ZenHub Board data for a repository (`repo_id`) within the Workspace (`workspace_id`) + +#### Endpoint + +`GET /p2/workspaces/:workspace_id/repositories/:repo_id/board` + +#### URL Parameters + +| Name | Type | Comments | +| -------------- | -------- | -------- | +| `repo_id` | `Number` | Required | +| `workspace_id` | `String` | Required | + +#### Example Response -This is an example of returned JSON data: ```json { "pipelines": [ { + "id": "595d430add03f01d32460080", "name": "New Issues", "issues": [ { "issue_number": 279, - "estimate": { - "value": 40 - }, + "estimate": { "value": 40 }, "position": 0, "is_epic": true }, @@ -169,26 +727,24 @@ This is an example of returned JSON data: ] }, { + "id": "595d430add03f01d32460081", "name": "Backlog", "issues": [ { "issue_number": 303, - "estimate": { - "value": 40 - }, + "estimate": { "value": 40 }, "position": 3, "is_epic": false } ] }, { + "id": "595d430add03f01d32460082", "name": "To Do", "issues": [ { "issue_number": 380, - "estimate": { - "value": 1 - }, + "estimate": { "value": 1 }, "position": 0, "is_epic": true }, @@ -199,9 +755,7 @@ This is an example of returned JSON data: }, { "issue_number": 329, - "estimate": { - "value": 8 - }, + "estimate": { "value": 8 }, "position": 7, "is_epic": false } @@ -211,145 +765,595 @@ This is an example of returned JSON data: } ``` -### Get Epics for a repository +### Get the oldest ZenHub board for a repository -``` -GET https://api.zenhub.io/p1/repositories/:repo_id/epics -``` +#### Endpoint -Please note: `repo_id` is the ID of the repository, not its full name. For example, the ID of the `ZenHubIO/support` repository is `13550592`. -To find out the ID of your repository, use [GitHub's API](https://developer.github.com/v3/repos/#get). +`GET /p1/repositories/:repo_id/board` -For example, the URL to fetch the [ZenHubIO/support](https://github.com/ZenHubIO/issues) epics would be `https://api.zenhub.io/p1/repositories/13550592/epics`. +#### URL Parameters -The endpoint returns an array of the repository's epics. For each epic, _issue number_, _repository ID_, and the _GitHub issue URL_ is provided. +| Name | Type | Comments | +| --------- | -------- | -------- | +| `repo_id` | `Number` | Required | -Note: If an issue is only an issue belonging to an epic (and not a parent epic), it is not considered an epic and won't be included in the return array. - -Here is an example of returned JSON data: +#### Example Response ```json { - "epic_issues": [ + "pipelines": [ { - "issue_number": 3953, - "repo_id": 1234567, - "issue_url": "https://github.com/RepoOwner/RepoName/issues/3953" + "id": "595d430add03f01d32460080", + "name": "New Issues", + "issues": [ + { + "issue_number": 279, + "estimate": { "value": 40 }, + "position": 0, + "is_epic": true + }, + { + "issue_number": 142, + "is_epic": false + } + ] }, { - "issue_number": 1342, - "repo_id": 1234567, - "issue_url": "https://github.com/RepoOwner/RepoName/issues/1342" + "id": "595d430add03f01d32460081", + "name": "Backlog", + "issues": [ + { + "issue_number": 303, + "estimate": { "value": 40 }, + "position": 3, + "is_epic": false + } + ] }, + { + "id": "595d430add03f01d32460082", + "name": "To Do", + "issues": [ + { + "issue_number": 380, + "estimate": { "value": 1 }, + "position": 0, + "is_epic": true + }, + { + "issue_number": 284, + "position": 2, + "is_epic": false + }, + { + "issue_number": 329, + "estimate": { "value": 8 }, + "position": 7, + "is_epic": false + } + ] + } ] } ``` -### Get Epic data +#### Notes + +- The endpoint returns the Board’s pipelines, plus the issues contained within each Pipeline. It returns the issue number of each issue, their position in the Board, the `is_epic` flag (`true` or `false`), and its Estimate (if set). +- Even if the issues are returned in the right order, the position can’t be guessed from its index. Note that some issues won’t have position – this is because they have not been prioritized on your Board. +- The Board returned by the endpoint doesn’t include closed issues. To get closed issues for a repository, you can use the GitHub API. Reopened issues might take up to one minute to appear in the correct Pipeline. + +## Milestones + +- [Set the Milestone Start Date](#set-milestone-start-date) +- [Get the Milestone Start Date](#get-milestone-start-date) + +### Set milestone start date +#### Endpoint + +`POST /p1/repositories/:repo_id/milestones/:milestone_number/start_date` + +#### URL Parameters + +| Name | Type | Comments | +| ------------------ | -------- | -------- | +| `repo_id` | `Number` | Required | +| `milestone_number` | `Number` | Required | + +#### Body Parameters + +| Name | Type | Comments | +| ------------ | ------------------- | -------- | +| `start_date` | ISO8601 date string | Required | + +#### Example Request Body + +```json +{ "start_date": "2010-11-13T01:38:56.842Z" } ``` -GET https://api.zenhub.io/p1/repositories/:repo_id/epics/:epic_id + +#### Example Response + +```json +{ "start_date": "2010-11-13T01:38:56.842Z" } +``` + +### Get milestone start date + +#### Endpoint + +`GET /p1/repositories/:repo_id/milestones/:milestone_number/start_date` + +#### URL Parameters + +| Name | Type | Comments | +| ------------------ | -------- | -------- | +| `repo_id` | `Number` | Required | +| `milestone_number` | `Number` | Required | + +#### Example Response + +```json +{ "start_date": "2010-11-13T01:38:56.842Z" } ``` -Please note: `repo_id` is the ID of the repository, not its full name. For example, the ID of the `ZenHubIO/support` repository is `13550592`. -To find out the ID of your repository, use [GitHub's API](https://developer.github.com/v3/repos/#get). -`epic_id` is the GitHub issue number (you may fetch the list of epics using **Get Epics for a repository** endpoint). +## Dependencies + +- [Get Dependencies for a Repository](#get-dependencies-for-a-repository) +- [Create a Dependency](#create-a-dependency) +- [Remove a Dependency](#remove-a-dependency) + +### Get Dependencies for a Repository + +#### Endpoint -The endpoint returns the _total estimate epic value_ (the sum of the epic's estimate, plus all estimates contained within it), the _estimate of the epic_, _pipeline name_ the epic is in, and the _issues_ belonging to it. For each issue belonging to the epic, its _issue number_, _repo id_, _estimate value_, _is epic_ flag (true/false) are provided; in addition, if the issue is from the same repository as the epic, the ZenHub Board's _pipeline name_ (from the repo the epic is in) is attached. +`GET /p1/repositories/:repo_id/dependencies` -NOTE: If an issue is from a different repository than the epic it belongs to, the pipeline name is not attached. +#### URL Parameters + +| Name | Type | Comments | +| --------- | -------- | -------- | +| `repo_id` | `Number` | Required | + +#### Example Response ```json { - "total_epic_estimates": { - "value": 60 - }, - "issues": [ + "dependencies": [ { - "issue_number": 3161, - "is_epic": true, - "repo_id": 1099029, - "estimate": { - "value": 40 + "blocking": { + "issue_number": 3953, + "repo_id": 1234567 }, - "pipeline": { - "name": "New Issues" + "blocked": { + "issue_number": 1342, + "repo_id": 1234567 } }, { - "issue_number": 2, - "is_epic": false, - "repo_id": 1234567, - "estimate": { - "value": 10 + "blocking": { + "issue_number": 5, + "repo_id": 987 }, - "pipeline": { - "name": "New Issues" + "blocked": { + "issue_number": 1342, + "repo_id": 1234567 } - }, - { - "issue_number": 1, - "is_epic": false, - "repo_id": 1234567 - }, - { - "issue_number": 6, - "is_epic": false, - "repo_id": 1234567 - }, - { - "issue_number": 7, - "is_epic": true, - "repo_id": 9876543 } - ], - "estimate": { - "value": 10 + ] +} +``` + +#### Notes + +- This endpoint fetches all dependencies associated to the given repository that the user has read permission to +- The endpoint takes a `repo_id` param in the URL. +- Only dependencies where the user has read permissions to both sides will be returned + +## Create a Dependency + +#### Endpoint + +`POST /p1/dependencies` + +#### Body Parameters + +| Name | Type | Comments | +| ----------------------- | -------- | -------- | +| `blocking` | `Object` | Required | +| `blocking.repo_id` | `Number` | Required | +| `blocking.issue_number` | `Number` | Required | +| `blocked` | `Object` | Required | +| `blocked.repo_id` | `Number` | Required | +| `blocked.issue_number` | `Number` | Required | + +#### Example Request Body + +```json +{ + "blocking": { + "repo_id": 92563409, + "issue_number": 14 }, - "pipeline": { - "name": "Backlog" + "blocked": { + "repo_id": 92563409, + "issue_number": 13 } } ``` +#### Example Response Body +```json +{ + "blocking": { + "repo_id": 92563409, + "issue_number": 14 + }, + "blocked": { + "repo_id": 92563409, + "issue_number": 13 + } +} +``` -## API limits +#### Notes -We allow 100 requests per minute to our API. All requests responses include some headers related to this limitation. +- This endpoint creates one dependency +- The endpoint takes a `dependency` in the Body (see description above). +- User needs write permission on both repositories +- Cannot create dependency that will cause cycle, or between repositories not in the same workspace +- On success: returns HTTP 200 and returns the created object -Header | Meaning ------- | ------- -X-RateLimit-Limit | Total number of requests allowed before the reset time -X-RateLimit-Used | Number of requests sent in the current cycle. Will be set to 0 at the reset time. -X-RateLimit-Reset | Time in UTC epoch seconds when the usage gets reset. +## Remove a Dependency -To avoid time differences between your computer and our servers, we suggest to use the `Date` header in the response to know exactly when the limit is reset. +#### Endpoint -## Errors +`DELETE /p1/dependencies` -The ZenHub API can return the following errors: +#### Body Parameters + +| Name | Type | Comments | +| ----------------------- | -------- | -------- | +| `blocking` | `Object` | Required | +| `blocking.repo_id` | `Number` | Required | +| `blocking.issue_number` | `Number` | Required | +| `blocked` | `Object` | Required | +| `blocked.repo_id` | `Number` | Required | +| `blocked.issue_number` | `Number` | Required | + +#### Example Request Body + +```json +{ + "blocking": { + "repo_id": 92563409, + "issue_number": 14 + }, + "blocked": { + "repo_id": 92563409, + "issue_number": 13 + } +} +``` -Status Code | Meaning ------------ | ------- -401 | The token is not valid. See [Authentication](#authentication). -403 | Reached request limit to the API. See [API Limits](#api-limits). -404 | Not found. +#### Notes +- This endpoint removes one dependency +- The endpoint takes a `dependency` in the Body (see description above). +- User needs write permission on both repositories +- On success: returns HTTP 204 No Content and empty body -## Webhooks +## Release Reports + +- [Create a Release Report](#create-a-release-report) +- [Get a Release Report](#get-a-release-report) +- [Get Release Reports for a Repository](#get-release-reports-for-a-repository) +- [Edit a Release Report](#edit-a-release-report) +- [Add a Repository to a Release Report](#add-a-repository-to-a-release-report) +- [Remove a Repository from a Release Report](#remove-a-repository-from-a-release-report) + +### Create a Release Report + +#### Endpoint + +`POST /p1/repositories/:repo_id/reports/release` + +#### URL Parameters + +| Name | Type | Comments | +| --------- | -------- | -------- | +| `repo_id` | `Number` | Required | + +#### Body Parameters + +| Name | Type | Comments | +| ------------------ | ------------------- | -------- | +| `title` | `String` | Required | +| `description` | `String` | Optional | +| `start_date` | ISO8601 date string | Required | +| `desired_end_date` | ISO8601 date string | Required | +| `repositories` | `[Number]` | Optional | + +#### Example Request Body + +```json +{ + "title": "Great title", + "description": "Amazing description", + "start_date": "2007-01-01T00:00:00Z", + "desired_end_date": "2007-01-01T00:00:00Z", + "repositories": [103707262] +} +``` + +#### Example Response + +```json +{ + "release_id": "59dff4f508399a35a276a1ea", + "title": "Great title", + "description": "Amazing description", + "start_date": "2007-01-01T00:00:00.000Z", + "desired_end_date": "2007-01-01T00:00:00.000Z", + "created_at": "2017-10-12T23:04:21.795Z", + "closed_at": null, + "state": "open", + "repositories": [103707262] +} +``` + +#### Notes + +- **CHANGE NOTICE:** Only the repositories provided in the param and the body will be added to the Release Report - change in effect mid February 2019. +- The endpoint takes a `repo_id` param in the URL. +- Additional repository IDs can be passed in the body `repositories` parameter +- Any Boards not associated with the URL `repo_id` parameter, but associated with repositories in the request body `repositories` parameter will also be associated to the Release Report. +- The user creating the release requires push permission to the repositories in the request. + +### Get a Release Report + +#### Endpoint + +`GET /p1/reports/release/:release_id` + +#### URL Parameters + +| Name | Type | Comments | +| ------------ | -------- | -------- | +| `release_id` | `String` | Required | + +#### Example Response + +```json +{ + "release_id": "59d3cd520a430a6344fd3bdb", + "title": "Test release", + "description": "", + "start_date": "2017-10-01T19:00:00.000Z", + "desired_end_date": "2017-10-03T19:00:00.000Z", + "created_at": "2017-10-03T17:48:02.701Z", + "closed_at": null, + "state": "open", + "repositories": [105683718] +} +``` + +### Get Release Reports for a Repository + +#### Endpoint + +`GET /p1/repositories/:repo_id/reports/releases` + +#### URL Parameters + +| Name | Type | Comments | +| --------- | -------- | -------- | +| `repo_id` | `Number` | Required | + +#### Example Response + +```json +[ + { + "release_id": "59cbf2fde010f7a5207406e8", + "title": "Great title for release 1", + "description": "Great description for release", + "start_date": "2000-10-10T00:00:00.000Z", + "desired_end_date": "2010-10-10T00:00:00.000Z", + "created_at": "2017-09-27T18:50:37.418Z", + "closed_at": null, + "state": "open" + }, + { + "release_id": "59cbf2fde010f7a5207406e8", + "title": "Great title for release 2", + "description": "Great description for release", + "start_date": "2000-10-10T00:00:00.000Z", + "desired_end_date": "2010-10-10T00:00:00.000Z", + "created_at": "2017-09-27T18:50:37.418Z", + "closed_at": null, + "state": "open" + } +] +``` + +### Edit a Release Report + +#### Endpoint + +`PATCH /p1/reports/release/:release_id` + +#### URL Parameters + +| Name | Type | Comments | +| ------------ | -------- | -------- | +| `release_id` | `String` | Required | + +#### Body Parameters + +| Name | Type | Comments | +| ------------------ | ------------------- | ---------------------------- | +| `title` | `String` | Required | +| `description` | `String` | Optional | +| `start_date` | ISO8601 date string | Optional | +| `desired_end_date` | ISO8601 date string | Optional | +| `state` | `String` | Optional, `open` or `closed` | + +#### Example Request Body + +```json +{ + "title": "Amazing title", + "description": "Amazing description", + "start_date": "2007-01-01T00:00:00Z", + "desired_end_date": "2007-01-01T00:00:00Z", + "state": "closed" +} +``` + +#### Example Response + +```json +{ + "release_id": "59d3d6438b3f16667f9e7174", + "title": "Amazing title", + "description": "Amazing description", + "start_date": "2007-01-01T00:00:00.000Z", + "desired_end_date": "2007-01-01T00:00:00.000Z", + "created_at": "2017-10-03T18:26:11.700Z", + "closed_at": "2017-10-03T18:26:11.700Z", + "state": "closed", + "repositories": [105683567, 105683718] +} +``` + +### Add a Repository to a Release Report + +#### Endpoint + +`POST /p1/reports/release/:release_id/repository/:repo_id` + +#### URL Parameters + +| Name | Type | Comments | +| ------------ | -------- | -------- | +| `release_id` | `String` | Required | +| `repo_id` | `Number` | Required | + +#### Notes + +- On success, returns HTTP 200 OK and empty body + +### Remove a Repository from a Release Report + +#### Endpoint + +`DELETE /p1/reports/release/:release_id/repository/:repo_id` + +#### URL Parameters + +| Name | Type | Comments | +| ------------ | -------- | -------- | +| `release_id` | `String` | Required | +| `repo_id` | `Number` | Required | + +#### Notes + +- On success, returns HTTP 204 OK and empty body + +## Release Report Issues + +- [Get all the Issues in a Release Report](#get-all-the-issues-for-a-release-report) +- [Add or Remove Issues from a Release Report](#add-or-remove-issues-to-or-from-a-release-report) + +### Get all the Issues for a Release Report + +#### Endpoint + +`GET /p1/reports/release/:release_id/issues` + +#### URL Parameters + +| Name | Type | Comments | +| ------------ | -------- | -------- | +| `release_id` | `String` | Required | + +#### Example Response + +```json +[ + { "repo_id": 103707262, "issue_number": 2 }, + { "repo_id": 103707262, "issue_number": 3 } +] +``` + +### Add or Remove Issues to or from a Release Report + +#### Endpoint + +`PATCH /p1/reports/release/:release_id/issues` + +#### URL Parameters + +| Name | Type | Comments | +| ------------ | -------- | -------- | +| `release_id` | `String` | Required | + +#### Body Parameters + +| Name | Type | Comments | +| --------------- | ------------------------------------------- | ------------------------------------------------------------ | +| `add_issues` | `[{repo_id: Number, issue_number: Number}]` | Required, array of Objects with `repo_id` and `issue_number` | +| `remove_issues` | `[{repo_id: Number, issue_number: Number}]` | Required, array of Objects with `repo_id` and `issue_number` | + +#### Note + +- Both the `add_issues` and `remove_issues` keys are required, but can be an empty array when not used + +#### Example Body Request + +```json +{ + "add_issues": [{ "repo_id": 103707262, "issue_number": 3 }], + "remove_issues": [] +} +``` + +#### Example Response + +```json +{ + "added": [{ "repo_id": 103707262, "issue_number": 3 }], + "removed": [] +} +``` + +#### Note + +- Adding and removing issues can be done in the same request by populating with the `add_issues` and `remove_issues` keys. + +### Webhooks You can use our webhooks to fetch or store your ZenHub data, in real time, across services like Slack, Gitter, Spark, HipChat, or something custom! -To set up an integration, head on over to our [Dashboard](https://dashboard.zenhub.com/), navigate to your organization, and select the **Integrations** tab. From there, you may choose one of the 5 services (Slack, HipChat, Gitter, Spark, or Custom). +To set up an integration, head on over to our [Dashboard](https://app.zenhub.com/dashboard), navigate to your organization, and select the **Slack & Integrations** tab. From there, you may choose one of the 5 services (Slack, HipChat, Gitter, Spark, or Custom). For instructions, you'll notice the `How to create a webhook` link changes dynamically based on the service you select. Simply choose a repository with which to connect, add an optional description, paste your webhook, and click "Add" to save your new integration. -ZenHub +ZenHub integrations ### Custom webhooks -Our custom webhook sends a POST request to your webhook for multiple events that occur on your ZenHub board: +Our custom webhook sends a POST request to your webhook for multiple events that occur on your ZenHub board. See below for examples of the events and data that they will contain. Please note that the content type in the examples has been written in JSON, however the actual data is sent in **x-www-form-urlencoded** format. + +#### Content Type: urlencoded + +The POST request is sent in the **x-www-form-urlencoded** format. + +Example: +```field1=value1&field2=value2``` #### Issue transfer @@ -363,6 +1367,8 @@ Our custom webhook sends a POST request to your webhook for multiple events that "issue_number": "618", "issue_title": "ZenHub Change Log", "to_pipeline_name": "New Issues", + "workspace_id": "603fc3e575de63001cc163f9", + "workspace_name": "My Workspace", "from_pipeline_name": "Discussion" } ``` @@ -392,7 +1398,7 @@ Our custom webhook sends a POST request to your webhook for multiple events that "repo": "support", "user_name": "ZenHubIO", "issue_number": "618", - "issue_title": "ZenHub Change Log", + "issue_title": "ZenHub Change Log" } ``` @@ -409,32 +1415,31 @@ Our custom webhook sends a POST request to your webhook for multiple events that "issue_title": "ZenHub Change Log", "to_pipeline_name": "Backlog", "from_position": "4", - "to_position": "0" + "to_position": "0", + "workspace_id": "603fc3e575de63001cc163f9", + "workspace_name" "My Workspace" } ``` +As an example, here's a simple Node/Express app that would be able receive the webhooks (using ngrok): -As an example, here's a simple Node/Express app that would be able receive the webhooks(using ngrok): +```javascript +var express = require('express'); +var http = require('http'); +var bodyParser = require('body-parser'); +var app = express(); -``` -var express = require('express'); -var http = require('http'); -var bodyParser = require('body-parser'); -var app = express(); - -http.createServer(app).listen('6000', function(){ - console.log('Listening on 6000'); +http.createServer(app).listen('6000', function() { + console.log('Listening on 6000'); }); app.use(bodyParser()); - -app.post('*', function( req, res) { - console.dir(req.body); +app.post('*', function(req, res) { + console.dir(req.body); }); ``` +# Contact us -## Contact us - -We'd love to hear from you. If you have any questions, concerns, or ideas related to the ZenHub API, open an issue in our [Support](https://github.com/ZenHubIO/support/issues#boards) repo or find us on [Twitter](http://www.twitter.com/zenhubio). +We’d love to hear from you. If you have any questions, concerns, or ideas related to the ZenHub API, please reach us at [support@zenhub.com](mailto:support@zenhub.com) or find us on [Twitter](https://twitter.com/zenhubstatus).