API Reference

Canny's API lets you access and modify your Canny data programmatically. We also support webhooks, where we notify your server of events as they happen.

The API expects the request to use the POST HTTP method, the body as JSON, and the Content-Type header set to "application/json".

This API enables you to seamlessly integrate Canny with your existing services.

Authentication

API requests must be authenticated by including your secret API key. You can find your secret API key in your company settings. Store it on your server and don't share it.

You can include your secret API key in a request by adding it as a POST parameter with key apiKey.

Example request:
$ curl https://canny.io/api/v1/boards/list \
    -d apiKey=YOUR_API_KEY

Pagination

Endpoints which list many results will have a limit on the number of results that can be returned, and pagination is the mechanism to retrieve results beyond that limit. To fetch more results, make a subsequent request with the necessary pagination parameters.

There are two approaches to pagination in the Canny API:

  • Cursor: Request returns a cursor, which is used to fetch the next page.
  • Skip: The skip parameter allows you to bypass the first N results.

Not all endpoints use the same approach, review the documentation for each endpoint to understand how to paginate.

Cursor pagination:
$ curl https://canny.io/api/v2/users/list \
    -d apiKey=YOUR_API_KEY \
    -d cursor=NTUzYzNlZjhiOGNkY2QxNTAxYmExMjNhXzIwMjUtMDItMDRUMTY6NDA6MDcuNTQxWl8x \
    -d limit=10
Skip pagination:
$ curl https://canny.io/api/v1/posts/list \
    -d apiKey=YOUR_API_KEY \
    -d skip=10 \
    -d limit=10

Rate limits

Requests are counted per API key across every endpoint, in one-second, one-minute and one-hour windows. The allowance depends on your plan.

Limits by plan
Businessper key
20 requests per second, 600 per minute, 15,000 per hour.
Core, Starter, Growth, Proper key
10 requests per second, 300 per minute, 5,000 per hour.
Freeper key
5 requests per second, 100 per minute, 1,000 per hour.

Requests are also counted per IP address, with the same allowance as a Business key. If several keys share one address, contact support and we can raise it.

Every response includes headers describing the window closest to its limit. When a limit is exceeded the request is not processed and the response has status 429 with a Retry-After header. Wait that many seconds before retrying.

Headers
X-RateLimit-Limitinteger
Maximum requests allowed in the current window.
X-RateLimit-Remaininginteger
Requests left in the current window.
X-RateLimit-Resetunix timestamp
When the current window resets, in seconds since the epoch.
Retry-Afterinteger
Seconds to wait before sending another request.

For bulk work such as syncing users, spread requests out or run a scheduled job that stays under the hourly limit rather than sending updates as they happen.

Example response headers:
HTTP/1.1 200 OK
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 587
X-RateLimit-Reset: 1790600460
Example rate limited response:
HTTP/1.1 429 Too Many Requests
Retry-After: 37
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1790600460

{"error":"slow down"}

Boards

Boards are the high-level objects where your users can post and vote on ideas for a specific topic, typically feature requests. The API allows you to fetch a specific board, or a list of all boards for your company.

The board object

Attributes
idstring
A unique identifier for the board.
createdstring
Time at which the board was created, in ISO 8601 format.
isPrivateboolean
Whether or not the board is set as private in the administrative settings.
namestring
The board's name.
postCountnumber
The number of non-deleted posts associated with the board. This number includes posts that are marked as closed or complete.
privateCommentsboolean
Whether or not comments left on posts can be viewed by other end-users.
urlstring
The URL to the board's page.
Example board object:
{
  "id": "6a2889c586d7b8843bf4cf01",
  "created": "2017-07-15T22:11:00.000Z",
  "name": "Feature Requests",
  "postCount": 123,
  "url": "https://your-company.canny.io/admin/board/feature-requests"
}

Retrieve board

Retrieves the details of an existing board, specified by its id.

Arguments
apiKeystringrequired
Your secret API key.
idstringrequired
The board's unique identifier.

Returns

Returns a board object, if a valid id was supplied.

Endpoint
POSThttps://canny.io/api/v1/boards/retrieve
Example Request
$ curl https://canny.io/api/v1/boards/retrieve -X POST \
    -d apiKey=YOUR_API_KEY \
    -d id=6a2889c586d7b8843bf4cf01
Example Response
{
  "id": "6a2889c586d7b8843bf4cf01",
  "created": "2017-07-15T22:11:00.000Z",
  "isPrivate": true,
  "name": "Feature Requests",
  "postCount": 123,
  "privateComments": false,
  "url": "https://your-company.canny.io/admin/board/feature-requests"
}

List all boards

Returns a list of all boards associated with your company, in no particular order.

Arguments
apiKeystringrequired
Your secret API key.

Returns

A dictionary with a "boards" property that contains an array of board objects.

Endpoint
POSThttps://canny.io/api/v1/boards/list
Example Request
$ curl https://canny.io/api/v1/boards/list -X POST \
    -d apiKey=YOUR_API_KEY
Example Response
{
  "boards": [{
    "id": "6a2889c586d7b8843bf4cf01",
    "created": "2026-11-01T04:00:00.000Z",
    "isPrivate": true,
    "name": "Feature Requests",
    "postCount": 123,
    "privateComments": false,
    "token": "11111111-2222-3333-4444-555555555555",
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  }, {
    "id": "6a2889c586d7b8843bf4cf05",
    "created": "2026-11-01T04:00:00.000Z",
    "isPrivate": false,
    "name": "Bug Reports",
    "postCount": 42,
    "privateComments": true,
    "token": "11111111-2222-3333-4444-555555555555",
    "url": "https://your-company.canny.io/admin/board/bug-reports"
  }]
}

Categories

Posts can be assigned categories. Each category is for a specific board, not company-wide.

Our API allows you to list, and retrieve categories. You can also change categories of posts.

The category object

Attributes
idstring
A unique identifier for the category.
boardBoard object
The board this category is associated with.
createdstring
Time at which the category was created, in ISO 8601 format.
namestring
The name of the category.
parentIDstring | null
The id of the parent category. If this category is not a sub category, this field will be null.
postCountnumber
The number of posts that have been assigned this category.
urlstring
The URL to the board, filtered to just posts that have been assigned this category.
Example category object:
{
  "id": "6a2889c586d7b8843bf4cf0b",
  "board": {
    "created": "2026-11-01T04:00:00.000Z",
    "id": "6a2889c586d7b8843bf4cf12",
    "name": "Feature Requests",
    "postCount": 99,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "created": "2026-11-01T04:00:00.000Z",
  "name": "Example Category Name",
  "parentID": "6a2889c586d7b8843bf4cf18",
  "postCount": 12,
  "url": "https://your-company.canny.io/admin/board/feature-requests?category=example-category-name"
}

Retrieve category

Retrieves the details of an existing category, specified by its id.

Arguments
apiKeystringrequired
Your secret API key.
idstringrequired
The category's unique identifier.

Returns

Returns a category object, if a valid id was supplied.

Endpoint
POSThttps://canny.io/api/v1/categories/retrieve
Example Request
$ curl https://canny.io/api/v1/categories/retrieve -X POST \
    -d apiKey=YOUR_API_KEY \
    -d id=6a2889c586d7b8843bf4cf0b
Example Response
{
  "id": "6a2889c586d7b8843bf4cf0b",
  "board": {
    "created": "2026-11-01T04:00:00.000Z",
    "id": "6a2889c586d7b8843bf4cf12",
    "name": "Feature Requests",
    "postCount": 99,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "created": "2026-11-01T04:00:00.000Z",
  "name": "Example Category Name",
  "parentID": "6a2889c586d7b8843bf4cf18",
  "postCount": 12,
  "url": "https://your-company.canny.io/admin/board/feature-requests?category=example-category-name"
}

List categories

Returns a list of categories. Include parameters to specify board and pagination. Sorted by newest.

Arguments
apiKeystringrequired
Your secret API key.
boardIDstringoptional
The id of the board you'd like to fetch categories for.
limitnumberoptional
The number of categories you'd like to fetch. Defaults to 10 if not specified. Max of 10000.
skipnumberoptional
The number of categories you'd like to skip before starting to fetch. Defaults to 0 if not specified.

Returns

A dictionary with a "categories" property that contains an array of tag objects. There's also a "hasMore" property that specifies whether this query returns more categories than the limit.

Endpoint
POSThttps://canny.io/api/v1/categories/list
Example Request
$ curl https://canny.io/api/v1/categories/list -X POST \
    -d apiKey=YOUR_API_KEY \
    -d boardID=6a2889c586d7b8843bf4cf01
Example Response
{
  "categories": [{
    "id": "6a2889c586d7b8843bf4cf0b",
    "board": {
      "created": "2026-11-01T04:00:00.000Z",
      "id": "6a2889c586d7b8843bf4cf12",
      "name": "Feature Requests",
      "postCount": 99,
      "url": "https://your-company.canny.io/admin/board/feature-requests"
    },
    "created": "2026-11-01T04:00:00.000Z",
    "name": "Example Category Name",
    "parentID": "6a2889c586d7b8843bf4cf18",
    "postCount": 12,
    "url": "https://your-company.canny.io/admin/board/feature-requests?category=example-category-name"
  }],
  "hasMore": false,
}

Create category

Creates a new category.

Arguments
apiKeystringrequired
Your secret API key.
boardIDstringrequired
The id of the board you'd like to create the category for.
namestringrequired
The name of the category. Must be between 1 and 30 characters long.
parentIDstringoptional
The id of the parent category.
subscribeAdminsbooleanrequired
Whether or not the admins will be subscribed to the category.

Returns

Upon success, returns an object with a single key: id.

Endpoint
POSThttps://canny.io/api/v1/categories/create
Example Request
$ curl https://canny.io/api/v1/categories/create -X POST \
  -d apiKey=YOUR_API_KEY \
  -d boardID=6a2889c586d7b8843bf4cf01 \
  -d name="new category" \
  -d parentID=6a2889c586d7b8843bf4cf17 \
  -d subscribeAdmins=true
  
Example Response
{
  "id": "6a2889c586d7b8843bf4cf09",
}

Delete category

Deletes a category If the category does not have sub categories.

Arguments
apiKeystringrequired
Your secret API key.
categoryIDstringrequired
The unique identifier of the category.

Returns

Returns "success" if the category was successfully deleted.

Endpoint
POSThttps://canny.io/api/v1/categories/delete
Example Request
$ curl https://canny.io/api/v1/categories/delete -X POST \
    -d apiKey=YOUR_API_KEY \
    -d categoryID=6a2889c586d7b8843bf4cf0c"
Example Response
success

Entries

A changelog is simply a list of entries. The API allows you to create, update, publish, schedule, delete, and fetch the list of entries associated with your changelog.

The changelog entry object

Attributes
idstring
A unique identifier for the entry.
createdstring
Time at which the entry was first created, in ISO 8601 format.
labelsarray
The list of labels that the entry is associated with. Each label has an id, created, entryCount, name, and url.
lastSavedstring
Time at which the entry was last updated, in ISO 8601 format.
markdownDetailsstring
The markdown contents of the entry.
plaintextDetailsstring
The plaintext contents of the entry, with images, videos, and links stripped.
postsarray
The list of posts this entry is linked to.
publishedAtstring | null
Time at which the entry was published, if it has been published.
reactionsobject | null
The number of reactions an entry has received.
scheduledForstring | null
Time at which the entry is scheduled to be published, if it is scheduled.
statusstring
The status of the entry, describing whether it has been published. Will be set to draft, scheduled, or published.
titlestring
The title of the entry.
typesarray
The list of types associated with the entry. Can include new, improved, or fixed.
urlstring
The public URL to the entry page on Canny.
Example entry object:
{
  "id": "6a2889c586d7b8843bf4cf08",
  "created": "2026-11-01T04:00:00.000Z",
  "labels": [{
    "created": "2026-11-01T04:00:00.000Z",
    "entryCount": 3,
    "id": "6a2889c586d7b8843bf4cf0a",
    "name": "Mobile",
    "url": "https://your-company.canny.io/admin/changelog?labels=mobile"
  }],
  "lastSaved": "2026-11-01T04:00:00.000Z",
  "markdownDetails": "# heading\n**bold**\n[link](https://canny.io)",
  "plaintextDetails": "heading\nbold\nlink",
  "posts": [{
    "board": {
      "created": "2026-11-01T04:00:00.000Z",
      "id": "6a2889c586d7b8843bf4cf01",
      "name": "Feature Requests",
      "postCount": 123,
      "url": "https://your-company.canny.io/admin/board/feature-requests"
    },
    "category": {
      "created": "2026-11-01T04:00:00.000Z",
      "id": "6a2889c586d7b8843bf4cf0d",
      "name": "Dashboard",
      "postCount": 42,
      "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
    },
    "commentCount": 2,
    "details": "Post details",
    "id": "6a2889c586d7b8843bf4cf13",
    "imageURLs": [],
    "jira": {
      "linkedIssues": [{
        "id": "123",
        "key": "ID-123",
        "url": "https://your-company.atlassian.net/browse/ID-123"
      }],
      "linkedIssueIDs": ["123"]
    },
    "linear": {
      "linkedIssueIDs": ["ID-123", "ID-345"]
    },
    "score": 13,
    "status": "planned",
    "tags": [{
      "id": "6a2889c586d7b8843bf4cf10",
      "name": "iOS",
      "postCount": 15,
      "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
    }],
    "title": "post-title",
    "url": "https://your-company.canny.io/admin/board/feature-requests/p/post-title"
  }],
  "publishedAt": "2026-11-01T04:00:00.000Z",
  "reactions": {
    "like": 2
  },
  "scheduledFor": null,
  "status": "published",
  "title": "Entry title",
  "types": ["new", "improved"],
  "url": "https://your-company.canny.io/changelog/entry-title"
}

Create entry

Creates a new changelog entry, and optionally publishes or schedules it.

Arguments
apiKeystringrequired
Your secret API key.
titlestringrequired
The title of your changelog entry.
detailsstringrequired
The details of your changelog entry.
typestringoptional
The type of your changelog entry. Can be one of "fixed", "new", and "improved".
notifybooleanoptional
Whether to notify users by email after publishing. Default is false.
publishedbooleanoptional
Whether you want to publish the changelog entry immediately or not. Default is false.
publishedOnstringoptional
If published is set to true and you would like to set a published date in the past, in ISO 8601 format.
scheduledForstringoptional
The date you want the changelog entry to be published, in ISO 8601 format. This has to be in the future, and published must be false.
labelIDsarrayoptional
A list of label IDs to assign to your changelog entry.
postIDsarrayoptional
A list of posts to link to your changelog entry. A post that is already linked to another entry cannot be added.

Returns

Upon success, returns an object with a single key: id.

Endpoint
POSThttps://canny.io/api/v1/entries/create
Example Request
$ curl https://canny.io/api/v1/entries/create -X POST \
  -d apiKey=YOUR_API_KEY \
  -d title="Sample entry" \
  -d details="Some details..." \
  -d type="fixed" \
  
Example Response
{
  "id": "6a2889c586d7b8843bf4cf09"
}

List entries

Returns a list of changelog entries. Unless filtered by status, this includes drafts and scheduled entries.

To request translated changelog items, use the Accept-Language header in your HTTP request to specify the preferred language for the response.

Arguments
apiKeystringrequired
Your secret API key.
labelIDsarrayoptional
Fetch only entries with at least one of the labels in the array.
limitnumberoptional
The number of entries you'd like to fetch, up to 10,000. Defaults to 10 if not specified.
skipnumberoptional
The number of entries you'd like to skip before starting to fetch, up to 1,000. Defaults to 0 if not specified.
sortstringoptional
The order in which the entries should be fetched. "created", "lastSaved", and "publishedAt" return the most recent first by that date. "nonPublishedFirst" returns drafts and scheduled entries first, most recently saved first, followed by published entries, most recently published first. Defaults to "nonPublishedFirst" if not specified.
statusstringoptional
A comma separated list of statuses to fetch. Values can be "draft", "scheduled", and "published". For example, "published" returns only published entries. Returns entries of every status if not specified.
typestringoptional
The type of entries to fetch. Value can be "new", "improved", or "fixed".

Returns

A dictionary with a "entries" property that contains an array of entry objects. There's also a "hasMore" property that specifies whether this query returns more entries than the limit.

Endpoint
POSThttps://canny.io/api/v1/entries/list
Example Request
$ curl https://canny.io/api/v1/entries/list -X POST \
    -d apiKey=YOUR_API_KEY
Example Response
{
  "hasMore": false,
  "entries": [{
    "id": "6a2889c586d7b8843bf4cf08",
    "created": "2026-11-01T04:00:00.000Z",
    "labels": [{
      "id": "6a2889c586d7b8843bf4cf15",
      "created": "2026-11-01T04:00:00.000Z",
      "entryCount": 4,
      "name": "Feature",
      "url": "https://your-company.canny.io/admin/changelog?labels=feature"
    }],
    "lastSaved": "2026-11-01T04:00:00.000Z",
    "markdownDetails": "# heading\n**bold**\n[link](https://canny.io)",
    "plaintextDetails": "heading\nbold\nlink",
    "posts": [{
      "board": {
        "created": "2026-11-01T04:00:00.000Z",
        "id": "6a2889c586d7b8843bf4cf01",
        "name": "Feature Requests",
        "postCount": 123,
        "url": "https://your-company.canny.io/admin/board/feature-requests"
      },
      "category": {
        "created": "2026-11-01T04:00:00.000Z",
        "id": "6a2889c586d7b8843bf4cf0d",
        "name": "Dashboard",
        "postCount": 42,
        "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
      },
      "commentCount": 2,
      "details": "Post details",
      "id": "6a2889c586d7b8843bf4cf13",
      "imageURLs": [],
      "jira": {
        "linkedIssues": [{
          "id": "123",
          "key": "ID-123",
          "url": "https://your-company.atlassian.net/browse/ID-123"
        }],
        "linkedIssueIDs": ["123"]
      },
      "linear": {
        "linkedIssueIDs": ["ID-123", "ID-345"]
      },
      "score": 13,
      "status": "planned",
      "tags": [{
        "id": "6a2889c586d7b8843bf4cf10",
        "name": "iOS",
        "postCount": 15,
        "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
      }],
      "title": "post-title",
      "url": "https://your-company.canny.io/admin/board/feature-requests/p/post-title"
    }],
    "publishedAt": "2026-11-01T04:00:00.000Z",
    "reactions": {
      "like": 2
    },
    "scheduledFor": null,
    "status": "published",
    "title": "Entry title",
    "types": ["new", "improved"],
    "url": "https://your-company.canny.io/changelog/entry-title"
  }]
}

Update entry

Updates the content of an existing changelog entry. Only the arguments you provide are changed; omitted arguments are left untouched. This does not change whether the entry is published or scheduled.

Arguments
apiKeystringrequired
Your secret API key.
entryIDstringrequired
The unique identifier of the changelog entry.
detailsstringoptional
The details of your changelog entry.
labelIDsarrayoptional
Replaces the labels on your changelog entry. Pass an empty array to remove all labels.
notifybooleanoptional
Whether to notify users by email after publishing.
postIDsarrayoptional
Replaces the posts linked to your changelog entry. A post that is already linked to another entry cannot be added. Pass an empty array to unlink all posts.
titlestringoptional
The title of your changelog entry.
typesarrayoptional
Replaces the types of your changelog entry. Each can be one of "fixed", "new", and "improved". Pass an empty array to remove all types.

Returns

Returns "success" if the entry was updated successfully.

Endpoint
POSThttps://canny.io/api/v1/entries/update
Example Request
$ curl https://canny.io/api/v1/entries/update -X POST -d '{
    "apiKey": "YOUR_API_KEY",
    "entryID": "6a2889c586d7b8843bf4cf09",
    "title": "Updated entry title",
    "details": "Updated entry details",
    "types": ["new", "improved"]}' \
    -H "Content-Type: application/json"
Example Response
success

Change entry status

Publishes, schedules, unpublishes, or unschedules a changelog entry. Publishing emails users if the entry has notify enabled. Setting an entry to the status it already has does nothing, except that a scheduled entry is rescheduled to the new scheduledFor date.

Arguments
apiKeystringrequired
Your secret API key.
entryIDstringrequired
The unique identifier of the changelog entry.
statusstringrequired
The new status. Use "published" to publish the entry now. Use "scheduled" to publish it at the scheduledFor date. Use "draft" to unpublish a published entry or cancel a scheduled one.
publishedOnstringoptional
When status is "published", a published date in the past, in ISO 8601 format. Defaults to the current time.
scheduledForstringoptional
Required when status is "scheduled". The date you want the entry to be published, in ISO 8601 format. This has to be in the future.

Returns

Returns "success" if the status was changed successfully.

Endpoint
POSThttps://canny.io/api/v1/entries/change_status
Example Request
$ curl https://canny.io/api/v1/entries/change_status -X POST \
  -d apiKey=YOUR_API_KEY \
  -d entryID=6a2889c586d7b8843bf4cf09 \
  -d status=scheduled \
  -d scheduledFor=2026-12-01T17:00:00.000Z \
  
Example Response
success

Delete entry

Deletes a changelog entry, whether it is a draft, scheduled, or published. Deleting an entry that is already deleted does nothing.

Arguments
apiKeystringrequired
Your secret API key.
entryIDstringrequired
The unique identifier of the changelog entry.

Returns

Returns "success" if the entry was deleted successfully.

Endpoint
POSThttps://canny.io/api/v1/entries/delete
Example Request
$ curl https://canny.io/api/v1/entries/delete -X POST \
  -d apiKey=YOUR_API_KEY \
  -d entryID=6a2889c586d7b8843bf4cf09 \
  
Example Response
success

Portal Comments

Portal comments are left by users and admins on posts. They are always associated with a post. The API allows you to fetch a specific comment, or a list of comments for a post.

The portal comment object

Attributes
idstring
A unique identifier for the comment.
authorUser object
The user who created the comment.
boardBoard object
The board the comment is associated with.
createdstring
Time at which the comment was created, in ISO 8601 format.
imageURLsarray
An array of the URLs of the images associated with this comment.
internalboolean
Whether or not the comment is an internal comment.
likeCountnumber
The number of likes a comment has received.
mentionsarray
An array of user objects who are mentioned in the comment.
parentIDstring | null
The id of the comment that this comment is a reply to. If this comment is not a reply, this field will be null.
postPost object
The post the comment is associated with.
privateboolean
If the comment is private from other users, only applies if the "Allow end-users to see each others' comments" setting is disabled.
reactionsobject | null
The number of reactions a comment has received.
statusstring | null
The post status set when this comment was created as part of a status change. Otherwise null.
valuestring
The text value of this comment.
Example portal comment object:
{
  "id": "6a2889c586d7b8843bf4cf05",
  "author": {
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2026-11-01T04:00:00.000Z",
    "email": "test@test.test",
    "isAdmin": false,
    "name": "Sally Doe",
    "url": "https://your-company.canny.io/admin/users/sally-doe",
    "userID": "1234"
  },
  "board": {
    "created": "2026-11-01T04:00:00.000Z",
    "id": "6a2889c586d7b8843bf4cf01",
    "name": "Feature Requests",
    "postCount": 123,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "created": "2026-11-01T04:00:00.000Z",
  "imageURLs": [
    "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
    "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
  ],
  "internal": false,
  "likeCount": 2,
  "mentions": [],
  "parentID": "6a2889c586d7b8843bf4cf11",
  "post": {
    "category": {
      "id": "6a2889c586d7b8843bf4cf0d",
      "name": "Dashboard",
      "postCount": 42,
      "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
    },
    "commentCount": 2,
    "id": "6a2889c586d7b8843bf4cf13",
    "imageURLs": [],
    "jira": {
      "linkedIssues": [{
        "id": "123",
        "key": "ID-123",
        "url": "https://your-company.atlassian.net/browse/ID-123"
      }]
    },
    "linear": {
      "linkedIssueIDs": ["ID-123", "ID-345"],
    },
    "score": 13,
    "status": "planned",
    "tags": [{
      "id": "6a2889c586d7b8843bf4cf0d",
      "name": "iOS",
      "postCount": 15,
      "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
    }],
    "title": "post-title",
    "url": "https://your-company.canny.io/admin/board/feature-requests/p/post-title"
  },
  "private": false,
  "reactions": {
    "like": 2
  },
  "status": null,
  "value": "Some cool comment"
}

Retrieve portal comment

Retrieves the details of an existing portal comment, specified by its id.

Arguments
apiKeystringrequired
Your secret API key.
idstringrequired
The comment's unique identifier.

Returns

Returns a portal comment object, if a valid id was supplied.

Endpoint
POSThttps://canny.io/api/v1/comments/retrieve
Example Request
$ curl https://canny.io/api/v1/comments/retrieve -X POST \
    -d apiKey=YOUR_API_KEY \
    -d id=6a2889c586d7b8843bf4cf05
Example Response
{
  "id": "6a2889c586d7b8843bf4cf05",
  "author": {
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2026-11-01T04:00:00.000Z",
    "email": "test@test.test",
    "isAdmin": false,
    "name": "Sally Doe",
    "url": "https://your-company.canny.io/admin/users/sally-doe",
    "userID": "1234"
  },
  "board": {
    "created": "2026-11-01T04:00:00.000Z",
    "id": "6a2889c586d7b8843bf4cf01",
    "name": "Feature Requests",
    "postCount": 123,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "created": "2026-11-01T04:00:00.000Z",
  "imageURLs": [
    "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
    "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
  ],
  "internal": false,
  "likeCount": 2,
  "mentions": [],
  "parentID": "6a2889c586d7b8843bf4cf11",
  "post": {
    "category": {
      "id": "6a2889c586d7b8843bf4cf0d",
      "name": "Dashboard",
      "postCount": 42,
      "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
    },
    "commentCount": 2,
    "eta": "February 2026",
    "id": "6a2889c586d7b8843bf4cf13",
    "imageURLs": [],
    "jira": {
      "linkedIssues": [{
        "id": "123",
        "key": "ID-123",
        "url": "https://your-company.atlassian.net/browse/ID-123"
      }]
    },
    "linear": {
      "linkedIssueIDs": ["ID-123", "ID-345"],
    },
    "score": 13,
    "status": "planned",
    "tags": [{
      "id": "6a2889c586d7b8843bf4cf10",
      "name": "iOS",
      "postCount": 15,
      "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
    }],
    "title": "post-title",
    "url": "https://your-company.canny.io/admin/board/feature-requests/p/post-title"
  },
  "private": false,
  "reactions": {
    "like": 2
  },
  "value": "Some cool comment"
}

List portal comments

Returns a list of portal comments. Include parameters to specify post, board, and pagination. Sorted by newest.

Arguments
apiKeystringrequired
Your secret API key.
authorIDstringoptional
The id of the author you'd like to fetch comments for.
boardIDstringoptional
The id of the board you'd like to fetch comments for.
companyIDstringoptional
If specified, will only fetch posts created by users linked to the company with this custom identifier.
cursorstringoptional
Pagination cursor from a previous response. Use this to fetch the next page of results.
limitnumberoptional
The number of comments you'd like to fetch. Must be between 1 and 100. Defaults to 10 if not specified.
postIDstringoptional
The id of the post you'd like to fetch comments for.

Returns

A dictionary with an "items" property that contains an array of portal comment objects. There's also a "hasNextPage" property that specifies whether there are more comments available, and a "cursor" property that can be used to fetch the next page of results.

Endpoint
POSThttps://canny.io/api/v2/comments/list
Example Request
$ curl https://canny.io/api/v2/comments/list -X POST \
    -d apiKey=YOUR_API_KEY \
    -d postID=6a2889c586d7b8843bf4cf0f
Example Response
{
  "cursor": "eyJhZnRlciI6eyJfaWQiOiI1NTNjM2VmOGI4Y2RjZD...",
  "hasNextPage": true,
  "items": [{
    "id": "6a2889c586d7b8843bf4cf05",
    "author": {
      "id": "6a2889c586d7b8843bf4cf07",
      "created": "2026-11-01T04:00:00.000Z",
      "email": "test@test.test",
      "isAdmin": false,
      "name": "Sally Doe",
      "url": "https://your-company.canny.io/admin/users/sally-doe",
      "userID": "1234"
    },
    "board": {
      "created": "2026-11-01T04:00:00.000Z",
      "id": "6a2889c586d7b8843bf4cf01",
      "name": "Feature Requests",
      "postCount": 123,
      "url": "https://your-company.canny.io/admin/board/feature-requests"
    },
    "created": "2026-11-01T04:00:00.000Z",
    "imageURLs": [
      "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
      "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
    ],
    "internal": false,
    "likeCount": 2,
    "mentions": [],
    "parentID": "6a2889c586d7b8843bf4cf11",
    "post": {
      "category": {
        "id": "6a2889c586d7b8843bf4cf0d",
        "name": "Dashboard",
        "postCount": 42,
        "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
      },
      "commentCount": 2,
      "id": "6a2889c586d7b8843bf4cf13",
      "imageURLs": [],
      "jira": {
        "linkedIssues": [{
          "id": "123",
          "key": "ID-123",
          "url": "https://your-company.atlassian.net/browse/ID-123"
        }]
      },
      "linear": {
        "linkedIssueIDs": ["ID-123", "ID-345"],
      },
      "score": 13,
      "status": "planned",
      "tags": [{
        "id": "6a2889c586d7b8843bf4cf10",
        "name": "iOS",
        "postCount": 15,
        "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
      }],
      "title": "post-title",
      "url": "https://your-company.canny.io/admin/board/feature-requests/p/post-title"
    },
    "private": false,
    "reactions": {
      "like": 2
    },
    "status": null,
    "value": "Some cool comment"
  }]
}

Create portal comment

Creates a new portal comment.

You may also want to use the Create or update user endpoint to make sure the comment author's account exists (and fetch their id).

Arguments
apiKeystringrequired
Your secret API key.
authorIDstringrequired
The unique identifier of the comment's author.
createdAtstringoptional
If the comment is being moved from another source, the date this comment was originally created in ISO 8601 format.
imageURLsarrayoptional
An array of the URLs of comment's images.
internalbooleanoptional
Whether this comment is only available for internal usage. Default is false. This can only be set to true if the author is a member of the company.
parentIDstringoptional
The unique identifier of the comment's parent, if this comment is a reply.
postIDstringrequired
The unique identifier of the comment's post.
shouldNotifyVotersbooleanoptional
Whether this comment should be allowed to trigger email notifications. Default is false.
valuestringrequired
The comment value. Optional if imageURLs are provided. Must be under 2500 characters.

Returns

Upon success, returns an object with a single key: id.

Endpoint
POSThttps://canny.io/api/v1/comments/create
Example Request
$ curl https://canny.io/api/v1/comments/create -X POST \
    -d apiKey=YOUR_API_KEY \
    -d authorID=6a2889c586d7b8843bf4cf05 \
    -d postID=6a2889c586d7b8843bf4cf09 \
    -d value="This is the comment's value"
Example Response
{
  "id": "6a2889c586d7b8843bf4cf0c",
}

Delete portal comment

Deletes a portal comment.

Arguments
apiKeystringrequired
Your secret API key.
commentIDstringrequired
The unique identifier of the comment.

Returns

Returns "success" if the comment was successfully deleted.

Endpoint
POSThttps://canny.io/api/v1/comments/delete
Example Request
$ curl https://canny.io/api/v1/comments/delete -X POST \
    -d apiKey=YOUR_API_KEY \
    -d commentID=6a2889c586d7b8843bf4cf0c"
Example Response
success

Companies

Companies are created by associating them to users using Canny's SDK, SSO tokens, or the API. The API allows you to delete a specific company.

The company object

Attributes
idstring
A unique identifier for the company.
createdstring
Time at which the company was created, in ISO 8601 format.
customFieldsobject
Any custom fields associated with the company.
domainstring | null
The company's domain.
memberCountnumber
The number of users associated with the company.
monthlySpendnumber | null
The monthly spend associated with the company.
namestring
The name of the company.
Example company object:
{
  "id": "1234567890",
  "created": "2026-11-01T04:00:00.000Z",
  "customFields": {
    "plan": "Premium"
  },
  "domain": "acme.com",
  "memberCount": 12,
  "monthlySpend": 1000,
  "name": "Acme, Inc."
}

List companies

Returns a list of all companies associated with your company, ordered by created date.

Arguments
apiKeystringrequired
Your secret API key.
searchstringoptional
A string to search by company name
segmentstringoptional
the URL name of the segment you want to use to filter companies
limitnumberoptional
The number of companies you'd like to fetch, between 1-100. Defaults to 10 if not specified.
cursorstringoptional
The cursor from a previous request.

Returns

A dictionary with a "companies" property that contains an array of company objects. You'll also receive a "hasNextPage" boolean indicating whether additional companies are available and a "cursor" string to request the next page. If "search" is provided, "hasNextPage" will always be false and "cursor" will be null.

Endpoint
POSThttps://canny.io/api/v2/companies/list
Example Request
$ curl https://canny.io/api/v2/companies/list -X POST \
    -d apiKey=YOUR_API_KEY \
    -d limit=100 \
    -d cursor=NTUzYzNlZjhiOGNkY2QxNTAxYmExMjNhXzIwMjUtMDItMDRUMTY6NDA6MDcuNTQxWl8x
Example Response
{
    "hasNextPage": true,
    "cursor": "NTUzYzNlZjhiOGNkY2QxNTAxYmExMjNhXzIwMjUtMDItMDRUMTY6NDA6MDcuNTQxWl8x",
    "companies": [
      {
        "id": "company1",
        "created": "2022-06-17T12:44:38.797Z",
        "customFields": {
          "number": 1,
          "bool": true,
          "string": "test"
        },
        "domain": "example.com",
        "memberCount": 5,
        "monthlySpend": 100.23,
        "name": "company 1"
      }
    ]
  }

Create company

If you want to create a company, this can be accomplished by associating it to a user via the Create or Update User endpoint.

Update company

Given a company ID, this endpoint updates the company with the data supplied

If you have company syncing integrations in place (e.g., Hubspot, Salesforce, SSO Tokens, Canny Identify, ...), the fields updated with this endpoint might be overwritten.

Arguments
apiKeystringrequired
Your secret API key.
idstringrequired
The identifier for your company.
createddateoptional
The date the company was created in your system.
customFieldsobjectoptional
Any custom fields associated with the company. Field names must be under 30 characters, and text values must be under 200 characters.
domainstringoptional
The company's domain, e.g. "acme.com". Must be a bare domain, without a protocol or path. Used to match companies across data sources.
monthlySpendintegeroptional
The company's MRR in dollars. Can't be negative, and is rounded to two decimal places.
namestringoptional
The company's name, up to 100 characters. Can't be blank, contain a URL or slashes, or be a reserved word like "Feedback" or "Roadmap".

Returns

Upon success, returns an object with a single key: id.

Endpoint
POSThttps://canny.io/api/v1/companies/update
Example Request
$ curl https://canny.io/api/v1/companies/update -X POST -d '{
    "apiKey": "YOUR_API_KEY",
    "id": "company1",
    "created": "2026-11-01T04:00:00.000Z",
    "customFields": {
      "number": "12",
      "string": "value"
      "boolean": true,
    },
    "domain": "acme.com",
    "monthlySpend": 500.59,
    "name": "company 1"}' \
    -H "Content-Type: application/json"
Example Response
{
  "id": "company1"
}

Delete company

Deletes a company.

Arguments
apiKeystringrequired
Your secret API key.
companyIDstringrequired
The identifier you used when creating the company.

Returns

Returns "success" if the company was successfully deleted.

Endpoint
POSThttps://canny.io/api/v1/companies/delete
Example Request
$ curl https://canny.io/api/v1/companies/delete -X POST \
    -d apiKey=YOUR_API_KEY \
    -d companyID=company1"
Example Response
success

Groups

Groups let you organize ideas within a company. They can be nested to build a hierarchy and are often used to scope ownership or product areas.

The Groups API allows you to list your groups.

The group object

Attributes
idstring
A unique identifier for the group.
namestring
The name of the group.
descriptionstring
A short description of what this group represents.
parentIDstring | null
The ID of the parent group, if any.
urlNamestring
A URL-friendly identifier for the group.
Example group object:
{
  "id": "6a2889c586d7b8843bf4cf0b",
  "name": "Product",
  "description": "All ideas related to the product surface area.",
  "parentID": "6a2889c586d7b8843bf4cf0a",
  "urlName": "product"
}

List groups

Returns a paginated list of groups for your company.

Arguments
apiKeystringrequired
Your secret API key.
cursorstringoptional
A pagination cursor returned from a previous request. Use it to fetch the next page of results.
limitnumberoptional
The number of groups to fetch. Defaults to 50 if not specified. Maximum is 100.

Returns

A dictionary with an "items" array of group objects, a "hasNextPage" boolean indicating if more results are available, and a "cursor" string to fetch the next page when hasNextPage is true.

Endpoint
POSThttps://canny.io/api/v1/groups/list
Example Request
$ curl https://canny.io/api/v1/groups/list -X POST \
    -d apiKey=YOUR_API_KEY \
    -d limit=10
Example Response
{
  "cursor": "eyJhZnRlciI6eyJfaWQiOiI1NTNjM2VmOGI4Y2RjZD...",
  "hasNextPage": true,
  "items": [{
    "id": "6a2889c586d7b8843bf4cf02",
    "name": "Product",
    "description": "Ideas related to the product surface area.",
    "urlName": "product"
  }]
}

Retrieve group

Retrieves the details of an existing group, specified by its id or urlName.

Arguments
apiKeystringrequired
Your secret API key.
idstringoptional
The group's unique identifier.
urlNamestringoptional
The group's unique urlName.

Returns

Returns a group object, if a valid id or urlName was supplied.

Endpoint
POSThttps://canny.io/api/v1/groups/retrieve
Example Request
$ curl https://canny.io/api/v1/groups/retrieve -X POST \
    -d apiKey=YOUR_API_KEY \
    -d id=6a2889c586d7b8843bf4cf02
Example Response
{
  "id": "6a2889c586d7b8843bf4cf02",
  "name": "Product",
  "description": "Ideas related to the product surface area.",
  "urlName": "product"
}

Ideas

An idea is an object that represents a feature request or feedback item. Ideas can be organized into groups, have parent-child relationships, and include custom fields. The API allows you to fetch a list of ideas with filtering, sorting, and pagination options.

The idea object

Attributes
idstring
A unique identifier for the idea.
childCountnumber
The number of child ideas (ideas that have this idea as their parent).
authorUser object | null
The user who authored the idea, if any.
createdstring
Time at which the idea was created, in ISO 8601 format.
descriptionstring
The description of the idea.
groupGroup object | null
The group this idea belongs to, if any.
ownerUser object | null
The user who is assigned as owner of this idea, if any.
parentPartial idea object | null
The parent idea, if this idea is a child of another idea.
sourceSource object
The source where the idea was created from.
statusStatus object | null
The status of the idea.
titlestring
The title of the idea.
updatedAtstring | null
Time at which the idea was last updated, in ISO 8601 format.
urlNamestring
The URL name of the idea.
Example idea object:
{
  "id": "6a2889c586d7b8843bf4cf05",
  "author": {
    "id": "6a2889c586d7b8843bf4cf03",
    "created": "2026-11-01T04:00:00.000Z",
    "email": "john@example.com",
    "isAdmin": false,
    "name": "John Doe",
    "url": "https://your-company.canny.io/admin/users/john-doe",
    "userID": "1234"
  },
  "childCount": 1,
  "created": "2026-11-01T04:00:00.000Z",
  "description": "Test idea description",
  "group": {
    "id": "6a2889c586d7b8843bf4cf02",
    "name": "Feature Requests",
    "description": "Feature requests are ideas for new features or improvements to the product.",
    "parentID": "6a2889c586d7b8843bf4cf01",
    "urlName": "feature-requests"
  },
  "owner": {
    "id": "6a2889c586d7b8843bf4cf03",
    "created": "2026-11-01T04:00:00.000Z",
    "email": "john@example.com",
    "isAdmin": false,
    "name": "John Doe",
    "url": "https://your-company.canny.io/admin/users/john-doe",
    "userID": "1234"
  },
  "parent": {
    "id": "6a2889c586d7b8843bf4cf04",
    "title": "Parent Idea",
    "urlName": "parent-idea"
  },
  "source": {
    "name": "canny",
    "type": "web"
  },
  "status": {
    "id": "6a2889c586d7b8843bf4cf0d",
    "name": "Under Review",
    "type": "Active",
    "urlName": "under-review"
  },
  "title": "An awesome feature request",
  "updatedAt": "2026-11-01T04:00:00.000Z",
  "urlName": "an-awesome-feature-request"
}

The filtering object

Attributes
filtersarray

An array of filters to apply to the ideas. Should contain, resource, condition and value.

Resources: ideaDefaultField.

Conditions: Should be one of the ones listed below.

Value:

  • fieldID: The ID of the field to filter by. Should be one of the ones listed below.
  • value: The value to filter by. Should be one of the ones listed below.

Default Field IDs: "author", "board", "category", "created", "description", "group", "lastAdminPortalResponse", "owner", "portalAuthor", "portalETA", "portalStatus", "status", "statusChanged", "tags", "themes", "title", "type"

Fields Conditions:
  • Text Field: "isEmpty", "isNotEmpty", "contains", "notContains"
  • Numeric Field: "equals", "doesNotEqual", "greaterThan", "lessThan"
  • Alphanumeric Field: "isEmpty", "isNotEmpty", "contains", "notContains"
  • Dropdown Field: "is", "isNot", "isDropdownSet", "isDropdownNotSet"
  • Multiselect Field: "isOneOf", "isNotOneOf", "isMultiselectSet", "isMultiselectNotSet"
  • Boolean Field: "isTrue", "isFalse"
  • Date Field: "isDateSet", "isDateNotSet", "isBefore", "isAfter", "isBetween"
filtersOperatorstring
The operator to apply to the filters. Can be "all" (AND) or "any" (OR).
Example filtering object:
{
  "filters": [
    {
      "resource": "ideaDefaultField",
      "condition": "is",
      "value": {
        "fieldID": "title",
        "value": "My Awesome Idea"
      }
    }
  ],
  "filtersOperator": "all"
}

List ideas

Returns a list of ideas. Include parameters to specify pagination, filtering, and sorting.

Arguments
apiKeystringrequired
Your secret API key.
cursorstringoptional
A pagination cursor returned from a previous request. Used to fetch the next page of results.
filteringFiltering objectoptional
A filtering object containing filters and filtersOperator. Filters allow you to filter ideas by various fields such as author, group, owner, status, etc. The filtersOperator can be "all" (AND) or "any" (OR).
limitnumberoptional
The number of ideas you'd like to fetch. Defaults to 50 if not specified. Maximum is 100.
parentIDstringoptional
If specified, will only fetch ideas that are children of the idea with this id.
searchstringoptional
If specified, will only fetch ideas that match your search query. Note: search does not support pagination or sorting.
sortobjectoptional
An object specifying how to sort the ideas. Must have "field" (string) and "direction" ("asc" or "desc") properties. Defaults to { field: "_id", direction: "desc" } if not specified.

Returns

A dictionary with an "items" property that contains an array of idea objects. There's also a "hasNextPage" property that specifies whether this query returns more ideas than the limit, and a "cursor" property that can be used to fetch the next page of results when hasNextPage is true.

Endpoint
POSThttps://canny.io/api/v1/ideas/list
Example Request
$ curl https://canny.io/api/v1/ideas/list -X POST \
    -d apiKey=YOUR_API_KEY \
    -d limit=10
Example Response
{
  "cursor": "eyJhZnRlciI6eyJfaWQiOiI1NTNjM2VmOGI4Y2RjZD...",
  "hasNextPage": true,
  "items": [{
    "id": "6a2889c586d7b8843bf4cf05",
    "author": {
      "id": "6a2889c586d7b8843bf4cf03",
      "created": "2026-11-01T04:00:00.000Z",
      "email": "jane@example.com",
      "isAdmin": false,
      "name": "Jane Doe",
      "url": "https://your-company.canny.io/admin/users/jane-doe",
      "userID": "5678"
    },
    "childCount": 1,
    "created": "2026-11-01T04:00:00.000Z",
    "description": "Test idea description",
    "group": {
      "id": "6a2889c586d7b8843bf4cf02",
      "name": "Feature Requests",
      "description": "Feature requests are ideas for new features or improvements to the product.",
      "urlName": "feature-requests"
    },
    "owner": {
      "id": "6a2889c586d7b8843bf4cf03",
      "created": "2026-11-01T04:00:00.000Z",
      "email": "john@example.com",
      "isAdmin": false,
      "name": "John Doe",
      "url": "https://your-company.canny.io/admin/users/john-doe",
      "userID": "1234"
    },
    "parent": {
      "id": "6a2889c586d7b8843bf4cf04",
      "title": "Parent Idea",
      "urlName": "parent-idea"
    },
    "source": {
      "name": "canny",
      "type": "web"
    },
    "status": {
      "id": "6a2889c586d7b8843bf4cf0d",
      "name": "Under Review",
      "type": "Active",
      "urlName": "under-review"
    },
    "title": "An awesome feature request",
    "updatedAt": "2026-11-01T04:00:00.000Z",
    "urlName": "an-awesome-feature-request"
  }]
}

Merge idea

Merges one idea into another idea.

Arguments
apiKeystringrequired
Your secret API key.
mergeIdeaIDstringrequired
The idea's unique identifier that will be merged.
intoIdeaIDstringrequired
The idea's unique identifier that the mergeIdeaID will be merged into.
mergerIDstringrequired
The unique identifier of the user that will be the merger.

Returns

Returns success if the idea was merged.

Endpoint
POSThttps://canny.io/api/v1/ideas/merge
Example Request
$ curl https://canny.io/api/v1/ideas/merge -X POST \
    -d apiKey=YOUR_API_KEY \
    -d mergeIdeaID=6a2889c586d7b8843bf4cf05 \
    -d intoIdeaID=6a2889c586d7b8843bf4cf06 \
    -d mergerID=6a2889c586d7b8843bf4cf07
Example Responses
success
{"error":"invalid mergeIdeaID"}

Retrieve idea

Retrieves the details of an existing idea, specified by its id or urlName.

Arguments
apiKeystringrequired
Your secret API key.
idstringoptional
The idea's unique identifier.
urlNamestringoptional
The idea's unique urlName.

Returns

Returns an idea object, if a valid id or urlName was supplied.

Endpoint
POSThttps://canny.io/api/v1/ideas/retrieve
Example Request
$ curl https://canny.io/api/v1/ideas/retrieve -X POST \
    -d apiKey=YOUR_API_KEY \
    -d id=6a2889c586d7b8843bf4cf05
Example Response
{
  "id": "6a2889c586d7b8843bf4cf05",
  "childCount": 0,
  "created": "2026-11-01T04:00:00.000Z",
  "description": "Test idea description",
  "author": {
    "id": "6a2889c586d7b8843bf4cf03",
    "created": "2026-11-01T04:00:00.000Z",
    "email": "john@example.com",
    "isAdmin": false,
    "name": "John Doe",
    "url": "https://your-company.canny.io/admin/users/john-doe",
    "userID": "1234"
  },
  "owner": {
    "id": "6a2889c586d7b8843bf4cf03",
    "created": "2026-11-01T04:00:00.000Z",
    "email": "john@example.com",
    "isAdmin": false,
    "name": "John Doe",
    "url": "https://your-company.canny.io/admin/users/john-doe",
    "userID": "1234"
  },
  "group": {
    "id": "6a2889c586d7b8843bf4cf02",
    "name": "Feature Requests",
    "description": "Feature requests are ideas for new features or improvements to the product.",
    "urlName": "feature-requests"
  },
  "parent": {
    "id": "6a2889c586d7b8843bf4cf04",
    "title": "Parent Idea",
    "urlName": "parent-idea"
  },
  "source": {
    "name": "canny",
    "type": "web"
  },
  "status": {
    "id": "6a2889c586d7b8843bf4cf0d",
    "name": "Under Review",
    "type": "Active",
    "urlName": "under-review"
  },
  "title": "An awesome feature request",
  "updatedAt": "2026-11-01T04:00:00.000Z",
  "urlName": "an-awesome-feature-request"
}

Delete idea

Deletes an existing idea.

Arguments
apiKeystringrequired
Your secret API key.
idstringrequired
The idea's unique identifier.

Returns

Returns success if the idea was deleted successfully.

Endpoint
POSThttps://canny.io/api/v1/ideas/delete
Example Request
$ curl https://canny.io/api/v1/ideas/delete -X POST -d '{
    "apiKey": "YOUR_API_KEY",
    "id": "6a2889c586d7b8843bf4cf05"' \
    -H "Content-Type: application/json"
Example Responses
success
{"error":"invalid idea id"}

Insights

Insights are pieces of feedback, context, or information associated with ideas. They can be created manually or automatically from comments, votes, or other sources.

The Insights API allows you to list insights for your company.

The insight object

Attributes
idstring
A unique identifier for the insight.
authorUser object | null
The user who created the insight, if available.
companyCompany object | null
The company associated with the insight, if available.
createdstring
Time at which the insight was created, in ISO 8601 format.
ideaIDstring
The ID of the idea associated with the insight.
prioritystring | null
The priority level of the insight, if set. Possible values are "nice-to-have", "important", "must-have", "no-priority"
sourceSource object
The source of the insight (e.g., "canny", "webhook").
urlstring | null
A URL associated with the insight, if available.
usersUser object[]
The users mentioned or associated with the insight.
valuestring
The text content of the insight.
Example insight object:
{
  "id": "6a2889c586d7b8843bf4cf02",
  "author": {
    "id": "6a2889c586d7b8843bf4cf03",
    "created": "2026-11-01T04:00:00.000Z",
    "email": "john@example.com",
    "isAdmin": false,
    "name": "John Doe",
    "url": "https://your-company.canny.io/admin/users/john-doe",
    "userID": "1234"
  },
  "company": {
    "monthlySpend": 1000,
    "name": "Company Name",
    "id": "1234567890",
    "urlName": "company-name"
  },
  "created": "2026-11-01T04:00:00.000Z",
  "ideaID": "6a2889c586d7b8843bf4cf04",
  "priority": "high",
  "source": {
    "name": "ui",
    "type": "manual"
  },
  "url": null,
  "users": [{
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2026-11-01T04:00:00.000Z",
    "email": "john@example.com",
    "isAdmin": false,
    "name": "John Doe",
    "url": "https://your-company.canny.io/admin/users/john-doe",
    "userID": "1234"
  }],
  "value": "This is an important insight about the feature."
}

List insights

Returns a paginated list of insights for your company.

Arguments
apiKeystringrequired
Your secret API key.
cursorstringoptional
A pagination cursor returned from a previous request. Use it to fetch the next page of results.
ideaIDstringoptional
If specified, will only fetch insights associated with the idea with this id.
limitnumberoptional
The number of insights to fetch. Defaults to 50 if not specified. Maximum is 100.

Returns

A dictionary with an "items" array of insight objects, a "hasNextPage" boolean indicating if more results are available, and a "cursor" string to fetch the next page when hasNextPage is true.

Endpoint
POSThttps://canny.io/api/v1/insights/list
Example Request
$ curl https://canny.io/api/v1/insights/list -X POST \
    -d apiKey=YOUR_API_KEY \
    -d limit=10
Example Response
{
  "cursor": "eyJhZnRlciI6eyJfaWQiOiI1NTNjM2VmOGI4Y2RjZD...",
  "hasNextPage": true,
  "items": [{
    "id": "6a2889c586d7b8843bf4cf02",
    "author": {
      "id": "6a2889c586d7b8843bf4cf03",
      "created": "2026-11-01T04:00:00.000Z",
      "email": "john@example.com",
      "isAdmin": false,
      "name": "John Doe",
      "url": "https://your-company.canny.io/admin/users/john-doe",
      "userID": "1234"
    },
    "company": {
      "monthlySpend": 1000,
      "name": "Company Name",
      "id": "1234567890",
      "urlName": "company-name"
    },
    "created": "2026-11-01T04:00:00.000Z",
    "ideaID": "6a2889c586d7b8843bf4cf04",
    "priority": "high",
    "source": {
      "name": "ui",
      "type": "manual"
    },
    "url": null,
    "users": [{
      "id": "6a2889c586d7b8843bf4cf07",
      "created": "2026-11-01T04:00:00.000Z",
      "email": "john@example.com",
      "isAdmin": false,
      "name": "John Doe",
      "url": "https://your-company.canny.io/admin/users/john-doe",
      "userID": "1234"
    }],
    "value": "This is an important insight about the feature."
  }]
}

Retrieve insight

Retrieves the details of an existing insight. You must specify the insight's unique identifier from Canny.

Arguments
apiKeystringrequired
Your secret API key.
idstringrequired
The insight's unique identifier from Canny.

Returns

Returns an insight object, if a valid identifier was supplied.

Endpoint
POSThttps://canny.io/api/v1/insights/retrieve
Example Request
$ curl https://canny.io/api/v1/insights/retrieve -X POST \
    -d apiKey=YOUR_API_KEY \
    -d id=6a2889c586d7b8843bf4cf02
Example Response
{
  "id": "6a2889c586d7b8843bf4cf02",
  "author": {
    "id": "6a2889c586d7b8843bf4cf03",
    "created": "2026-11-01T04:00:00.000Z",
    "email": "john@example.com",
    "isAdmin": false,
    "name": "John Doe",
    "url": "https://your-company.canny.io/admin/users/john-doe",
    "userID": "1234"
  },
  "company": {
    "monthlySpend": 1000,
    "name": "Company Name",
    "id": "1234567890",
    "urlName": "company-name"
  },
  "created": "2026-11-01T04:00:00.000Z",
  "ideaID": "6a2889c586d7b8843bf4cf04",
  "priority": "undefined",
  "source": {
    "name": "ui",
    "type": "manual"
  },
  "url": "https://example.com/insight",
  "users": [{
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2026-11-01T04:00:00.000Z",
    "email": "john@example.com",
    "isAdmin": false,
    "name": "John Doe",
    "url": "https://your-company.canny.io/admin/users/john-doe",
    "userID": "1234"
  }],
  "value": "This is an important insight about the feature."
}

Opportunities

An opportunity represents a potential customer, synced in from Salesforce or Hubspot. The API allows you to fetch the list of opportunities linked to posts in Canny.

The opportunity object

Attributes
idstring
A unique identifier for the opportunity.
closedboolean
Whether the opportunity is closed.
ideaIDsarray
The list of idea ids this opportunity is linked to.
namestring
The name of the opportunity.
postIDsarray
The list of post ids this opportunity is linked to.
salesforceOpportunityID
string
The unique identifier for the opportunity in Salesforce.
stagestring | null
The current pipeline stage of the opportunity, as labeled in the source CRM (e.g. "Discovery", "Closed Won"). May be null if the stage could not be resolved.
valuenumber
The value of the opportunity.
wonboolean
Whether the opportunity has been won.
Example opportunity object:
{
  "id": "61a942ed07bfe27b60109ee0",
  "closed": true,
  "ideaIDs": ["61a942ed07bfe27b60109eaf", "61a942ed07bfe27b60109ebc"],
  "name": "Opportunity Name",
  "postIDs": ["61a942ed07bfe27b60109ea3", "61a942ed07bfe27b60109eb4"],
  "salesforceOpportunityID": "0063k00000zx6BkAAI",
  "stage": "Closed Won",
  "value": 1999.99,
  "won": true
}

List opportunties

Returns a list of opportunities linked to posts in Canny.

Arguments
apiKeystringrequired
Your secret API key.
limitnumberoptional
The number of opportunities you'd like to fetch. Defaults to 10 if not specified.
skipnumberoptional
The number of opportunities you'd like to skip before starting to fetch. Defaults to 0 if not specified.

Returns

A dictionary with an "opportunities" property that contains an array of opportunity objects. There's also a "hasMore" property that specifies whether this query returns more opportunities than the limit.

Endpoint
POSThttps://canny.io/api/v1/opportunities/list
Example Request
$ curl https://canny.io/api/v1/opportunities/list -X POST \
    -d apiKey=YOUR_API_KEY
Example Response
{
  "hasMore": false,
  "opportunities": [{
    "id": "61a942ed07bfe27b60109ee0",
    "closed": true,
    "ideaIDs": ["61a942ed07bfe27b60109ea3", "61a942ed07bfe27b60109eb4"],
    "name": "Opportunity Name",
    "postIDs": ["61a942ed07bfe27b60109eaf", "61a942ed07bfe27b60109ebc"],
    "salesforceOpportunityID": "0063k00000zx6BkAAI",
    "stage": "Closed Won",
    "value": 1999.99,
    "won": true
  }]
}

Posts

A post is an object that represents an idea posted to a board. They are always associated with a user and a board. Users can vote on them. The API allows you to fetch a specific post, or a list of posts for a board.

The post object

Attributes
idstring
A unique identifier for the post.
authorUser object | null
The user who authored the post. If the author's account has been deleted, this field will be null.
boardBoard object
The board this post is associated with.
byUser object | null
The user who created the post on behalf of the author.
categoryCategory object | null
The category this post is assigned to, if any.
commentCountnumber
The number of non-deleted comments associated with this post.
createdstring
Time at which the post was created, in ISO 8601 format.
clickup.linkedTasksarray
A list of Clickup tasks that are linked with this post
customFieldsarray
An array of custom fields associated with the post.
detailsstring
Any details the user included in the post. This is the longer text field (where the shorter one is "title").
etastring | null
The month and year the post is estimated to be delivered.
imageURLsarray
An array of the URLs of the images associated with this post
ideaIdea object | null
The idea associated with this post, if any.
jira.linkedIssuesarray
A list of Jira issues that are linked with this post
linear.linkedIssueIDsarray
A list of Linear issue IDs that are linked with this post
ownerUser object | null
The owner of the post
roadmapsarray
The roadmaps the post is associated with
scorenumber
The number of votes that have been cast on this post.
statusstring
The post's status: "open", "under review", "planned", "in progress", "complete", "closed", or any other status your team has set on the settings page.
statusChangedAtstring
Time at which the post's status was last changed
tagsarray
The list of tag objects associated with this post.
titlestring
A brief title describing the post. This is the shorter text input (where the longer is details).
urlstring
The URL to the post's page.
Example post object:
{
  "id": "6a2889c586d7b8843bf4cf05",
  "author": {
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2017-07-20T22:11:00.000Z",
    "email": "test@test.test",
    "isAdmin": true,
    "name": "Sally Doe",
    "url": "https://your-company.canny.io/admin/users/sally-doe",
    "userID": "1234"
  },
  "board": {
    "id": "6a2889c586d7b8843bf4cf01",
    "created": "2017-07-15T22:11:00.000Z",
    "name": "Feature Requests",
    "postCount": 123,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "by": {
    "id": "6a2889c586d7b8843bf4cf19",
    "created": "2017-07-15T22:11:00.000Z",
    "email": "test@john.test",
    "isAdmin": true,
    "name": "John Doe",
    "url": "https://your-company.canny.io/admin/users/john-doe",
    "userID": "5678"
  },
  "category": {
    "id": "6a2889c586d7b8843bf4cf0d",
    "name": "Dashboard",
    "parentID": null,
    "postCount": 42,
    "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
  },
  "commentCount": 10,
  "created": "2017-08-03T22:11:00.000Z",
  "clickup": {
    "linkedTasks": [{
      "id": "123",
      "linkID": "2334jdsai23234io22",
      "name": "Clickup issue",
      "postID": "6a2889c586d7b8843bf4cf05",
      "status": "to do",
      "url": "https://app.clickup.com/t/123456"
    }]
  },
  "customFields": [
    {
      "id": "6a2889c586d7b8843bf4cf20",
      "name": "Estimated effort",
      "value": "3 weeks"
    }
  ],
  "details": "Test post details",
  "eta": "February 2020",
  "imageURLs": [
    "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
    "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
  ],
  "idea": null,
  "jira": {
    "linkedIssues": [{
      "id": "123",
      "key": "ID-123",
      "url": "https://your-company.atlassian.net/browse/ID-123"
    }]
  },
  "linear": {
    "linkedIssueIDs": ["ID-123", "ID-345"],
  },
  "owner": {
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2017-07-20T22:11:00.000Z",
    "email": "test@test.test",
    "isAdmin": true,
    "name": "Sally Doe",
    "url": "https://your-company.canny.io/admin/users/sally-doe",
    "userID": "1234"
  },
  "score": 72,
  "status": "in progress",
  "tags": [{
    "id": "6a2889c586d7b8843bf4cf0d",
    "name": "iOS",
    "postCount": 15,
    "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
  }],
  "title": "An awesome feature request",
  "url": "https://your-company.canny.io/admin/board/feature-requests/p/an-awesome-feature-request"
}

Retrieve post

Retrieves the details of an existing post, specified by its id or urlName.

Arguments
apiKeystringrequired
Your secret API key.
boardIDstringoptional
The board that the post belongs to. Only required if fetching by urlName.
idstringoptional
The post's unique identifier.
urlNamestringoptional
The post's unique urlName.

Returns

Returns a post object, if a valid id was supplied.

Endpoint
POSThttps://canny.io/api/v1/posts/retrieve
Example Request
$ curl https://canny.io/api/v1/posts/retrieve -X POST \
    -d apiKey=YOUR_API_KEY \
    -d id=6a2889c586d7b8843bf4cf05
Example Response
{
  "id": "6a2889c586d7b8843bf4cf05",
  "author": {
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2026-11-01T04:00:00.000Z",
    "email": "test@test.test",
    "isAdmin": true,
    "name": "Sally Doe",
    "url": "https://your-company.canny.io/admin/users/sally-doe",
    "userID": "1234"
  },
  "board": {
    "created": "2026-11-01T04:00:00.000Z",
    "id": "6a2889c586d7b8843bf4cf01",
    "name": "Feature Requests",
    "postCount": 123,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "by": {
    "id": "6a2889c586d7b8843bf4cf19",
    "created": "2026-11-01T04:00:00.000Z",
    "email": "test@john.test",
    "isAdmin": true,
    "name": "John Doe",
    "url": "https://your-company.canny.io/admin/users/john-doe",
    "userID": "5678"
  },
  "category": {
    "id": "6a2889c586d7b8843bf4cf0d",
    "name": "Dashboard",
    "parentID": null,
    "postCount": 42,
    "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
  },
  "changeComment": {
    "value": "The status has changed!",
    "imageURLs": ["https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b2.png"]
  },
  "clickup": {
    "linkedTasks": [{
      "id": "123",
      "linkID": "2334jdsai23234io22",
      "name": "Clickup issue",
      "postID": "6a2889c586d7b8843bf4cf05",
      "status": "to do",
      "url": "https://app.clickup.com/t/123456"
    }]
  },
  "commentCount": 10,
  "created": "2026-11-01T04:00:00.000Z",
  "customFields": [{
    "id": "6a2889c586d7b8843bf4cf0e",
    "name": "priority",
    "value": "high",
  }],
  "details": "Test post details",
  "eta": "February 2020",
  "imageURLs": [
    "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
    "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
  ],
  "jira": {
    "linkedIssues": [{
      "id": "123",
      "key": "ID-123",
      "url": "https://your-company.atlassian.net/browse/ID-123"
    }]
  },
  "linear": {
    "linkedIssueIDs": ["ID-123", "ID-345"],
  },
  "mergeHistory":[{
    "created":"2026-11-01T04:00:00.000Z",
    "post": {
      "created":"2026-11-01T04:00:00.000Z",
      "details":"Awesome feature post details",
      "id": "6a2889c586d7b8843bf4cf14",
      "imageURLs":[],
      "title":"Another awesome feature request"
    }
  }],
  "owner": {
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2026-11-01T04:00:00.000Z",
    "email": "test@test.test",
    "isAdmin": true,
    "name": "Sally Doe",
    "url": "https://your-company.canny.io/admin/users/sally-doe",
    "userID": "1234"
  },
  "score": 72,
  "status": "in progress",
  "statusChangedAt": "2026-11-01T04:00:00.000Z",
  "tags": [{
    "id": "6a2889c586d7b8843bf4cf10",
    "name": "iOS",
    "postCount": 15,
    "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
  }],
  "title": "An awesome feature request",
  "url": "https://your-company.canny.io/admin/board/feature-requests/p/an-awesome-feature-request"
}

List posts

Returns a list of posts. Include parameters to specify board, pagination, filtering, and sorting.

Arguments
apiKeystringrequired
Your secret API key.
boardIDstringoptional
The id of the board you'd like to fetch posts for.
authorIDstringoptional
If specified, will only fetch posts by the author with this id.
companyIDstringoptional
If specified, will only fetch posts created by users linked to the company with this custom identifier.
tagIDsarrayoptional
If specified, will only fetch posts tagged with at least one of the tags in the array.
limitnumberoptional
The number of posts you'd like to fetch. Defaults to 10 if not specified.
searchstringoptional
If specified, will only fetch posts that match your search query.
skipnumberoptional
The number of posts you'd like to skip before starting to fetch. Defaults to 0 if not specified.
sortstringoptional
The order in which the posts should be fetched. Options include: "newest", "oldest", "relevance", "score", "statusChanged", "trending". Defaults to "newest" if not specified. The "relevance" sort can only be specified if a search value has been specified.
statusstringoptional
A comma separated list of statuses. Only posts with these statuses will be fetched.

Returns

A dictionary with a "posts" property that contains an array of post objects. There's also a "hasMore" property that specifies whether this query returns more posts than the limit.

Endpoint
POSThttps://canny.io/api/v1/posts/list
Example Request
$ curl https://canny.io/api/v1/posts/list -X POST \
    -d apiKey=YOUR_API_KEY \
    -d boardID=6a2889c586d7b8843bf4cf01
Example Response
{
  "hasMore": true,
  "posts": [{
    "id": "6a2889c586d7b8843bf4cf05",
    "author": {
      "id": "6a2889c586d7b8843bf4cf07",
      "created": "2026-11-01T04:00:00.000Z",
      "email": "test@test.test",
      "isAdmin": false,
      "name": "Sally Doe",
      "url": "https://your-company.canny.io/admin/users/sally-doe",
      "userID": "1234"
    },
    "board": {
      "created": "2026-11-01T04:00:00.000Z",
      "id": "6a2889c586d7b8843bf4cf01",
      "name": "Feature Requests",
      "postCount": 123,
      "url": "https://your-company.canny.io/admin/board/feature-requests"
    },
    "by": {
      "id": "6a2889c586d7b8843bf4cf19",
      "created": "2026-11-01T04:00:00.000Z",
      "email": "test@john.test",
      "isAdmin": true,
      "name": "John Doe",
      "url": "https://your-company.canny.io/admin/users/john-doe",
      "userID": "5678"
    },
    "category": {
      "id": "6a2889c586d7b8843bf4cf0d",
      "name": "Dashboard",
      "parentID": null,
      "postCount": 42,
      "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
    },
    "clickup": {
      "linkedTasks": [{
        "id": "123",
        "linkID": "2334jdsai23234io22",
        "name": "Clickup issue",
        "postID": "6a2889c586d7b8843bf4cf05",
        "status": "to do",
        "url": "https://app.clickup.com/t/123456"
      }]
    },
    "commentCount": 10,
    "created": "2026-11-01T04:00:00.000Z",
    "customFields": [{
      "id": "6a2889c586d7b8843bf4cf0e",
      "name": "priority",
      "value": "high",
    }],
    "details": "Test post details",
    "eta": "February 2020",
    "imageURLs": [
      "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
      "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
    ],
    "jira": {
      "linkedIssues": [{
        "id": "123",
        "key": "ID-123",
        "url": "https://your-company.atlassian.net/browse/ID-123"
      }]
    },
    "linear": {
      "linkedIssueIDs": ["ID-123", "ID-345"],
    },
    "mergeHistory":[{
      "created":"2026-11-01T04:00:00.000Z",
      "post": {
        "created":"2026-11-01T04:00:00.000Z",
        "details":"Awesome feature post details",
        "id": "6a2889c586d7b8843bf4cf14",
        "imageURLs":[],
        "title":"Another awesome feature request"
      }
    }],
    "owner": {
      "id": "6a2889c586d7b8843bf4cf07",
      "created": "2026-11-01T04:00:00.000Z",
      "email": "test@test.test",
      "isAdmin": true,
      "name": "Sally Doe",
      "url": "https://your-company.canny.io/admin/users/sally-doe",
      "userID": "1234"
    },
    "score": 72,
    "status": "in progress",
    "statusChangedAt": "2026-11-01T04:00:00.000Z",
    "tags": [{
      "id": "6a2889c586d7b8843bf4cf10",
      "name": "iOS",
      "postCount": 15,
      "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
    }],
    "title": "An awesome feature request",
    "url": "https://your-company.canny.io/admin/board/feature-requests/p/post-title"
  }]
}

Create post

Creates a new post.

You may also want to use the Create or update user endpoint to make sure the post author's account exists (and fetch their id).

Arguments
apiKeystringrequired
Your secret API key.
authorIDstringrequired
The unique identifier of the post's author.
boardIDstringrequired
The unique identifier of the post's board.
byIDstringoptional
The identifier of the admin who creates the post on behalf of the author. This will be visible in the post.
categoryIDstringoptional
The unique identifier of the post's category or subcategory.
customFieldsobjectoptional
Values for the post's custom fields, keyed by field name. Each name must match one of your custom fields, and each value must fit that field's type. For dropdown and multi-select fields, use one of the field's existing options. If you include custom fields, include every field that's required on the board.
detailsstringrequired
The post details, up to 10,000 characters.
etastringoptional
The estimated date of the post's completion. In the format of MM/YYYY, eg, 06/2022.
etaPublicbooleanoptional
If the ETA should be made visible to all users.
titlestringrequired
The post title, up to 400 characters. Can't be blank.
ownerIDstringoptional
The ID of the user responsible for the completion of the work described in the post.
imageURLsarrayoptional
An array of the URLs of post's images.
createdAtstringoptional
If the post is being moved from another source, the date this post was originally created in ISO 8601 format.

Returns

Upon success, returns an object with two keys: id and url.

Endpoint
POSThttps://canny.io/api/v1/posts/create
Example Request
$ curl https://canny.io/api/v1/posts/create -X POST -d '{
    "apiKey": "YOUR_API_KEY",
    "authorID": "6a2889c586d7b8843bf4cf05",
    "boardID": "6a2889c586d7b8843bf4cf01",
    "customFields": {
      "number": 12,
      "string": "value"
    },
    "details": "This is the post's details",
    "title": "Post Title"}' \
    -H "Content-Type: application/json"
Example Response
{
  "id": "6a2889c586d7b8843bf4cf09",
  "url": "https://your-company.canny.io/admin/board/feature-requests/p/post-title"
}

Change board

Moves a post to another board.

Arguments
apiKeystringrequired
Your secret API key.
boardIDstringrequired
The destination board's unique identifier.
postIDstringrequired
The post's unique identifier.

Returns

Returns success if the post was moved.

Endpoint
POSThttps://canny.io/api/v1/posts/change_board
Example Request
$ curl https://canny.io/api/v1/posts/change_board -X POST \
    -d apiKey=YOUR_API_KEY \
    -d boardID=6a2889c586d7b8843bf4cf01 \
    -d postID=6a2889c586d7b8843bf4cf05
Example Responses
success
{"error":"invalid postID"}

Change post category

Changes a post's category specified by its id.

Arguments
apiKeystringrequired
Your secret API key.
categoryIDstringoptional
The category's unique identifier. Setting it to "null" removes the category of the post.
postIDstringrequired
The post's unique identifier.

Returns

Returns the updated post if the post's category was successfully changed.

Endpoint
POSThttps://canny.io/api/v1/posts/change_category
Example Request
$ curl https://canny.io/api/v1/posts/change_category -X POST \
    -d apiKey=YOUR_API_KEY \
    -d categoryID=6a2889c586d7b8843bf4cf1a \
    -d postID=6a2889c586d7b8843bf4cf05
Example Responses
{
  "id": "6a2889c586d7b8843bf4cf05",
  "author": {
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2017-07-15T22:11:00.000Z",
    "email": "test@test.test",
    "isAdmin": false,
    "name": "Sally Doe",
    "url": "https://your-company.canny.io/admin/users/sally-doe",
    "userID": "1234"
  },
  "board": {
    "created": "2017-07-10T11:22:00.000Z",
    "id": "6a2889c586d7b8843bf4cf01",
    "name": "Feature Requests",
    "postCount": 123,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "by": {
    "id": "6a2889c586d7b8843bf4cf19",
    "created": "2017-07-15T22:11:00.000Z",
    "email": "test@john.test",
    "isAdmin": true,
    "name": "John Doe",
    "url": "https://your-company.canny.io/admin/users/john-doe",
    "userID": "5678"
  },
  "category": {
    "id": "6a2889c586d7b8843bf4cf0d",
    "name": "Dashboard",
    "postCount": 42,
    "parentID": null,
    "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
  },
  "changeComment": {
    "value": "The status has changed!",
    "imageURLs": ["https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b2.png"]
  },
  "clickup": {
    "linkedTasks": [{
      "id": "123",
      "linkID": "2334jdsai23234io22",
      "name": "Clickup issue",
      "postID": "6a2889c586d7b8843bf4cf05",
      "status": "to do",
      "url": "https://app.clickup.com/t/123456"
    }]
  },
  "commentCount": 10,
  "created": "2017-08-22T13:32:00.000Z",
  "details": "Test post details",
  "eta": "February 2020",
  "imageURLs": [
    "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
    "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
  ],
  "jira": {
    "linkedIssues": [{
      "id": "123",
      "key": "ID-123",
      "url": "https://your-company.atlassian.net/browse/ID-123"
    }]
  },
  "linear": {
    "linkedIssueIDs": ["ID-123", "ID-345"],
  },
  "mergeHistory":[{
    "created":"2018-06-17T22:42:37.167Z",
    "post": {
      "created":"2018-06-17T22:42:00.797Z",
      "details":"Awesome feature post details",
      "id": "6a2889c586d7b8843bf4cf14",
      "imageURLs":[],
      "title":"Another awesome feature request"
    }
  }],
  "score": 72,
  "status": "in progress",
  "statusChangedAt": "2017-08-24T23:22:00.000Z",
  "tags": [{
    "id": "6a2889c586d7b8843bf4cf10",
    "name": "iOS",
    "postCount": 15,
    "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
  }],
  "title": "An awesome feature request",
  "url": "https://your-company.canny.io/admin/board/feature-requests/p/an-awesome-feature-request"
}
{"error":"invalid post id"}

Change post status

Changes a post's status (eg. to 'planned'), specified by its id.

Arguments
apiKeystringrequired
Your secret API key.
changerIDstringrequired
The identifier of the admin to record as having changed the post's status. This will be visible in the post's activity section.
postIDstringrequired
The post's unique identifier.
shouldNotifyVotersbooleanrequired
Whether or not to notify non-admin voters of the status change.
statusstringrequired
The status to change the post to. Options include: "open", "under review", "planned", "in progress", "complete", "closed", or any other status your team has set on the settings page.
commentValuestringoptional
The comment attached to this status change. If the comment includes line breaks, use " " instead of directly inputting the line breaks.
commentImageURLsarrayoptional
An array of the URLs of the images associated with this status change.

Returns

Returns the updated post if the post's status was successfully changed.

Note: If you try to change a post's status to its current status (eg. "planned" to "planned"), the endpoint will still respond with the updated post but none of the side-effects will trigger (post activity unit, slack notification, user notification, etc).
Endpoint
POSThttps://canny.io/api/v1/posts/change_status
Example Request
$ curl https://canny.io/api/v1/posts/change_status -X POST \
    -d apiKey=YOUR_API_KEY \
    -d changerID=6a2889c586d7b8843bf4cf07 \
    -d postID=6a2889c586d7b8843bf4cf05 \
    -d shouldNotifyVoters=true \
    -d status="in progress"
Example Responses
{
  "id": "6a2889c586d7b8843bf4cf05",
  "author": {
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2017-07-15T22:11:00.000Z",
    "email": "test@test.test",
    "isAdmin": false,
    "name": "Sally Doe",
    "url": "https://your-company.canny.io/admin/users/sally-doe",
    "userID": "1234"
  },
  "board": {
    "created": "2017-07-10T11:22:00.000Z",
    "id": "6a2889c586d7b8843bf4cf01",
    "name": "Feature Requests",
    "postCount": 123,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "by": {
    "id": "6a2889c586d7b8843bf4cf19",
    "created": "2017-07-15T22:11:00.000Z",
    "email": "test@john.test",
    "isAdmin": true,
    "name": "John Doe",
    "url": "https://your-company.canny.io/admin/users/john-doe",
    "userID": "5678"
  },
  "category": {
    "id": "6a2889c586d7b8843bf4cf0d",
    "name": "Dashboard",
    "postCount": 42,
    "parentID": null,
    "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
  },
  "changeComment": {
    "value": "The status has changed!",
    "imageURLs": ["https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b2.png"]
  },
  "commentCount": 10,
  "created": "2017-08-22T13:32:00.000Z",
  "details": "Test post details",
  "eta": "February 2020",
  "imageURLs": [
    "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
    "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
  ],
  "jira": {
    "linkedIssues": [{
      "id": "123",
      "key": "ID-123",
      "url": "https://your-company.atlassian.net/browse/ID-123"
    }]
  },
  "linear": {
    "linkedIssueIDs": ["ID-123", "ID-345"],
  },
  "mergeHistory":[{
    "created":"2018-06-17T22:42:37.167Z",
    "post": {
      "created":"2018-06-17T22:42:00.797Z",
      "details":"Awesome feature post details",
      "id": "6a2889c586d7b8843bf4cf14",
      "imageURLs":[],
      "title":"Another awesome feature request"
    }
  }],
  "score": 72,
  "status": "in progress",
  "statusChangedAt": "2017-08-24T23:22:00.000Z",
  "tags": [{
    "id": "6a2889c586d7b8843bf4cf10",
    "name": "iOS",
    "postCount": 15,
    "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
  }],
  "title": "An awesome feature request",
  "url": "https://your-company.canny.io/admin/board/feature-requests/p/an-awesome-feature-request"
}
{"error":"invalid post id"}

Merge post

Merges one post into another post.

Arguments
apiKeystringrequired
Your secret API key.
mergePostIDstringrequired
The post's unique identifier that will be merged.
intoPostIDstringrequired
The post's unique identifier that the mergePostID will be merged into.
mergerIDstringrequired
The unique identifier of the user that will be the merger.

Returns

Returns success if the post was merged.

Endpoint
POSThttps://canny.io/api/v1/posts/merge
Example Request
$ curl https://canny.io/api/v1/posts/merge -X POST \
    -d apiKey=YOUR_API_KEY \
    -d mergePostID=6a2889c586d7b8843bf4cf05 \
    -d intoPostID=6a2889c586d7b8843bf4cf06 \
    -d mergerID=6a2889c586d7b8843bf4cf07
Example Responses
success
{"error":"invalid mergePostID"}

Add post tag

Adds a tag to a post. Both post and tag are specified by their unique id.

Arguments
apiKeystringrequired
Your secret API key.
postIDstringrequired
The post's unique identifier.
tagIDstringrequired
The tag's unique identifier.

Returns

Returns the updated post if the tag was successfully added.

Note: If you try to add a tag that has already been added, the endpoint will still respond with the updated post, even though no tag has been added.
Endpoint
POSThttps://canny.io/api/v1/posts/add_tag
Example Request
$ curl https://canny.io/api/v1/posts/add_tag -X POST \
    -d apiKey=YOUR_API_KEY \
    -d postID=6a2889c586d7b8843bf4cf05
    -d tagID=6a2889c586d7b8843bf4cf0b
Example Responses
{
  "id": "6a2889c586d7b8843bf4cf05",
  "author": {
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2017-07-15T22:11:00.000Z",
    "email": "test@test.test",
    "isAdmin": false,
    "name": "Sally Doe",
    "url": "https://your-company.canny.io/admin/users/sally-doe",
    "userID": "1234"
  },
  "board": {
    "created": "2017-07-10T11:22:00.000Z",
    "id": "6a2889c586d7b8843bf4cf01",
    "name": "Feature Requests",
    "postCount": 123,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "by": {
    "id": "6a2889c586d7b8843bf4cf19",
    "created": "2017-07-15T22:11:00.000Z",
    "email": "test@john.test",
    "isAdmin": true,
    "name": "John Doe",
    "url": "https://your-company.canny.io/admin/users/john-doe",
    "userID": "5678"
  },
  "category": {
    "id": "6a2889c586d7b8843bf4cf0d",
    "name": "Dashboard",
    "parentID": null,
    "postCount": 42,
    "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
  },
  "changeComment": {
    "value": "The status has changed!",
    "imageURLs": ["https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b2.png"]
  },
  "clickup": {
    "linkedTasks": [{
      "id": "123",
      "linkID": "2334jdsai23234io22",
      "name": "Clickup issue",
      "postID": "6a2889c586d7b8843bf4cf05",
      "status": "to do",
      "url": "https://app.clickup.com/t/123456"
    }]
  },
  "commentCount": 10,
  "created": "2017-08-22T13:32:00.000Z",
  "details": "Test post details",
  "eta": "February 2020",
  "imageURLs": [
    "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
    "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
  ],
  "jira": {
    "linkedIssues": [{
      "id": "123",
      "key": "ID-123",
      "url": "https://your-company.atlassian.net/browse/ID-123"
    }]
  },
  "linear": {
    "linkedIssueIDs": ["ID-123", "ID-345"],
  },
  "mergeHistory":[{
    "created":"2018-06-17T22:42:37.167Z",
    "post": {
      "created":"2018-06-17T22:42:00.797Z",
      "details":"Awesome feature post details",
      "id": "6a2889c586d7b8843bf4cf14",
      "imageURLs":[],
      "title":"Another awesome feature request"
    }
  }],
  "score": 72,
  "status": "in progress",
  "statusChangedAt": "2017-08-24T23:22:00.000Z",
  "tags": [{
    "board": {
      "created": "2017-07-10T11:22:00.000Z",
      "id": "6a2889c586d7b8843bf4cf01",
      "name": "Feature Requests",
      "postCount": 123,
      "url": "https://your-company.canny.io/admin/board/feature-requests"
    },
    "created": "2017-08-30T13:32:01.000Z",
    "id": "6a2889c586d7b8843bf4cf10",
    "name": "iOS",
    "postCount": 15,
    "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
  }, {
    "board": {
      "created": "2017-07-10T11:22:00.000Z",
      "id": "6a2889c586d7b8843bf4cf01",
      "name": "Feature Requests",
      "postCount": 123,
      "url": "https://your-company.canny.io/admin/board/feature-requests"
    },
    "created": "2018-04-20T13:32:01.000Z",
    "id": "6a2889c586d7b8843bf4cf0b",
    "name": "Example Tag",
    "postCount": 12,
    "url": "https://your-company.canny.io/admin/board/feature-requests?tags=example-tag"
  }],
  "title": "An awesome feature request",
  "url": "https://your-company.canny.io/admin/board/feature-requests/p/an-awesome-feature-request"
}
{"error":"invalid post id"}

Remove post tag

Removes a tag from a post. Both post and tag are specified by their unique id.

Arguments
apiKeystringrequired
Your secret API key.
postIDstringrequired
The post's unique identifier.
tagIDstringrequired
The tag's unique identifier.

Returns

Returns the updated post if the tag was successfully remove.

Note: If you try to remove a tag that isn't assigned to the post, the endpoint will still respond with the updated post, even though no tag has been removed.
Endpoint
POSThttps://canny.io/api/v1/posts/remove_tag
Example Request
$ curl https://canny.io/api/v1/posts/remove_tag -X POST \
    -d apiKey=YOUR_API_KEY \
    -d postID=6a2889c586d7b8843bf4cf05
    -d tagID=6a2889c586d7b8843bf4cf0b
Example Responses
{
  "id": "6a2889c586d7b8843bf4cf05",
  "author": {
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2017-07-15T22:11:00.000Z",
    "email": "test@test.test",
    "isAdmin": false,
    "name": "Sally Doe",
    "url": "https://your-company.canny.io/admin/users/sally-doe",
    "userID": "1234"
  },
  "board": {
    "created": "2017-07-10T11:22:00.000Z",
    "id": "6a2889c586d7b8843bf4cf01",
    "name": "Feature Requests",
    "postCount": 123,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "by": {
    "id": "6a2889c586d7b8843bf4cf19",
    "created": "2017-07-15T22:11:00.000Z",
    "email": "test@john.test",
    "isAdmin": true,
    "name": "John Doe",
    "url": "https://your-company.canny.io/admin/users/john-doe",
    "userID": "5678"
  },
  "category": {
    "id": "6a2889c586d7b8843bf4cf0d",
    "name": "Dashboard",
    "parentID": null,
    "postCount": 42,
    "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
  },
  "changeComment": {
    "value": "The status has changed!",
    "imageURLs": ["https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b2.png"]
  },
  "clickup": {
    "linkedTasks": [{
      "id": "123",
      "linkID": "2334jdsai23234io22",
      "name": "Clickup issue",
      "postID": "6a2889c586d7b8843bf4cf05",
      "status": "to do",
      "url": "https://app.clickup.com/t/123456"
    }]
  },
  "commentCount": 10,
  "created": "2017-08-22T13:32:00.000Z",
  "details": "Test post details",
  "eta": "February 2020",
  "imageURLs": [
    "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
    "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
  ],
  "jira": {
    "linkedIssues": [{
      "id": "123",
      "key": "ID-123",
      "url": "https://your-company.atlassian.net/browse/ID-123"
    }]
  },
  "linear": {
    "linkedIssueIDs": ["ID-123", "ID-345"],
  },
  "mergeHistory":[{
    "created":"2018-06-17T22:42:37.167Z",
    "post": {
      "created":"2018-06-17T22:42:00.797Z",
      "details":"Awesome feature post details",
      "id": "6a2889c586d7b8843bf4cf14",
      "imageURLs":[],
      "title":"Another awesome feature request"
    }
  }],
  "score": 72,
  "status": "in progress",
  "statusChangedAt": "2017-08-24T23:22:00.000Z",
  "tags": [{
    "board": {
      "created": "2017-07-10T11:22:00.000Z",
      "id": "6a2889c586d7b8843bf4cf01",
      "name": "Feature Requests",
      "postCount": 123,
      "url": "https://your-company.canny.io/admin/board/feature-requests"
    },
    "created": "2017-08-30T13:32:01.000Z",
    "id": "6a2889c586d7b8843bf4cf10",
    "name": "iOS",
    "postCount": 15,
    "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
  }],
  "title": "An awesome feature request",
  "url": "https://your-company.canny.io/admin/board/feature-requests/p/an-awesome-feature-request"
}
{"error":"invalid post id"}

Update post

Updates an existing post.

Arguments
apiKeystringrequired
Your secret API key.
postIDstringrequired
The unique identifier of the post.
changerIDstringoptional
The unique identifier of the admin making the change. Required when ownerID is provided. They must have permission to change post owners.
customFieldsobjectoptional
Values for the post's custom fields, keyed by field name. Each name must match one of your custom fields, and each value must fit that field's type. For dropdown and multi-select fields, use one of the field's existing options. Fields you leave out keep their current values.
detailsstringoptional
The post details, up to 10,000 characters.
etastring | nulloptional
The estimated date of the post's completion. In the format of MM/YYYY, eg, 06/2022. Set to null to clear the ETA.
etaPublicbooleanoptional
If the ETA should be made visible to all users.
imageURLsarrayoptional
An array of the URLs of the post's images. Replaces the post's existing images. Pass an empty array to remove all images; omit it to leave them untouched.
ownerIDstring | nulloptional
The unique identifier of the admin to make the owner of the post. Set to null to remove the owner. Requires changerID.
titlestringoptional
The post title, up to 400 characters. Can't be blank.

Returns

Returns "success" if the post was updated successfully.

Endpoint
POSThttps://canny.io/api/v1/posts/update
Example Request
$ curl https://canny.io/api/v1/posts/update -X POST -d '{
    "apiKey": "YOUR_API_KEY",
    "postID": "6a2889c586d7b8843bf4cf05",
    "customFields": {
      "number": 12,
      "string": "value"
    },
    "details": "Updated post details",
    "title": "Updated post title",
    "eta": "01/2025",
    "imageURLs": ["https://picsum.photos/200"],
    "etaPublic": true}' \
    -H "Content-Type: application/json"
Example Response
success

Delete post

Deletes an existing post.

Arguments
apiKeystringrequired
Your secret API key.
postIDstringrequired
The unique identifier of the post.

Returns

Returns "success" if the post was deleted successfully.

Endpoint
POSThttps://canny.io/api/v1/posts/delete
Example Request
$ curl https://canny.io/api/v1/posts/delete -X POST -d '{
    "apiKey": "YOUR_API_KEY",
    "postID": "6a2889c586d7b8843bf4cf05"' \
    -H "Content-Type: application/json"
Example Response
success

Roadmaps

A roadmap is a collection of posts. Posts can be associated with multiple roadmaps.

Roadmap data is exposed via API endpoints that fetch post data.

The roadmap object

Attributes
idstring
A unique identifier for the roadmap.
archivedboolean
Whether the roadmap has been archived.
createdstring
Time at which the roadmap was created, in ISO 8601 format.
namestring
The name of the roadmap.
postCountnumber
The number of posts associated with the roadmap.
urlstring
The URL to the roadmap in the admin view.
Example roadmap object:
{
  "id": "6a2889c586d7b8843bf4cf0b",
  "archived": false,
  "created": "2024-09-30T13:32:01.000Z",
  "name": "Example Roadmap",
  "postCount": 12,
  "url": "https://your-company.canny.io/admin/roadmap/example-roadmap"
}

Status Changes

Posts can be assigned a status: open, under review, planned, in progress, complete, or closed.

Our API allows you to list status changes, sorted by recency.

The status change object

Attributes
idstring
A unique identifier for the status change.
changeCommentComment object | null
The comment attached to this status change. Only imageURLs and value fields are included.
changerUser object | null
The user who changed the status.
createdstring
Time at which the status was changed, in ISO 8601 format.
postPost object
The post that had its status changed.
statusstring
The status the post was changed to.
Example status change object:
{
  "changeComment": {
    "imageURLs": [
      "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
      "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
    ],
    "value": "This is the status change comment value"
  },
  "changer": {
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2017-07-15T22:11:00.000Z",
    "email": "test@test.test",
    "isAdmin": false,
    "name": "Sally Doe",
    "url": "https://your-company.canny.io/admin/users/sally-doe",
    "userID": "1234"
  },
  "created": "2017-08-30T13:32:01.000Z",
  "id": "6a2889c586d7b8843bf4cf0b",
  "post": {
    "id": "6a2889c586d7b8843bf4cf05",
    "author": {
      "id": "6a2889c586d7b8843bf4cf07",
      "created": "2017-07-15T22:11:00.000Z",
      "email": "test@test.test",
      "isAdmin": false,
      "name": "Sally Doe",
      "url": "https://your-company.canny.io/admin/users/sally-doe",
      "userID": "1234"
    },
    "board": {
      "created": "2017-07-10T11:22:00.000Z",
      "id": "6a2889c586d7b8843bf4cf01",
      "name": "Feature Requests",
      "postCount": 123,
      "url": "https://your-company.canny.io/admin/board/feature-requests"
    },
    "category": {
      "id": "6a2889c586d7b8843bf4cf0d",
      "name": "Dashboard",
      "postCount": 42,
      "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
    },
    "changeComment": {
      "value": "The status has changed!",
      "imageURLs": ["https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b2.png"]
    },
    "clickup": {
      "linkedTasks": [{
        "id": "123",
        "linkID": "2334jdsai23234io22",
        "name": "Clickup issue",
        "postID": "6a2889c586d7b8843bf4cf05",
        "status": "to do",
        "url": "https://app.clickup.com/t/123456"
      }]
    },
    "commentCount": 10,
    "created": "2017-08-22T13:32:00.000Z",
    "details": "Test post details",
    "eta": "February 2020",
    "imageURLs": [
      "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
      "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
    ],
    "jira": {
      "linkedIssues": [{
        "id": "123",
        "key": "ID-123",
        "url": "https://your-company.atlassian.net/browse/ID-123"
      }]
    },
    "linear": {
      "linkedIssueIDs": ["ID-123", "ID-345"],
    },
    "mergeHistory":[{
      "created":"2018-06-17T22:42:37.167Z",
      "post": {
        "created":"2018-06-17T22:42:00.797Z",
        "details":"Awesome feature post details",
        "id": "6a2889c586d7b8843bf4cf14",
        "imageURLs":[],
        "title":"Another awesome feature request"
      }
    }],
    "score": 72,
    "status": "in progress",
    "statusChangedAt": "2017-08-24T23:22:00.000Z",
    "tags": [{
      "id": "6a2889c586d7b8843bf4cf10",
      "name": "iOS",
      "postCount": 15,
      "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
    }],
    "title": "An awesome feature request",
    "url": "https://your-company.canny.io/admin/board/feature-requests/p/an-awesome-feature-request"
  },
  "status": "in progress"
}

List status changes

Returns a list of status changes. Include parameters to specify board and pagination. Sorted by newest.

Arguments
apiKeystringrequired
Your secret API key.
boardIDstringoptional
The id of the board you'd like to fetch status changes for.
cursorstringoptional
A pagination cursor returned from a previous request. Used to fetch the next page of results.
limitnumberoptional
The number of status changes you'd like to fetch. Must be between 1 and 100. Defaults to 10 if not specified.

Returns

A dictionary with an "items" property that contains an array of status change objects. There's also a "hasNextPage" property that specifies whether there are more status changes available, and a "cursor" property that can be used to fetch the next page of results.

Endpoint
POSThttps://canny.io/api/v2/status_changes/list
Example Request
$ curl https://canny.io/api/v2/status_changes/list -X POST \
    -d apiKey=YOUR_API_KEY \
    -d boardID=6a2889c586d7b8843bf4cf01
Example Response
{
  "cursor": "eyJhZnRlciI6eyJfaWQiOiI1NTNjM2VmOGI4Y2RjZD...",
  "hasNextPage": false,
  "items": [{
    "changeComment": {
      "imageURLs": [
        "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
        "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
      ],
      "value": "The status has changed!"
    },
    "changer": {
      "id": "6a2889c586d7b8843bf4cf07",
      "created": "2026-11-01T04:00:00.000Z",
      "email": "test@test.test",
      "isAdmin": false,
      "name": "Sally Doe",
      "url": "https://your-company.canny.io/admin/users/sally-doe",
      "userID": "1234"
    },
    "created": "2026-11-01T04:00:00.000Z",
    "id": "6a2889c586d7b8843bf4cf0b",
    "post": {
      "category": {
        "created": "2026-11-01T04:00:00.000Z",
        "id": "6a2889c586d7b8843bf4cf0d",
        "name": "Dashboard",
        "parentID": null,
        "postCount": 42,
        "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
      },
      "commentCount": 10,
      "details": "Test post details",
      "id": "6a2889c586d7b8843bf4cf05",
      "imageURLs": [
        "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
        "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
      ],
      "jira": {
        "linkedIssues": [{
          "id": "123",
          "key": "ID-123",
          "url": "https://your-company.atlassian.net/browse/ID-123"
        }],
        "linkedIssueIDs": ["123"]
      },
      "linear": {
        "linkedIssueIDs": ["ID-123", "ID-345"]
      },
      "score": 72,
      "status": "in progress",
      "tags": [{
        "id": "6a2889c586d7b8843bf4cf10",
        "name": "iOS",
        "postCount": 15,
        "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
      }],
      "title": "An awesome feature request",
      "url": "https://your-company.canny.io/admin/board/feature-requests/p/an-awesome-feature-request"
    },
    "status": "in progress"
  }]
}

Tags

Posts can be assigned multiple tags. Each tag is for a specific board, not company-wide.

Our API allows you to create, list, and retrieve tags. You can also tag and un-tag posts.

The tag object

Attributes
idstring
A unique identifier for the tag.
boardBoard object
The board this tag is associated with.
createdstring
Time at which the tag was created, in ISO 8601 format.
namestring
The name of the tag.
postCountnumber
The number of posts that have been assigned this tag.
urlstring
The URL to the board, filtered to just posts that have been assigned this tag.
Example tag object:
{
  "id": "6a2889c586d7b8843bf4cf0b",
  "board": {
    "created": "2017-08-30T13:32:01.000Z",
    "id": "6a2889c586d7b8843bf4cf12",
    "name": "Feature Requests",
    "postCount": 99,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "created": "2017-08-30T13:32:01.000Z",
  "name": "Example Tag Name",
  "postCount": 12,
  "url": "https://your-company.canny.io/admin/board/feature-requests?tag=example-tag-name"
}

Retrieve tag

Retrieves the details of an existing tag, specified by its id.

Arguments
apiKeystringrequired
Your secret API key.
idstringrequired
The tag's unique identifier.

Returns

Returns a tag object, if a valid id was supplied.

Endpoint
POSThttps://canny.io/api/v1/tags/retrieve
Example Request
$ curl https://canny.io/api/v1/tags/retrieve -X POST \
    -d apiKey=YOUR_API_KEY \
    -d id=6a2889c586d7b8843bf4cf0b
Example Response
{
  "id": "6a2889c586d7b8843bf4cf0b",
  "board": {
    "created": "2017-08-30T13:32:01.000Z",
    "id": "6a2889c586d7b8843bf4cf12",
    "name": "Feature Requests",
    "postCount": 99,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "created": "2017-08-30T13:32:01.000Z",
  "name": "Example Tag Name",
  "postCount": 12,
  "url": "https://your-company.canny.io/admin/board/feature-requests?tag=example-tag-name"
}

List tags

Returns a list of tags. Include parameters to specify board and pagination. Sorted by newest.

Arguments
apiKeystringrequired
Your secret API key.
boardIDstringoptional
The id of the board you'd like to fetch tags for.
limitnumberoptional
The number of tags you'd like to fetch. Defaults to 10 if not specified.
skipnumberoptional
The number of tags you'd like to skip before starting to fetch. Defaults to 0 if not specified.

Returns

A dictionary with a "tags" property that contains an array of tag objects. There's also a "hasMore" property that specifies whether this query returns more tags than the limit.

Endpoint
POSThttps://canny.io/api/v1/tags/list
Example Request
$ curl https://canny.io/api/v1/tags/list -X POST \
    -d apiKey=YOUR_API_KEY \
    -d boardID=6a2889c586d7b8843bf4cf01
Example Response
{
  "hasMore": false,
  "tags": [{
    "id": "6a2889c586d7b8843bf4cf0b",
    "board": {
      "created": "2017-08-30T13:32:01.000Z",
      "id": "6a2889c586d7b8843bf4cf12",
      "name": "Feature Requests",
      "postCount": 99,
      "url": "https://your-company.canny.io/admin/board/feature-requests"
    },
    "created": "2017-08-30T13:32:01.000Z",
    "name": "Example Tag Name",
    "postCount": 12,
    "url": "https://your-company.canny.io/admin/board/feature-requests?tag=example-tag-name"
  }]
}

Create tag

Creates a new tag.

Arguments
apiKeystringrequired
Your secret API key.
boardIDstringrequired
The unique identifier of the board the tag should be created for.
namestringrequired
The name of the tag. Must be between 1 and 30 characters long.

Returns

Returns the tag object, if it was successfully created or already exists.

Endpoint
POSThttps://canny.io/api/v1/tags/create
Example Request
$ curl https://canny.io/api/v1/tags/create -X POST \
    -d apiKey=YOUR_API_KEY \
    -d boardID=6a2889c586d7b8843bf4cf12 \
    -d name="New Tag"
Example Response
{
  "id": "6a2889c586d7b8843bf4cf0b",
  "board": {
    "created": "2017-08-30T13:32:01.000Z",
    "id": "6a2889c586d7b8843bf4cf12",
    "name": "Feature Requests",
    "postCount": 99,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "created": "2017-08-30T13:32:01.000Z",
  "name": "Example Tag Name",
  "postCount": 12,
  "url": "https://your-company.canny.io/admin/board/feature-requests?tag=example-tag-name"
}

Users

Users can create posts, votes, and comments. Admins also have user accounts. The API allows you to fetch a specific user.

The user object

Attributes
idstring
A unique identifier for the user.
aliasstring | null
Name used in place of the user's actual name in anonymized boards. This field can be null if user has not previously engaged with the company.
avatarURLstring | null
Link to the user's avatar image.
companiesarray
A list of companies the user is associated with.
createdstring
Time at which the user was created, in ISO 8601 format.
customFieldsobject | null
Any custom fields associated with the user.
emailstring | null
The user's email. This field can be null, for example when you create a new user by voting on behalf of them.
isAdminboolean
Whether or not the user is a Canny admin.
lastActivitystring | null
Time at which the user interacted with your company for the last time, in ISO 8601 format.
namestring
The user's name.
urlstring
The URL of the user's profile.
userIDstring | null
The user's unique identifier in your application. This field can be null. We only have this data if the user was authenticated via single sign-on, or if it was added via API.
Example user object:
{
  "id": "6a2889c586d7b8843bf4cf07",
  "alias": "Green Fish",
  "avatarURL": "https://canny.io/images/a3db0133d1e7d9122832b67b2c4caaaa.jpg",
  "companies": [{
    "created": "2020-01-23T04:56:07.890Z",
    "customFields": {
      "field1": "value1",
      "field2": "value2"
    },
    "id": "company123",
    "monthlySpend": 500.00,
    "name": "company name"
  }],
  "created": "2026-11-01T04:00:00.000Z",
  "customFields": {
    "field1": "value1",
    "field2": "value2"
  },
  "email": "test@test.test",
  "isAdmin": false,
  "lastActivity": "2026-11-01T04:00:00.000Z",
  "name": "Sally Doe",
  "url": "https://your-company.canny.io/admin/users/sally-doe",
  "userID": "1234"
}

List users

List the end-users within your workspace.

Arguments
apiKeystringrequired
Your secret API key.
limitnumberoptional
The number of entries you'd like to fetch, between 1-100. Defaults to 10.
cursorstringoptional
The cursor from a previous request

Returns

Returns an array of users.

Endpoint
POSThttps://canny.io/api/v2/users/list
Example Request
$ curl https://canny.io/api/v2/users/list -X POST \
    -d apiKey=YOUR_API_KEY \
    -d limit=100 \
    -d cursor=NTUzYzNlZjhiOGNkY2QxNTAxYmExMjNhXzIwMjUtMDItMDRUMTY6NDA6MDcuNTQxWl8x
Example Response

{
  "hasNextPage": true,
  "cursor": "NTUzYzNlZjhiOGNkY2QxNTAxYmExMjNhXzIwMjUtMDItMDRUMTY6NDA6MDcuNTQxWl8x",
  "users": [{
    "id": "6a2889c586d7b8843bf4cf07",
    "alias": "Green Fish",
    "avatarURL": "https://canny.io/images/a3db0133d1e7d9122832b67b2c4caaaa.jpg",
    "created": "2026-11-01T04:00:00.000Z",
    "customFields": {
      "field1": "value1",
      "field2": "value2"
    },
    "email": "test@test.test",
    "isAdmin": false,
    "lastActivity": "2026-11-01T04:00:00.000Z",
    "name": "Sally Doe",
    "url": "https://your-company.canny.io/admin/users/sally-doe",
    "userID": "1234"
  }]
}

Retrieve user

Retrieves the details of an existing user. You must specify exactly one of: their Canny id, their id in your application, or their email. These fields are all unique per user.

You can only retrieve them based on their id in your application if the user was authenticated via single sign-on or created via API.

Arguments
apiKeystringrequired
Your secret API key.
emailstringoptional
The user's email.
idstringoptional
The user's unique identifier from Canny.
userIDstringoptional
The user's unique identifier in your application.

Returns

Returns a user object, if a valid identifier was supplied.

Endpoint
POSThttps://canny.io/api/v1/users/retrieve
Example Request
$ curl https://canny.io/api/v1/users/retrieve -X POST \
    -d apiKey=YOUR_API_KEY \
    -d id=6a2889c586d7b8843bf4cf07
Example Request
$ curl https://canny.io/api/v1/users/retrieve -X POST \
    -d apiKey=YOUR_API_KEY \
    -d userID=1234
Example Request
$ curl https://canny.io/api/v1/users/retrieve -X POST \
    -d apiKey=YOUR_API_KEY \
    -d email="test@test.test"
Example Response
{
  "id": "6a2889c586d7b8843bf4cf07",
  "alias": "Green Fish",
  "avatarURL": "https://canny.io/images/a3db0133d1e7d9122832b67b2c4caaaa.jpg",
  "created": "2026-11-01T04:00:00.000Z",
  "customFields": {
    "field1": "value1",
    "field2": "value2"
  },
  "email": "test@test.test",
  "isAdmin": false,
  "lastActivity": "2026-11-01T04:00:00.000Z",
  "name": "Sally Doe",
  "url": "https://your-company.canny.io/admin/users/sally-doe",
  "userID": "1234"
}

Create or update user

If the user does not exist, one is created, and its id is returned. If the user does exist, they will be updated with the data supplied.

Why is this endpoint useful? If you're going to use an object creation API endpoint (such as create post), you may have to create the author's account before creating the post.

Note: the email, name and avatarURL properties of admin users cannot be modified through the Canny API. A Canny admin will need to login to Canny to update this information manually.
Note: one of email, userID or id must be provided to update a user.
Arguments
apiKeystringrequired
Your secret API key.
aliasstringoptional
The user's alias
avatarURLstringoptional
The URL pointing to the user's avatar image.
companiesarrayoptional

Defines the list of companies the user is associated with. To associate a user with a company, the user must have a UserID. Verify the UserID using the retrieve user endpoint.

Omitting a company that the user is currently associated with will not disassociate the user from that company. To disassociate a user from a company, use the remove user from a company endpoint.

Each company needs a name, up to 100 characters. It can't contain a URL or slashes, or be a reserved word like "Feedback" or "Roadmap". If you include a domain, use a bare domain like "acme.com".

createddateoptional
The date the user was created in your system.
customFieldsobjectoptional
Any custom fields associated with the user. Field names must be under 30 characters, and text values must be under 200 characters.
emailstringoptional
The user's email. Must be a valid email address, up to 200 characters.
idstringoptional

The user's unique identifier in Canny. This can be found through the list users endpoint.

namestringrequired
The user's name, up to 50 characters. Can't be blank or contain ":" or "/".
userIDstringoptional
The user's unique identifier in your application.

Returns

Upon success, returns an object with a single key: id.

You can then use this id in object creation API endpoints such as for creating posts.

Endpoint
POSThttps://canny.io/api/v1/users/create_or_update
Also available at https://canny.io/api/v1/users/find_or_create (deprecated)
Example Request
$ curl https://canny.io/api/v1/users/create_or_update -X POST -d '{
    "apiKey": "YOUR_API_KEY",
    "alias": "Green Fish",
    "companies": [{
      "created": "2026-11-01T04:00:00.000Z",
      "customFields": {
        "field1": "value1",
        "field2": "value2"
      },
      "domain": "acme.com",
      "id": "company123",
      "monthlySpend": 500.00,
      "name": "company name"
    }],
    "created": "2026-11-01T04:00:00.000Z",
    "customFields": {
      "field1": "value1",
      "field2": "value2"
    },
    "email": "sally@netflix.com",
    "name": "Sally Doe",
    "userID": "1234-5678"}' \
    -H "Content-Type: application/json"
Example Response
{
  "id": "6a2889c586d7b8843bf4cf07",
}

Delete user

Deletes a user. Also deletes all of their comments and votes.

If their posts have other comments/votes, then they'll be removed as an author. If there are no other comments/votes, the post will be removed.

Arguments
apiKeystringrequired
Your secret API key.
idstringrequired
The unique identifier of the user.

Returns

Returns "success" if the user was successfully deleted.

Endpoint
POSThttps://canny.io/api/v1/users/delete
Example Request
$ curl https://canny.io/api/v1/users/delete -X POST \
    -d apiKey=YOUR_API_KEY \
    -d userID=6a2889c586d7b8843bf4cf0c"
Example Response
success

Remove user from a company

Removes the user from a company that they were previously associated with.

Why is this endpoint useful? If a user of your application has moved from one organization to another or if a user is no longer a part of an organization.

Please note that removing a user from a company will result in their votes no longer being associated with the company's spend.

Arguments
apiKeystringrequired
Your secret API key.
companyIDstringrequired
The identifier of your company.
idstringrequired

The unique identifier of the user. This can be found through the Retrieve User endpoint.

Returns

Returns "success" if the user was removed. Otherwise an error describing the issue with your request.

Endpoint
POSThttps://canny.io/api/v1/users/remove_user_from_company
Example Request
$ curl https://canny.io/api/v1/users/remove_user_from_company -X POST -d '{
    "apiKey": "YOUR_API_KEY",
    "companyID": "company1",
    "id": "6a2889c586d7b8843bf4cf07"}' \
    -H "Content-Type: application/json"
Example Response
"success"

Votes

Users can vote on posts. Admins can also vote on behalf of users. Vote objects are always associated with a post and a voter. The API allows you to fetch a specific vote, or a list of votes associated with a post.

The vote object

Attributes
idstring
A unique identifier for the vote.
boardBoard object
The board this vote is associated with.
byUser object | null
The admin who cast this vote on behalf of a user. If the user voted themselves, this field will be null.
createdstring
Time at which the vote was first cast, in ISO 8601 format.
postPost object
The post this vote is associated with.
voterUser object
The user this post is associated with.
votePrioritystring
The priority of the vote. Can be one of "Nice to have", "Important", "Must have", or "No priority".
zendeskTicketobject | null
The Zendesk ticket associated with this vote, if the vote originated from Zendesk. Otherwise null.
Example vote object:
{
  "id": "6a2889c586d7b8843bf4cf08",
  "board": {
    "created": "2017-08-30T13:32:01.000Z",
    "id": "6a2889c586d7b8843bf4cf12",
    "name": "Feature Requests",
    "postCount": 99,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "by": null,
  "created": "2017-08-30T13:32:01.000Z",
  "post": {
    "category": {
      "id": "6a2889c586d7b8843bf4cf0d",
      "name": "Dashboard",
      "postCount": 42,
      "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
    },
    "commentCount": 2,
    "id": "6a2889c586d7b8843bf4cf13",
    "imageURLs": [],
    "jira": {
      "linkedIssues": [{
        "id": "123",
        "key": "ID-123",
        "url": "https://your-company.atlassian.net/browse/ID-123"
      }]
    },
    "linear": {
      "linkedIssueIDs": ["ID-123", "ID-345"],
    },
    "score": 13,
    "status": "planned",
    "tags": [{
      "id": "6a2889c586d7b8843bf4cf0d",
      "name": "iOS",
      "postCount": 15,
      "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
    }],
    "title": "post-title",
    "url": "https://your-company.canny.io/admin/board/feature-requests/p/post-title"
  },
  "voter": {
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2017-08-30T13:32:00.000Z",
    "email": "test@test.test",
    "isAdmin": false,
    "name": "Sally Doe",
    "url": "https://your-company.canny.io/admin/users/sally-doe",
    "userID": "1234"
  },
  "zendeskTicket": {
      "url": "https://your-company.zendesk.com/api/v2/tickets/2.json",
      "id": 2,
      "created": "2018-12-14T19:20:25Z",
      "subject": "Ticket subject",
      "description": "Ticket description",
  },
  "votePriority": "No priority",
}

Retrieve vote

Retrieves the details of an existing vote, specified by its id.

Arguments
apiKeystringrequired
Your secret API key.
idstringrequired
The vote's unique identifier.

Returns

Returns a vote object, if a valid id was supplied.

Endpoint
POSThttps://canny.io/api/v1/votes/retrieve
Example Request
$ curl https://canny.io/api/v1/votes/retrieve -X POST \
    -d apiKey=YOUR_API_KEY \
    -d id=6a2889c586d7b8843bf4cf08
Example Response
{
  "id": "6a2889c586d7b8843bf4cf08",
  "board": {
    "created": "2017-08-30T13:32:01.000Z",
    "id": "6a2889c586d7b8843bf4cf12",
    "name": "Feature Requests",
    "postCount": 99,
    "url": "https://your-company.canny.io/admin/board/feature-requests"
  },
  "by": null,
  "created": "2017-08-30T13:32:01.000Z",
  "post": {
    "category": {
      "id": "6a2889c586d7b8843bf4cf0d",
      "name": "Dashboard",
      "postCount": 42,
      "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
    },
    "commentCount": 2,
    "eta": "February 2020",
    "id": "6a2889c586d7b8843bf4cf13",
    "imageURLs": [],
    "jira": {
      "linkedIssues": [{
        "id": "123",
        "key": "ID-123",
        "url": "https://your-company.atlassian.net/browse/ID-123"
      }]
    },
    "linear": {
      "linkedIssueIDs": ["ID-123", "ID-345"],
    },
    "score": 13,
    "status": "planned",
    "tags": [{
      "id": "6a2889c586d7b8843bf4cf10",
      "name": "iOS",
      "postCount": 15,
      "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
    }],
    "title": "post-title",
    "url": "https://your-company.canny.io/admin/board/feature-requests/p/post-title"
  },
  "voter": {
    "id": "6a2889c586d7b8843bf4cf07",
    "created": "2017-08-30T13:32:00.000Z",
    "email": "test@test.test",
    "isAdmin": false,
    "name": "Sally Doe",
    "url": "https://your-company.canny.io/admin/users/sally-doe",
    "userID": "1234"
  },
  "votePriority": "Important",
}

List votes

Returns a list of votes. Include parameters to specify post, board, and pagination. Sorted by newest.

Arguments
apiKeystringrequired
Your secret API key.
boardIDstringoptional
The id of the board you'd like to fetch votes for.
companyIDstringoptional
If specified, will only fetch votes created by users linked to the company with this custom identifier.
cursorstringoptional
A pagination cursor returned from a previous request. Used to fetch the next page of results.
limitnumberoptional
The number of votes you'd like to fetch. Must be between 1 and 100. Defaults to 10 if not specified.
postIDstringoptional
Specify a postID to only fetch votes for a specific post.
userIDstringoptional
Specify a userID to only fetch votes for a specific user.

Returns

A dictionary with an "items" property that contains an array of vote objects. There's also a "hasNextPage" property that specifies whether there are more votes available, and a "cursor" property that can be used to fetch the next page of results.

Endpoint
POSThttps://canny.io/api/v2/votes/list
Example Request
$ curl https://canny.io/api/v2/votes/list -X POST \
    -d apiKey=YOUR_API_KEY \
    -d postID=6a2889c586d7b8843bf4cf0f
Example Response
{
  "cursor": "eyJhZnRlciI6eyJfaWQiOiI1NTNjM2VmOGI4Y2RjZD...",
  "hasNextPage": true,
  "items": [{
    "id": "6a2889c586d7b8843bf4cf08",
    "board": {
      "created": "2026-11-01T04:00:00.000Z",
      "id": "6a2889c586d7b8843bf4cf01",
      "name": "Feature Requests",
      "postCount": 123,
      "url": "https://your-company.canny.io/admin/board/feature-requests"
    },
    "by": null,
    "created": "2026-11-01T04:00:00.000Z",
    "post": {
      "category": {
        "id": "6a2889c586d7b8843bf4cf0d",
        "name": "Dashboard",
        "postCount": 42,
        "url": "https://your-company.canny.io/admin/board/feature-requests?category=dashboard"
      },
      "commentCount": 2,
      "id": "6a2889c586d7b8843bf4cf13",
      "imageURLs": [],
      "jira": {
        "linkedIssues": [{
          "id": "123",
          "key": "ID-123",
          "url": "https://your-company.atlassian.net/browse/ID-123"
        }],
      },
      "linear": {
        "linkedIssueIDs": ["ID-123", "ID-345"]
      },
      "score": 13,
      "status": "planned",
      "tags": [{
        "id": "6a2889c586d7b8843bf4cf10",
        "name": "iOS",
        "postCount": 15,
        "url": "https://your-company.canny.io/admin/board/feature-requests?tags=ios"
      }],
      "title": "post-title",
      "url": "https://your-company.canny.io/admin/board/feature-requests/p/post-title"
    },
    "voter": {
      "id": "6a2889c586d7b8843bf4cf07",
      "created": "2026-11-01T04:00:00.000Z",
      "email": "test@test.test",
      "isAdmin": false,
      "name": "Sally Doe",
      "url": "https://your-company.canny.io/admin/users/sally-doe",
      "userID": "1234"
    },
    "votePriority": "No priority",
    "zendeskTicket": {
      "id": "2",
      "created": "2018-12-14T19:20:25Z",
      "subject": "Ticket subject",
      "description": "Ticket description",
      "url": "https://your-company.zendesk.com/api/v2/tickets/2.json"
    }
  }]
}

Create vote

Creates a new vote.

You may also want to use the Create or update user endpoint to make sure the voter's account exists (and fetch their id).

Arguments
apiKeystringrequired
Your secret API key.
byIDstringoptional
The unique identifier of the user who cast the vote on behalf of the voter. Must be a Canny administrator.
createdAtstringoptional
If the vote is being moved from another source, the date this vote was originally created in ISO 8601 format.
postIDstringrequired
The unique identifier of the post to vote on.
voterIDstringrequired
The unique identifier of the voter.
votePrioritystringoptional
The priority of the vote. Can be one of "Nice to have", "Important", "Must have", or "No priority" to clear it. If omitted, an existing vote keeps its priority.

Returns

Returns "success" if the vote was successfully created or already exists.

Endpoint
POSThttps://canny.io/api/v1/votes/create
Example Request
$ curl https://canny.io/api/v1/votes/create -X POST \
    -d apiKey=YOUR_API_KEY \
    -d postID=6a2889c586d7b8843bf4cf09 \
    -d voterID=6a2889c586d7b8843bf4cf05
Example Response
success

Delete vote

Deletes a vote.

Arguments
apiKeystringrequired
Your secret API key.
postIDstringrequired
The unique identifier of the post to vote on.
voterIDstringrequired
The unique identifier of the voter.

Returns

Returns "success" if the vote was successfully deleted, or already doesn't exist.

Endpoint
POSThttps://canny.io/api/v1/votes/delete
Example Request
$ curl https://canny.io/api/v1/votes/delete -X POST \
    -d apiKey=YOUR_API_KEY \
    -d postID=6a2889c586d7b8843bf4cf09 \
    -d voterID=6a2889c586d7b8843bf4cf05
Example Response
success

Webhooks

By setting up webhooks, your server will be notified of Canny events as they happen. For example, if a user creates a new post, we would send a request to your server to let you know.

You can set up webhooks for your account in Your Subdomain > Settings > API.

Every request is signed, so you can confirm it was sent by Canny. Once you add a webhook, your signing secret appears on the same settings page, and Webhook signatures explains how to verify a request.

The event object

Attributes
createdstring
Time at which the event was created, in ISO 8601 format.
objectobject
The object the event is about.
objectTypestring
The type of object included in the event.
typestring
The type of event.
Example event object:
{
  "created": "2017-07-15T22:11:00.000Z",
  "object": {
    "id": "6a2889c586d7b8843bf4cf05",
    "author": {
      "id": "6a2889c586d7b8843bf4cf07",
      "created": "2017-07-15T22:11:00.000Z",
      "email": "test@test.test",
      "isAdmin": false,
      "name": "Sally Doe",
      "url": "https://your-company.canny.io/admin/users/sally-doe",
      "userID": "1234"
    },
    "board": {
      "id": "6a2889c586d7b8843bf4cf01",
      "created": "2017-07-15T22:11:00.000Z",
      "name": "Feature Requests",
      "postCount": 123,
      "url": "https://your-company.canny.io/admin/board/feature-requests"
    },
    "commentCount": 10,
    "created": "2017-07-15T22:11:00.000Z",
    "details": "Test post details",
    "eta": "February 2020",
    "imageURLs": [
      "https://canny.io/images/93fc5808937760b82c3dc00aa5cd86b8.png",
      "https://canny.io/images/316e5600645b81e4be287a52d506dbfd.jpg"
    ],
    "score": 72,
    "status": "in progress",
    "title": "An awesome feature request",
    "url": "https://your-company.canny.io/admin/board/feature-requests/p/post-title"
  },
  "objectType": "post",
  "type": "post.created"
}

Event types

This is a list of all types of events we send.

Event
post.created
Occurs when a new post is created.
post.edited
Occurs when a post is edited.
post.deleted
Occurs when a post is deleted.
post.jira_issue_linked
Occurs when a Jira issue is linked to a post.
post.jira_issue_unlinked
Occurs when a Jira issue is unlinked from a post.
post.tag_added
Occurs when a post is tagged
post.tag_removed
Occurs when a post's tag is removed.
post.status_changed
Occurs when a post's status is changed.
comment.created
Occurs when a new portal comment is created.
comment.edited
Occurs when a portal comment is edited.
comment.deleted
Occurs when a portal comment is deleted.
vote.created
Occurs when a user votes on a post.
vote.deleted
Occurs when a user unvotes on a post.
changelog_entry.created
Occurs when a changelog entry is created as a draft.
changelog_entry.published
Occurs when a changelog entry is published, including when a scheduled entry publishes.
changelog_entry.unpublished
Occurs when a published changelog entry is moved back to draft.
changelog_entry.deleted
Occurs when a changelog entry is deleted.

Webhook signatures

Canny signs every webhook following the Standard Webhooks specification, so you can confirm a request was sent by Canny and was not modified. Each request includes the following headers.

Headers
webhook-id
A unique identifier for the webhook message.
webhook-timestamp
The number of seconds since the UNIX epoch. It is covered by the signature, so you can safely reject requests older than a few minutes to prevent replays.
webhook-signature
One or more space-separated signatures, each "v1," followed by a Base64 HMAC (SHA-256) of "<webhook-id>.<webhook-timestamp>.<raw request body>". The HMAC key is your signing secret without its "whsec_" prefix, Base64 decoded.

Your signing secret is in your API settings once you have added a webhook. The easiest way to verify a request is with an official Standard Webhooks library, available for JavaScript, Python, Go, Java, C#, Ruby, PHP, Rust and Elixir. Pass it the raw request body exactly as received, before parsing it as JSON.

When you regenerate your signing secret, Canny signs each request with both the old and new secret for 24 hours, so update your server within that window and verification keeps working. Accept a request if any signature in webhook-signature matches; the official libraries already do. If your secret leaked, you can choose to revoke the old one immediately instead.
Teams that used webhooks before signing secrets were introduced also receive the legacy canny-timestamp, canny-nonce and canny-signature headers. That signature only covers the nonce, signed with your API key, so it does not protect the body or timestamp. Switch to webhook-signature; the legacy headers will be removed.
Example verification
Node.js
Python
Without a library
import { Webhook } from 'standardwebhooks';

const webhook = new Webhook('YOUR_WEBHOOK_SECRET');

// rawBody must be the exact request body, before any JSON parsing.
// Throws if the signature is invalid or the timestamp is too old.
function verify(rawBody, headers) {
  return webhook.verify(rawBody, headers);
}

Autopilot Feedback Processing

Autopilot is Canny's AI-powered set of tools that can detect, extract and deduplicate customer feedback. For Canny accounts with Autopilot, we offer direct access to Autopilot with an API that queues text input for processing.

Each API request uses one Autopilot credit. Click here to learn more about Autopilot and credits.

Enqueue feedback

Adds an item of feedback to the queue. During the next sync, Autopilot will extract any feature requests and identify any duplicates. You can see the results when they are done processing.

When in manual mode, the Autopilot inbox has a limit of 100 items. If you exceed this limit, you won't be able to enqueue any more items until you process them. Enabling automated mode is recommended to skip this limit, and view an audit log of the actions taken by Autopilot.

Arguments
apiKeystringrequired
Your secret API key.
payloadobjectrequired
The payload of the request detailing the author, sourceURL and source text. See below for more details.
payload.idstringrequired
The unique identifier of the conversation or chat you are enqueuing.
payload.authorIDstringrequired
The author's unique identifier in your application. If the user does not exist, you can create them with the "Create or Update User" endpoint first.
payload.textstringrequired
The text you want to extract features from e.g. a call transcript.
payload.sourceURLstringoptional
An optional URL linking to the conversation or chat you are enqueuing.
payload.sourceTypestringoptional
An optional indicator of the source e.g. X (Twitter), Zoom, Instagram, ...
typestringrequired
The type of content being enqueued. Currently only supports "call" and "conversation". Choose "call" if the transcript is from a call and "conversation" if it is from a chat conversation.

Returns

Returns "success" if the payload was enqueued. Otherwise an error describing the issue with your request.

Endpoint
POSThttps://canny.io/api/v1/ai/enqueue
Example Request
$ curl https://canny.io/api/v1/ai/enqueue -X POST -d '{
    "apiKey": "YOUR_API_KEY",
    "payload": {
      "id": "conversationID",
      "authorID": "6a2889c586d7b8843bf4cf07",
      "sourceURL": "https://twitter.com/cannyHQ/status/1728150249263218855",
      "sourceType": "twitter",
      "text": "I love this product! No feature requests, just wanted to say thanks!"
    },
    "type": "conversation"
    }' \
    -H "Content-Type: application/json"
Example Response
"success"